Hôm nay mình tiếp tục bài viết về cách sử dụng một feature cũng khá hay của Unity : ScriptableObject.

Câu chuyện của Designer và Developer

Mình sẽ bắt đầu bằng một câu chuyện nhỏ trong một team, về chuyện hợp tác giữa developers và designer trong quá trình developement.

Những team nhỏ hoặc những nhóm đơn lẻ làm việc với nhau thì thường không phân tách ra Designer là ai, thường chỉ là vài ông dev, một ông art cùng nhau design, coding … Còn với cấp Studio trở lên, vai trò Designer được tách ra rõ ràng. Designer không cần biết về cấu trúc code, thiết kế codebase thế nào, nhiệm vụ của họ là design gameplay, viết các document mô tả chi tiết, design level, maps, cân bằng các thông số trong game …

Trong quá trình chỉnh sửa các thông số game (tốc độ di chuyển player, máu, money …), nếu như các bạn đặt rải rác các con số “public” ở khắp các prefab, thì việc tùy chỉnh rất khó khăn. Ex : sửa tốc độ của nhân vật, phải vào Prefab/Character/MC.prefab , rồi chỉnh máu cho con monster lại vào Prefab/Monster/ … Tệ hơn có một số Junior còn chuyên hardcode trong script, khiến mỗi lần chỉnh cái gì lại phải mở code lên.

dizzy-fred-flintstones-coloring-page

Vì vậy, người ta luôn tìm cách đặt tất cả những config vào một vị trí chung (nếu ai thường vọc các game, config thường được lưu thành các file .ini, .cfg ..v…v…), để dễ chỉnh sửa, thay đổi, độc lập với codebase và data.


ScriptableObject là cái gì ? Trong trường hợp này nó đóng vai trò thế nào ?

Đọc ngay Manual của Unity cũng đã  khá đầy đủ thông tin rồi. Nôm na nó thế này : Bạn có một MovementComponent, trong đó có 10 float filed để config. Bạn attach nó vào 5 object trên scene, như vậy game sẽ cần bộ nhớ để lưu trữ 5 x 10 = 50 float field. Thay vào đó, bằng cách nào đó bạn lưu 10 float field này vào 1 file config, và 5 object kia đơn giản là “reference” tới file config này, như thế game chỉ cần bộ nhớ để lưu 1 x 10 float filed.

ScriptableObject chính là cái “file config” mà mình đề cập ở trên, nó đơn giản là một “cục” asset, trong đó đơn thuần là một script do bạn định nghĩa, với các trường (field) dùng để config. Các Object chỉ cần “reference” tới file asset này là truy xuất được các field trên.

capture_05012016_195855

Một ví dụ điển hình trong project mình từng làm qua : con MC (Main Character) có khá nhiều property về tốc độ di chuyển, tốc độ nhảy, lượng damage … Những property này bị thay đổi liên tục trong game khi nhân vật nhặt được các item khác nhau, bị sát thương …. Mình tạo ra khá nhiều các file ScriptableObject để lưu các config cho tất cả các state kể trên, mỗi lần chuyển state mình sẽ “swap” qua lại giữa các file config này, rất tiện trong quá trình config, balance game.


Cách sử dụng ScriptableObject

Tạo một ScriptableObject

Đây là demo cho một ScriptableObject :

[CreateAssetMenu(fileName = "MovementConfig", menuName = "GameConfiguration/PlayerMovement", order = 1)]
public class MovementConfig : ScriptableObject
{
public float movingAcceleration;
public float limitMovingSpeed;
public float jumpSpeed;
}

Chú ý field “menuName” chính là đường dẫn để tạo một ScriptableObject. Sau khi đã có script trên trong project, rightClick vào một chỗ bất kì trong ProjectBrowser window, bạn sẽ có thêm tùy chọn tạo file ScriptableObject PlayerMovement

ScriptableDemo_EditorCreate

Mặc định các file này sẽ có đuôi là .asset. Trong InspectorWindow sẽ hiện rõ các public field của PlayerMovement (cái này thì tương tự như các script MonoBehaviour)

ScriptableDemo_EditorInspector

Sử dụng ScriptableObject

Đơn giản, các bạn viết một MonoBehaviour script như bình thường, trong đó có một public filed MovementConfig, sau đó drag nó vào trong Inspector Window

public class MovementComponent : MonoBehaviour
{
// Drag your movementConfig to here
public MovementConfig movementConfig;
void Update()
{
var velo = _myRigidbody2D.velocity;
velo.x = movementConfig.limitVelocity;
_myRigidbody2D.velocity = velo;
}
}

Từ đó có thể truy xuất tới các thành phần trong file ScriptableObject trên. Như trong hình, mình config cho cái MovementComponent sử dụng cái NormalMovent config

ScriptableDemo_EditorInspector02


Demo Project

Như mọi khi, mình có một demo nhỏ trên GitHub, các bạn có thể checkout về để theo dõi. Đây là một game thể loại Platformer2D đơn giản. Thằng MC di chuyển bình thường, có 2 “vùng” để swap movmentConfig, khi đi vào 2 vùng này, MC sẽ được thay đổi vận tốc di chuyển, độ cao nhảy. Đơn giản là khi collision với 2 vùng đó, nó sẽ swap reference tới 2 file ScriptableObject.


Một số chú ý

ScriptableObject có thể được sử dụng theo nhiều cách khác nhau, demo và phạm vi bài viết của mình chỉ thể hiện việc “swap” các config game. Có một vài chú ý sau :

  • ScriptableObject có thể update trong lúc runtime, và nó sẽ apply cho tất cả object cùng sử dụng nó. Vì thế với các object cần các thông số cấu hình riêng thì không nên cho vào ScriptableOBject nhé
  • Hoặc lợi dụng tính năng trên, bạn cũng có thể thực hiện 1 thay đổi đồng loạt lên tất cả các object dùng chung config. ex : giảm speed của MonsterMovementConfig sẽ làm chậm tất cả monster sử dụng config này.

P/S :

Sau khi đăng bài, có một số bạn thắc mắc mình vài chỗ:

  • Cách xử lý va chạm trong Demo có phần hơi ngộ ngộ, các bạn có thể xem thêm ở bài viết về Xử lý va chạm của mình, để hiểu Attacker-Victim style hé
  • Cách phân tách componets cho các objects trong game. Món này để vài bữa có thời gian, mình sẽ viết một bài về các mindsets, patterns và tips cho việc design components.

 

Một số link tham khảo:

https://unity3d.com/learn/tutorials/modules/beginner/live-training-archive/scriptable-objects

5 thoughts on “[Unity3D] Sử dụng ScriptableObject để lưu các thông số cấu hình trong game

  1. anh ơi cho em hỏi có cách nào lưu lại thay đổi của dữ liệu khi kết thúc runtime ko ạ?

    Like

  2. Bác có thể làm một bài viết về Scriptable Object để save/load game có sài cả Json được không. Ví dụ một game nhập vai có nhiều acc, mỗi acc có các thông số về nhân vật, item, state. Mình xem cái Unite Europe 2016 trên youtube thì thấy họ hình như chỉ dùng scriptable asset để lưu giá trị mặc định trên ổ cứng, còn sau đó các scriptable này được lưu trong Json, và khi load lại người ta tạo ra 1 scriptable dưới dạng “HideFlags.HideAndDontSave” để sài. Câu hỏi đặt ra là có nên lưu game trực tiếp vào các Scriptable asset không, hay sciptable chỉ dùng để lưu các setting mặc định thôi. Rất mong bác chỉ dạy kinh nghệm

    Like

  3. À, thì bạn cứ hiểu ScriptableObject nó là một cái object, mà sinh ra từ đầu game, và … sống tới cuối game, thằng nào muốn lấy cái gì từ nó từ reference tới nó thôi. Cứ hiểu nó như một chỗ để chưa dữ liệu chung đi.
    Rồi, nó tiện, nhưng mỗi cái data của nó bị config từ khi build, và chỉ thay đổi khi runtime, không có lưu xuống disk. Cho nên nếu muốn nó linh hoạt được, thì mình phải manual làm cái việc ghi xuống đọc lên đó. Vd: đầu game đọc disk lên, save vào scriptableObject, cuối game, lấy scriptableObejct ra ghi lại xuống disk.
    Còn như chỗ của bạn, mình cũng gặp qua rồi. Mình có cái ScriptableObject là UserSessionData, lưu hầm bà lằn các thứ như name, coin … Còn UserProfile thì ghi xuống disk, đầu game, thằng user chơi profile nào thì lấy cái profile đó ghi qua cho cái UserSessionData.

    Like

Leave a comment