Saat ini saya sedang membuat proyek hobi kecil untuk kembali ke pengembangan game, dan saya telah memutuskan untuk membuat struktur entitas saya menggunakan ECS (Entity Component System). Implementasi ECS ini disusun seperti:
- Entitas : Dalam kasus saya ini adalah
intpengidentifikasi unik yang digunakan sebagai kunci untuk daftar komponen. - Komponen : Memegang hanya data, mis.
PositionKomponen menampungxdanymengoordinasikan, danMovementkomponen menyimpanspeeddandirectionvariabel. - Sistem : Menangani komponen, misalnya dibutuhkan
PositiondanMovementkomponen dan menambahkanspeeddandirectionke posisi inixdanykoordinat.
Ini berfungsi dengan baik, tapi sekarang saya ingin menerapkan skrip ke dalam permainan saya, dalam bentuk bahasa scripting. Dalam proyek-proyek sebelumnya saya telah menggunakan implementasi OOP objek game, yang berarti bahwa scripting cukup lurus ke depan. Misalnya, skrip sederhana dapat terlihat seperti ini:
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
Namun, ketika menggunakan ECS, entitas itu sendiri tidak memiliki fungsi seperti moveToatau getInventory, sebaliknya skrip di atas yang ditulis dengan gaya ECS akan terlihat seperti ini:
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
Ini jauh lebih banyak verbose dibandingkan dengan versi OOP, yang tidak diinginkan ketika skrip ditujukan untuk sebagian besar non-programmer (pemain game).
Salah satu solusinya adalah memiliki semacam objek pembungkus yang merangkum Entitydan memasok fungsi-fungsi seperti moveTosecara langsung, dan menangani sisanya secara internal, meskipun solusi semacam itu tampaknya kurang optimal karena butuh banyak pekerjaan untuk mencakup semua komponen, dan setiap saat komponen baru ditambahkan, Anda perlu mengubah objek pembungkus dengan fungsi baru.
Untuk semua pengembang game yang telah mengimplementasikan skrip di ECS sebelumnya - bagaimana Anda melakukannya? Fokus utama di sini adalah kegunaan untuk pengguna akhir, dengan biaya "pemeliharaan" sesedikit mungkin (sebaiknya Anda tidak perlu mengubahnya setiap kali Anda menambahkan komponen).
moveTometode ini sebagai bagian dari sistem yang mendasari dalam kasus penggunaan Anda, misalnya MovementSystem? Dengan cara ini Anda tidak hanya dapat menggunakannya dalam skrip yang Anda tulis, tetapi Anda juga dapat menggunakannya sebagai bagian dari kode C ++ di mana pun Anda membutuhkannya. Jadi ya Anda harus memaparkan metode baru ketika sistem baru ditambahkan, tapi itu diharapkan sebagai perilaku yang sama sekali baru yang diperkenalkan sistem ini.
Systemkelas / s untuk memungkinkan komponen tetap struct data.