Hero image

Sử dụng ScriptableObjects làm đối tượng ủy quyền

Trang web này đã được dịch bằng máy để thuận tiện cho bạn. Chúng tôi không thể đảm bảo tính chính xác hoặc độ tin cậy của nội dung được dịch. Nếu bạn có thắc mắc về tính chính xác của nội dung được dịch, vui lòng tham khảo phiên bản tiếng Anh chính thức của trang web.

Trang này giải thích cách sử dụng ScriptableObjects như các vùng chứa logic. Bằng cách này, bạn có thể coi chúng như các đối tượng ủy quyền, hoặc các gói hành động nhỏ mà bạn có thể gọi khi cần.

Đây là bài thứ tư trong chuỗi sáu bài hướng dẫn ngắn được tạo ra để hỗ trợ các nhà phát triển Unity với bản demo đi kèm sách điện tử " Tạo kiến ​​trúc trò chơi mô-đun trong Unity với ScriptableObjects" .

Bản demo này lấy cảm hứng từ cơ chế trò chơi điện tử arcade kinh điển với bóng và vợt, và cho thấy cách ScriptableObjects có thể giúp bạn tạo các thành phần có thể kiểm thử, có khả năng mở rộng và thân thiện với nhà thiết kế.

Cả sách điện tử, dự án minh họa và các hướng dẫn ngắn này cùng nhau cung cấp những phương pháp tốt nhất để sử dụng các mẫu thiết kế lập trình với lớp ScriptableObject trong dự án Unity của bạn. Những mẹo này có thể giúp bạn đơn giản hóa mã, giảm mức sử dụng bộ nhớ và thúc đẩy khả năng tái sử dụng mã.

Loạt bài này bao gồm các bài viết sau:

Lưu ý quan trọng trước khi bắt đầu

Trước khi bạn bắt đầu tìm hiểu dự án demo ScriptableObject và loạt bài hướng dẫn ngắn này, hãy nhớ rằng, về bản chất, các mẫu thiết kế chỉ là những ý tưởng. Những điều này không áp dụng cho mọi tình huống. Những kỹ thuật này có thể giúp bạn học được những cách làm việc mới với Unity và ScriptableObjects.

Mỗi kiểu mẫu đều có ưu điểm và nhược điểm riêng. Chỉ chọn những giải pháp mang lại lợi ích thiết thực cho dự án cụ thể của bạn. Các nhà thiết kế của bạn có phụ thuộc nhiều vào Unity Editor không? Một mô hình dựa trên ScriptableObject có thể là lựa chọn tốt để giúp họ cộng tác với các nhà phát triển của bạn.

Tóm lại, kiến ​​trúc mã nguồn tốt nhất là kiến ​​trúc phù hợp với dự án và nhóm của bạn.

ScriptableObjects

MỘT SCRIPTABLEOBJECT CHỨA “STRATEGY” BÊN TRONG MONOBEHAVIOUR.

Mô hình chiến lược

Sử dụng mẫu thiết kế Strategy , bạn có thể định nghĩa một giao diện hoặc lớp ScriptableObject cơ bản, sau đó làm cho các đối tượng ủy quyền đó có thể hoán đổi cho nhau trong thời gian chạy.

Một ứng dụng là đóng gói các thuật toán thực hiện các tác vụ cụ thể vào một ScriptableObject và sau đó sử dụng ScriptableObject đó trong ngữ cảnh của một việc khác.

Ví dụ, nếu bạn đang viết một hệ thống trí tuệ nhân tạo hoặc tìm đường đi cho lớp EnemyUnit, bạn có thể tạo một ScriptableObject với kỹ thuật tìm kiếm đường đi (như A*, Dijkstra, v.v.).

Bản thân EnemyUnit sẽ không chứa bất kỳ thuật toán tìm đường nào. Thay vào đó, nó sẽ giữ một tham chiếu đến một ScriptableObject "chiến lược" riêng biệt. Ưu điểm của thiết kế này là bạn có thể chuyển sang thuật toán khác chỉ bằng cách hoán đổi các đối tượng. Đây là một cách để lựa chọn các hành vi khác nhau trong quá trình thực thi.

Khi MonoBehaviour cần thực hiện một tác vụ, nó sẽ gọi các phương thức bên ngoài trên ScriptableObject thay vì phương thức của chính nó. Ví dụ, ScriptableObject có thể chứa các phương thức công khai như MoveUnit hoặc SetTarget để điều khiển đơn vị địch và chỉ định điểm đến.

Hành vi có thể cắm thêm

Bạn có thể cải tiến mẫu này bằng một lớp cơ sở trừu tượng hoặc một giao diện. Việc đó có nghĩa là bất kỳ ScriptableObject nào triển khai chiến lược đó đều có thể được thay thế bằng một ScriptableObject khác. ScriptableObject có thể thay thế nóng này "kết nối" vào MonoBehaviour tham chiếu đến nó - thậm chí ngay lập tức trong quá trình chạy.

Nếu bạn cần EnemyUnit thay đổi hành vi do các điều kiện trong trò chơi, ngữ cảnh bên ngoài ( MonoBehaviour) có thể kiểm tra các điều kiện đó. Sau đó, nó có thể cắm vào một ScriptableObject khác làm phản hồi.

Bằng cách tách các chi tiết triển khai thành một ScriptableObject, bạn cũng có thể tạo điều kiện thuận lợi hơn cho việc phân chia trách nhiệm giữa các thành viên trong nhóm. Một lập trình viên có thể tập trung vào thuật toán bên trong ScriptableObject, trong khi người khác làm việc trên ngữ cảnh MonoBehaviour .

Để tạo hành vi có thể cắm thêm này, hãy đảm bảo rằng:

  • Xác định một lớp cơ sở hoặc giao diện cho chiến lược: Lớp hoặc giao diện này cần bao gồm các phương thức và thuộc tính cần thiết để thực thi chiến lược.
  • Tạo các lớp ScriptableObject: Mỗi bên có thể đưa ra những cách triển khai chiến lược khác nhau. Ví dụ, bạn có thể tạo một lớp để triển khai thuật toán AI đơn giản và một lớp khác để triển khai thuật toán phức tạp hơn.
  • Tạo một ScriptableObject triển khai chiến lược: Hãy bổ sung các đoạn mã logic còn thiếu và điền đầy đủ các giá trị cần thiết vào Inspector.
  • Hãy áp dụng chiến lược này trong bối cảnh cụ thể: Trong MonoBehaviour, hãy gọi các phương thức và thuộc tính được triển khai trong ScriptableObject. Để việc gọi các phương thức đó dễ dàng hơn, hãy truyền các phần phụ thuộc dưới dạng tham số.

Việc sắp xếp mã theo cách này có thể giúp bạn dễ dàng chuyển đổi giữa các cách triển khai khác nhau của cùng một chiến lược. Nhờ đó, việc gỡ lỗi và bảo trì trở nên dễ dàng hơn với các mô-đun có thể cắm thêm.

Ví dụ: AudioDelegate

Thuật toán hay chiến lược không nhất thiết phải phức tạp. Ví dụ, dự án PaddleBallSO thể hiện một hệ thống phát lại âm thanh khá cơ bản trong SimpleAudioDelegate.

Lớp trừu tượng AudioDelegateSO định nghĩa một phương thức Play duy nhất chấp nhận tham số AudioSource. triển khai cụ thể sau đó sẽ ghi đè lên điều này.

Lớp con SimpleAudioDelegateSO định nghĩa một mảng các AudioClip. Nó chọn ngẫu nhiên một đoạn video và phát lại bằng cách sử triển khai phương thức Play được ghi đè. Điều này tạo ra sự biến đổi về cao độ và âm lượng trong một phạm vi tùy chỉnh.

Mặc dù chỉ có vài dòng mã, bạn có thể tạo ra nhiều hiệu ứng âm thanh khác nhau với đoạn trích mã bên dưới.

Mặc dù ví dụ cụ thể này không thực sự phù hợp cho việc sử dụng âm thanh chuyên sâu, nhưng nó được trình bày ở đây như một bản demo cơ bản về cách sử dụng ScriptableObjects trong mẫu thiết kế Strategy.

Nhà thiết kế có thể tạo nhiều ScriptableObject khác nhau để thể hiện hiệu ứng âm thanh mà không cần chỉnh sửa mã nguồn. Một lần nữa, điều này chỉ yêu cầu sự hỗ trợ tối thiểu từ nhà phát triển sau khi ScriptableObject cơ bản được hoàn thành.

Trong PaddleBallSO , giờ đây bất kỳ ai cũng có thể thiết lập một mảng âm thanh mới để phát lại khi bóng chạm vào một trong các bức tường của màn chơi. Các nhà thiết kế có được sự độc lập và linh hoạt sáng tạo vì họ làm việc hoàn toàn trong Trình chỉnh sửa. Cách tiếp cận này giúp giải phóng nguồn lực lập trình, vì các nhà phát triển không còn cần phải tham gia vào mọi quyết định thiết kế nữa.

[Có thể tuần tự hóa]
cấu trúc công khai RangedFloat
{
	Giá trị tối thiểu của float công khai;
	public float maxValue;
}

public abstract class AudioDelegateSO: ScriptableObject
{
	public abstract void Play(AudioSource source);
}

public class SimpleAudioDelegateSO: AudioDelegateSO
{
    [SerializeField] private AudioClip[] clips;
    [SerializeField] private RangedFloat volume;
    [SerializeField] private RangedFloat pitch;

    public override void Play(AudioSource source)
    {
        if (clips.Length == 0 || source == null)
            trở lại;

        source.clip = clips[Random.Range(0, clips.Length)];
        source.volume = Random.Range(volume.minValue, volume.maxValue);
        source.pitch = Random.Range(pitch.minValue, pitch.maxValue);

        source.Play();
    }
}
Đối tượng ủy quyền

CẢNH DEMO CỦA AUDIODELEGATES CHO THẤY CÁCH CÁC ĐỐI TƯỢNG CÓ THỂ LƯU TRỮ LOGIC.

Bản demo các mẫu

Bạn cũng có thể xem ví dụ âm thanh trong bản demo Patterns. Mỗi âm thanh đều được tạo ra từ một tài nguyên SimpleAudioDelegateSO hơi khác nhau, với những biến thể nhỏ giữa các trường hợp.

Trong ví dụ này, mỗi góc đều bao gồm một AudioSource. Một MonoBehaviour AudioModifier tùy chỉnh sử dụng một delegate dựa trên ScriptableObject để phát lại âm thanh.

Sự khác biệt về cao độ chỉ xuất phát từ các thiết lập trên từng tài nguyên ScriptableObject (BeepHighPitched_SO, BeepLowPitched_SO, v.v.).

Việc sử dụng ScriptableObject để kiểm soát logic hành động có thể giúp nhóm thiết kế của bạn dễ dàng thử nghiệm các ý tưởng hơn. Điều này cho phép nhà thiết kế làm việc độc lập hơn so với nhà phát triển.

Quản lý mục tiêu

TRÌNH QUẢN LÝ MỤC TIÊU (OBJECTIVEMANAGER) KIỂM TRA ĐIỀU KIỆN CHIẾN THẮNG BẰNG CÁCH SỬ DỤNG CÁC ĐỐI TƯỢNG CÓ THỂ LẬP TRÌNH (SCRIPTABLEOBJECTS).

Ví dụ: Mô hình chiến lược trong hệ thống mục tiêu

Dự án PaddleBallSO cũng sử dụng mô hình chiến lược trong hệ thống mục tiêu của mình. Mặc dù đây không phải là điều cần thay đổi trong quá trình thực thi, việc đóng gói mỗi đối tượng trong một ScriptableObject cung cấp một cách linh hoạt để kiểm tra các điều kiện thắng thua.

Lớp cơ sở trừu tượng, ObjectiveSO, lưu giữ các giá trị như tên mục tiêu và trạng thái hoàn thành của mục tiêu đó.

Các lớp con cụ thể, như ScoreObjectiveSO, sau đó sẽ triển khai logic thực tế về cách hoàn thành từng mục tiêu. Họ thực hiện điều đó bằng cách ghi đè phương thức CompleteObjective của ObjectiveSO và thêm logic điều kiện thắng.

Người chơi có cần đạt được một số điểm cụ thể hoặc đánh bại một số lượng kẻ thù nhất định không? Họ cần đến một địa điểm cụ thể hay lấy một món đồ cụ thể nào đó không? Đây là những điều kiện thắng lợi phổ biến có thể trở thành mục tiêu dựa trên ScriptableObject.

ObjectiveManager đóng vai trò là bối cảnh tổng thể lớn hơn cho ScriptableObjects. Nó duy trì một danh sách các ObjectiveSO và dựa vào từng ScriptableObject để xác định xem nó đã hoàn chỉnh hay chưa. Khi tất cả ObjectiveSO đều hiển thị trạng thái hoàn thành, trò chơi kết thúc.

Ví dụ, ScoreObjectiveSO thể hiện một cách để triển khai mục tiêu chấm điểm:

  • Một cấu trúc PlayerScore tùy chỉnh khớp với ID người chơi, một phần tử giao diện người dùng và giá trị điểm số thực tế.
  • Mỗi khi thành phần ScoreManager được cập nhật, mục tiêu sẽ kiểm tra điều kiện thắng.
  • Nếu điểm số của người chơi đạt hoặc vượt quá m_TargetScore , thì hệ thống sẽ gửi đối tượng PlayerScore chiến thắng dưới dạng một sự kiện.

Trình quản lý mục tiêu chỉ quan tâm đến việc tất cả các mục tiêu đã được hoàn thành. Nó không nắm rõ các chi tiết bên trong từng mục tiêu cụ thể.

Một lần nữa, mục tiêu ở đây là tính mô-đun. Điều này cho phép bạn tùy chỉnh từng ObjectiveSO mà không ảnh hưởng đến những ObjectiveSO đã có sẵn.

Trò chơi PaddleBallSO thực chất chỉ có một mục tiêu duy nhất. Nếu một trong các người chơi đạt được số điểm chiến thắng, trò chơi kết thúc.

Tuy nhiên, bạn có thể mở rộng hoặc kết hợp các mục tiêu này để tạo một hệ thống mục tiêu phức tạp hơn. Hãy thử nghiệm và xem liệu bạn có thể tạo ra các chế độ chơi mới (ví dụ: ghi số điểm tối thiểu trước khi hết thời gian).

Vì logic được gói gọn bên trong một ScriptableObject, bạn có thể thay thế bất kỳ ObjectiveSO nào bằng một ObjectiveSO khác. Việc tạo ra điều kiện thắng mới chỉ đơn giản là cấu hình lại danh sách trong ObjectiveManager. Theo một nghĩa nào đó, mục tiêu này có thể "lắp ghép" vào bối cảnh xung quanh.

Lưu ý rằng một khía cạnh hữu ích của ObjectiveSO là sự kiện được sử dụng để gửi tin nhắn giữa GameObjects. Tiếp theo, chúng ta sẽ tìm hiểu cách sử dụng ScriptableObjects để triển khai kiến ​​trúc hướng sự kiện này.

phần kết có thể lập trình

Thêm tài nguyên ScriptableObject

Tìm hiểu thêm về các mẫu thiết kế với ScriptableObjects trong sách điện tử "Tạo kiến ​​trúc trò chơi mô-đun trong Unity với ScriptableObjects" . Bạn cũng có thể tìm hiểu thêm về các mẫu thiết kế phát triển Unity phổ biến trong bài viết "Nâng tầm mã nguồn của bạn với các mẫu lập trình game" .