Tôi hiện đang tạo một dự án sở thích nhỏ để quay lại phát triển trò chơi và tôi đã quyết định cấu trúc các thực thể của mình bằng ECS (Hệ thống thành phần thực thể). Việc triển khai ECS này có cấu trúc như vậy:
- Thực thể : Trong trường hợp của tôi, đó là một mã
int
định danh duy nhất được sử dụng làm chìa khóa cho danh sách các thành phần. - Thành phần : Chỉ giữ dữ liệu, ví dụ:
Position
thành phần giữ mộtx
vày
tọa độ, vàMovement
thành phần giữ mộtspeed
vàdirection
biến. - Hệ thống : Xử lý các thành phần, ví dụ: nó lấy
Position
vàMovement
các thành phần và thêmspeed
vàdirection
vào vị tríx
vày
tọa độ.
Điều này hoạt động tốt, nhưng bây giờ tôi muốn triển khai kịch bản vào các trò chơi của mình, dưới dạng ngôn ngữ kịch bản. Trong các dự án trước đây, tôi đã sử dụng triển khai OOP cho các đối tượng trò chơi, điều đó có nghĩa là kịch bản chuyển tiếp khá đơn giản. Ví dụ, một tập lệnh đơn giản có thể trông giống như thế này:
function start()
local future = entity:moveTo(pos1)
wait(future)
local response = entity:showDialog(dialog1)
if wait(response) == 1 then
local itemStack = entity:getInventory():removeItemByName("apple", 1)
world:getPlayer():getInventory():addItemStack(itemStack)
else
entity:setBehavior(world:getPlayer(), BEHAVIOR_HOSTILE)
end
end
Tuy nhiên, khi sử dụng một ECS, thực thể chính nó không có bất kỳ chức năng như moveTo
hoặc getInventory
, thay vào đó là kịch bản trên được viết theo phong cách ECS sẽ giống như thế này:
function start()
local movement = world:getComponent(MOVEMENT, entity)
movement:moveTo(pos1)
local position = world:getComponent(POSITION, entity)
local future = Future:untilEquals(position.pos, pos1)
wait(future)
local dialogComp = world:getComponent(DIALOG, entity)
local response = dialogComp:showDialog(dialog1)
if wait(response) == 1 then
local entityInventory = world:getComponent(INVENTORY, entity)
local playerInventory = world:getComponent(INVENTORY, world:getPlayer())
local itemStack = entityInventory:removeItemByName("apple", 1)
playerInventory:addItemStack(itemStack)
else
local entityBehavior = world:getComponent(BEHAVIOR, entity)
local playerBehavior = world:getComponent(BEHAVIOR, world:getPlayer())
entityBehavior:set(playerBehavior, BEHAVIOR_HOSTILE)
end
end
Đây là rất nhiều tiết hơn so với phiên bản OOP, mà không phải là mong muốn khi kịch bản được nhằm hướng tới chủ yếu không phải lập trình (cầu thủ của trò chơi).
Một giải pháp sẽ là có một số loại đối tượng trình bao bọc đóng gói Entity
và cung cấp các chức năng như moveTo
trực tiếp và xử lý phần còn lại bên trong, mặc dù giải pháp đó có vẻ không tối ưu vì phải mất rất nhiều công việc để bao gồm tất cả các thành phần và mọi khi một thành phần mới được thêm vào, bạn sẽ cần thay đổi đối tượng trình bao bọc với các hàm mới.
Đối với tất cả các nhà phát triển trò chơi đã triển khai kịch bản trong ECS trước đây - bạn đã làm như thế nào? Trọng tâm chính ở đây là khả năng sử dụng cho người dùng cuối, với chi phí "bảo trì" càng ít càng tốt (tốt nhất là bạn không cần thay đổi nó mỗi khi bạn thêm một thành phần).
moveTo
phương thức như một phần của hệ thống cơ bản trong trường hợp sử dụng của bạn, ví dụ MovementSystem? Cách này không chỉ sau đó bạn có thể sử dụng nó trong các tập lệnh bạn viết mà sau đó bạn cũng có thể sử dụng nó như một phần của mã C ++, nơi bạn cần nó. Vì vậy, có, bạn sẽ phải đưa ra các phương thức mới khi các hệ thống mới được thêm vào, nhưng đó là điều được mong đợi vì hành vi hoàn toàn mới của nó mà các hệ thống này vẫn giới thiệu.
System
lớp để cho phép các thành phần duy trì cấu trúc dữ liệu.