Script yang mudah digunakan saat menggunakan ECS?

8

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 menampung xdan ymengoordinasikan, dan Movementkomponen menyimpan speeddan directionvariabel.
  • Sistem : Menangani komponen, misalnya dibutuhkan Positiondan Movementkomponen dan menambahkan speeddan directionke posisi ini xdan ykoordinat.

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).

Charanor
sumber
Saya akan menulis ini sebagai komentar karena saya sedikit tidak jelas tentang implementasi persis Anda (saya berasumsi C ++). Tidak bisakah Anda menggunakan templat di suatu tempat di sini? Untuk menerapkan komponen X terhadap komponen Y? Saya membayangkan kemudian bahwa komponen perlu mengganti metode "terapkan" dasar dan mengkhususkannya untuk jenis komponen yang dapat diterapkan padanya. Mengandalkan SFINAE akan memastikan bahwa itu berfungsi ketika seharusnya. Atau bisa dikhususkan dalam Systemkelas / s untuk memungkinkan komponen tetap struct data.
NeomerArcana
Mengapa tidak memaparkan 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.
Naros
Saya belum memiliki kesempatan untuk melakukannya, tetapi apakah mungkin untuk menambahkan "pintasan" hanya ke operasi yang lebih umum dilakukan?
Vaillancourt

Jawaban:

1

Anda bisa membuat sistem ScriptExecutionSystem yang beroperasi di semua entitas dengan komponen Script. Ia memperoleh semua komponen entitas yang dapat berguna untuk mengekspos ke sistem scripting dan meneruskannya ke fungsi scripted.

Pendekatan lain adalah membuat pengguna Anda juga merangkul ECS dan memungkinkan mereka untuk mendefinisikan komponen mereka sendiri dan menerapkan sistem mereka sendiri menggunakan bahasa scripting.

Philipp
sumber
0

ECS memiliki pro dan kontra. Skrip yang mudah digunakan bukan salah satu kelebihannya.

Masalah yang dipecahkan ECS adalah kemampuan untuk memiliki banyak hal serupa dalam gim Anda pada saat bersamaan sambil mempertahankan kinerja. Tetapi solusi ini datang dengan biaya - biaya arsitektur yang mudah digunakan. Ini bukan arsitektur terbaik untuk setiap game.

Sebagai contoh, ECS akan menjadi pilihan yang baik untuk Space Invaders , tetapi tidak begitu banyak untuk PacMan .

Jadi bukan jawaban yang Anda cari, tetapi mungkin ECS bukan alat yang tepat untuk pekerjaan Anda.

Jika Anda menambahkan pembungkus, perhatikan biaya overhead. Jika Anda akhirnya menghapus peningkatan kinerja ECS di bungkus Anda, maka Anda memiliki yang terburuk dari kedua dunia.


Tetapi untuk langsung menjawab pertanyaan Anda - "Untuk semua pengembang game yang telah mengimplementasikan scripting di ECS sebelumnya - bagaimana Anda melakukannya?"

Sama persis seperti yang Anda lakukan, tanpa bungkus. Entitas tidak memiliki apa pun selain pengidentifikasi. Komponen tidak memiliki apa-apa selain data. Sistem tidak memiliki apa pun kecuali logika. Sistem yang menerima entitas dengan komponen yang diperlukan dijalankan. Tambahkan sistem, entitas, dan komponen secara bebas.

Saya pernah menggunakan kerangka kerja dengan aspek keempat, yang disebut papan tulis. Itu pada dasarnya adalah cara bagi sistem untuk berkomunikasi dengan satu sama lain. Itu menciptakan lebih banyak masalah daripada yang dipecahkan.


Terkait: Haruskah saya menerapkan Sistem Komponen Entitas di semua proyek saya?

Evorlor
sumber
0

Dengan ECS Anda dapat memecah menjadi satu tanggung jawab, sehingga setiap entitas yang bergerak akan menginginkan dua komponen data: a MoveComponent, dan MoveSpeedComponent.

using System;
using Unity.Entities;

[Serializable]
public struct MoveForward : IComponentData{}
////////////////////////////////////////////
using System;
using Unity.Entities;

[Serializable]
public struct MoveSpeed : IComponentData
{
public float Value;
}
///////////////////////////////////////////

jadi sekarang dalam konversi Anda menambahkan komponen-komponen itu ke entitas Anda

public class MoveForwardConversion : MonoBehaviour, IConvertGameObjectToEntity
{
public float speed = 50f;

public void Convert(Entity entity, EntityManager manager,       GameObjectConversionSystem conversionSystem)
{
    manager.AddComponent(entity, typeof(MoveForward));

    MoveSpeed moveSpeed = new MoveSpeed { Value = speed };
    manager.AddComponentData(entity, moveSpeed);     
}

Sekarang setelah kita memiliki konversi dan data yang dapat kita pindahkan ke sistem, saya menghapus sistem input agar mudah dibaca tetapi jika Anda ingin mempelajari lebih lanjut tentang sistem input, saya akan memiliki semuanya tercantum dalam artikel saya minggu depan di unity connect.

using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;

namespace Unity.Transforms
{
public class MoveForwardSystem : JobComponentSystem
{
    [BurstCompile]
    [RequireComponentTag(typeof(MoveForward))]
    struct MoveForwardRotation : IJobForEach<Translation, MoveSpeed>
    {
        public float dt;

        public void Execute(ref Translation pos, [ReadOnly] ref MoveSpeed speed)
        {
            pos.Value = pos.Value + (dt * speed.Value);            
           // pos.Value.z += playerInput.Horizontal;
        }
    }
}

perhatikan bahwa kelas di atas menggunakan Unity.Mathmatics. Ini bagus untuk dapat menggunakan berbagai fungsi matematika yang biasa Anda gunakan dalam sistem normal. Dengan itu semua sejalan Anda sekarang dapat bekerja pada perilaku entitas - lagi saya menghapus input di sini tapi ini semua jauh lebih baik dijelaskan dalam artikel.

using Unity.Entities;
using UnityEngine;

public class EntityBehaviour : MonoBehaviour, IConvertGameObjectToEntity
{
public float speed = 22f;

void Update()
{
    Vector3 movement = transform.forward * speed * Time.deltaTime;
}
public void Convert(Entity entity, EntityManager manager, GameObjectConversionSystem conversionSystem)
{   
    //set speed of the entity
    MoveSpeed moveSpeed = new MoveSpeed { Value = speed };
    //take horizontal inputs for entites
    //PlayerInput horizontalinput = new PlayerInput { Horizontal = Input.GetAxis("Horizontal") };

    //add move component to entity
    manager.AddComponent(entity, typeof(MoveForward));
    //add component data  
    manager.AddComponentData(entity, moveSpeed);
   // manager.AddComponentData(entity, horizontalinput);
}
}

Sekarang Anda dapat memperkenalkan entitas yang akan bergerak maju dengan cepat.

Tetapi juga ini akan memindahkan setiap entitas dengan perilaku ini sehingga Anda dapat memperkenalkan tag, misalnya jika Anda menambahkan PlayerTag, maka hanya entitas dengan playerTag IComponentData yang akan dapat melakukan MoveForward jika saya hanya ingin memindahkan pemain seperti contoh di bawah.

Saya akan membahas lebih dalam pada artikel tersebut, tetapi kelihatannya seperti ini di ComponentSystem yang khas

    Entities.WithAll<PlayerTag>().ForEach((ref Translation pos) =>
    {
        pos = new Translation { Value =  /*PlayerPosition*/ };
    });

Banyak dari hal ini dijelaskan dengan cukup baik dalam presentasi Angry Dots dengan Mike Geig, jika Anda belum melihat saya sarankan untuk memeriksanya. Saya akan menunjukkan artikel saya juga setelah terserah. Seharusnya benar-benar membantu untuk mendapatkan beberapa hal yang biasa Anda kerjakan sesuai keinginan Anda di ECS.

Justin Markwell
sumber