Hero image

Cách sử dụng bộ runtime dựa trên ScriptableObject

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.

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.

Bộ thời gian chạy

ScriptableObjects có thể lưu trữ dữ liệu tĩnh . Chúng cũng có thể được điều chỉnh để chứa dữ liệu động. Điều này đặc biệt hữu ích khi tham chiếu đến một tập hợp GameObjects hoặc các thành phần trong quá trình chạy.

Một "tập hợp thời gian chạy" như thế này mô phỏng lý do tại sao một số nhà phát triển sử dụng singleton - truy cập toàn cục dễ dàng. Thật không may, các đối tượng đơn lẻ (singleton) lại đi kèm với những hạn chế như các phụ thuộc không mong muốn. Những phụ thuộc bổ sung này có thể làm phức tạp quá trình kiểm thử và khiến việc gỡ lỗi trở nên khó khăn hơn.

Một phương án khác là xem xét sử dụng bộ runtime dựa trên ScriptableObject cho các thành phần được tham chiếu thường xuyên. Ví dụ, bạn có thể sử dụng chúng để theo dõi các yếu tố trong trò chơi như kẻ thù xuất hiện, vật phẩm sưu tầm hoặc điểm kiểm tra.

Với tư cách là các tài sản cấp dự án, ScriptableObjects cung cấp quyền truy cập toàn cục vào dữ liệu cho tất cả các đối tượng trong một cảnh. Tính năng này cho phép chia sẻ thông tin mà không gặp phải nhiều nhược điểm thường thấy ở các đối tượng đơn lẻ.

triển khai cơ bản

Đây là một cách để tạo một tập hợp thời gian chạy cho GameObjects. Hãy hình dung nó như một thùng chứa dữ liệu lưu trữ một tập hợp các phần tử công khai – nhưng cũng bao gồm các phương thức để thêm và xóa các phần tử.

Trong quá trình thực thi, bất kỳ MonoBehaviour nào cũng có thể tham chiếu đến thuộc tính Items công khai để lấy danh sách đầy đủ GameObjects. Một đoạn mã hoặc thành phần khác có thể gọi Add hoặc Remove để sửa đổi danh sách Items .

sử dụng Collections;
Sử dụng UnityEngine;

[CreateAssetMenu(menuName = "GameObject Runtime Set", fileName
= "GORuntimeSet")]
public class GameObjectRuntimeSetSO: ScriptableObject
{

   Danh sách riêng tư các mục = danh sách mới ();
   Danh sách công khai Các mặt hàng => các mặt hàng;

   public void Add(GameObject thingToAdd)
   {
       if (!items.Contains(thingToAdd))
            items.Add(thingToAdd);
   }

   public void Remove(GameObject thingToRemove)
   {
       if (items.Contains(thingToRemove))
            items.Remove(thingToRemove);
   } 
}

Phiên bản chung

Bạn có thể thiết lập thời gian chạy cụ thể hơn để nó chỉ theo dõi một loại MonoBehaviour nhất định. Trong trường hợp đó, tạo các bộ thiết lập cụ thể cho từng loại (ví dụ: EnemyRuntimeSet, PickupRuntimeSet, v.v.).

Một lớp trừu tượng tổng quát có thể giúp đơn giản hóa quy trình này. Từ lớp cơ sở này, bạn có thể tạo ra các tập hợp thời gian chạy khác. Ví dụ, nếu bạn muốn tạo một bộ thời gian chạy cho một thành phần Enemy tùy chỉnh, bạn có thể định nghĩa các lớp cho RuntimeSetSO chung và EnemyRuntimeSetSO cụ thể như đoạn trích mã bên dưới.

Lưu ý rằng lớp cụ thể EnemyRuntimeSetSO không có bất kỳ chi tiết triển khai nào, chỉ có thuộc tính CreateAssetMenu .

Mỗi thành phần mới mà bạn muốn theo dõi chỉ cần một thiết lập thời gian chạy tương ứng để quản lý nó. Hãy tạo bao nhiêu lớp học cụ thể tùy thích. Kẻ thù, NPC, vật phẩm trong kho, nhiệm vụ, và nhiều thứ khác đều có thể có các bộ thiết lập thời gian chạy riêng.

public abstract class RuntimeSetSO : ScriptableObject
{
   Danh sách công khai Các mục = danh sách mới ();

   public void Add(T thing)
   {
       if (!Items.Contains(thing))
            Items.Add(thing);
   }

   public void Remove(T thing)
   {
       nếu (Items.Contains(thing))
            Items.Remove(thing);
   } 
}

[CreateAssetMenu(menuName = "Enemy Runtime Set", fileName =
"EnemyRuntimeSet")]

public class EnemyRuntimeSetSO: RuntimeSet
{

}
Lỗi không khớp

Cách sử dụng và giới hạn

Việc quản lý thiết lập thời gian chạy là tùy thuộc vào bạn. Thông thường, bạn có thể sử dụng MonoBehaviour thứ hai để thiết lập hoặc cập nhật nó trong quá trình chơi game.

Nếu bạn muốn một thành phần tự động được thêm vào một tập hợp, bạn cũng có thể gọi các phương thức Add hoặc Remove của tập hợp trong thời gian chạy từ các phương thức OnEnableOnDisable của MonoBehaviour.

Ví dụ, lớp Enemy giả định của bạn có thể trông giống như đoạn trích mã bên dưới.

Tại đây, mỗi thành phần Enemy có thể tự thêm hoặc xóa chính nó bằng cách sử dụng các phương thức OnEnable hoặc OnDisable . Nếu trường m_RuntimeSet được thiết lập trong Inspector, thành phần Enemy sẽ tự động xuất hiện trong EnemyRuntimeSetSO . Điều này đặc biệt hữu ích nếu bạn đang sử dụng thành phần Enemy với Prefabs.

Một hạn chế của kỹ thuật này là nếu bạn kiểm tra ScriptableObject trong thời gian chạy, bạn sẽ không thể thấy nội dung của danh sách Items trong Inspector theo mặc định.

Thay vào đó, thông báo "Không khớp kiểu dữ liệu" xuất hiện trong mỗi trường phần tử. Theo thiết kế, ScriptableObject không thể tuần tự hóa một đối tượng cảnh. Danh sách thực tế vẫn hoạt động, nhưng dữ liệu không hiển thị chính xác.

Hãy sử dụng thuộc tính công khai hoặc thuộc tính HideInInspector nếu bạn muốn tránh nhầm lẫn và ngăn danh sách hiển thị trong Inspector.

Bạn cũng có thể khắc phục sự cố này bằng cách sử dụng tập lệnh Editor tùy chỉnh và Inspector. Một số ví dụ điển hình bao gồm Soap – ScriptableObject Architecture Pattern trong Unity Asset Store hoặc ví dụ minh họa Patterns bên dưới.

public class Enemy: MonoBehaviour
{
     [SerializeField] private EnemyRuntimeSetSO m_RuntimeSet;

     private void OnEnable()
     {
         m_RuntimeSet.Add(this);
     }

     private void OnDisable()
     {
         m_RuntimeSet.Remove(this);
     }
}
Bộ thời gian chạy

Bản demo các mẫu

Mặc dù PaddleBallSO không có trường hợp sử dụng cụ thể nào cho các thiết lập thời gian chạy, nhưng bản demo Patterns của dự án bao gồm một ví dụ nhỏ minh họa cách thức hoạt động của mẫu thiết kế này.

Nhấp vào nút Tạo Khối để lấp đầy bức tường bằng các khối một cách dần dần. Quả bóng phá vỡ hàng rào khi tiếp xúc.

Chọn Select Set để hiển thị BlockRuntimeSet_Data trong Inspector. Đây là danh sách tất cả các khối trong khung cảnh.

Bạn có thể liên tục lấp đầy các khối vào lưới nhanh như tốc độ quả bóng lấy chúng đi. Đồng thời, tập hợp thời gian chạy theo dõi số lượng khối đang thay đổi liên tục trong danh sách Items.

Bản demo Patterns minh họa một vài cải tiến giúp nâng cao trải nghiệm người dùng khi làm việc với các tập hợp dữ liệu trong thời gian chạy:

  • Trình kiểm tra tùy chỉnh: Vì ScriptableObjects không thể tự động tuần tự hóa các đối tượng trong cảnh, nên tập lệnh GameObjectRuntimeSetEditor cung cấp một Inspector tùy chỉnh để ghi đè lỗi không khớp kiểu mặc định. Nhấp chuột vào bất kỳ khối nào trong cửa sổ Inspector để làm nổi bật khối đó trong cấu trúc phân cấp của cảnh.
  • Cập nhật sự kiện: Tập hợp thời gian chạy có một System.Action tên là ItemsChanged . Điều này thông báo cho tập lệnh Trình chỉnh sửa khi thêm hoặc xóa thành viên khỏi danh sách Mục. Thao tác này sẽ làm mới Trình kiểm tra khi danh sách các mục thay đổi. Kênh sự kiện cũng đồng bộ số lượng mục hiện tại với một thành phần RuntimeSetCounter tùy chỉnh. Điều này hiển thị tổng số khối trong giao diện người dùng trên màn hình.
Thanh tra hải quan

Cách các bộ thời gian chạy giúp ích

Lần tới khi bạn cần một danh sách tập trung GameObjects hoặc thành phần, hãy cân nhắc sử dụng runtime sets. Chúng có thể ít tốn kém hơn so với thao tác tìm kiếm ( Object.FindObjectOfType hoặc GameObject.FindWithTag ) và ít gây rắc rối hơn so với các đối tượng đơn lẻ (singleton).

Với tư cách là ScriptableObjects, chúng được tách rời khỏi GameObjects riêng lẻ trong cảnh. Hãy tạo nhiều bộ dữ liệu thời gian chạy khác nhau tùy theo nhu cầu của bạn đối với bất kỳ thứ gì bạn muốn theo dõi thường xuyên trong quá trình hoạt động.

Các thiết lập thời gian chạy có thể đơn giản hóa cách bạn giám sát các đối tượng trong trò chơi mà không gây ra các phụ thuộc không cần thiết. Bạn có thể sử dụng thời gian tiết kiệm được trong quá trình gỡ lỗi và kiểm thử để phát triển những trải nghiệm chơi game mới cho người chơi của mình.

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

Thêm tài nguyên ScriptableObject

Chúng tôi hy vọng rằng các bộ dữ liệu thời gian chạy có thể mang lại lợi ích cho các dự án sắp tới của bạn.

Tìm hiểu thêm về các mẫu thiết kế với ScriptableObjects trong sách điện tử kỹ thuật của chúng tôi, "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" .