การจัดการส่วนประกอบที่เขียนสคริปต์และ "ดั้งเดิม" ในระบบเอนทิตีที่อิงองค์ประกอบ


11

ขณะนี้ฉันกำลังพยายามใช้ระบบเอนทิตีที่อิงองค์ประกอบเป็นหลักโดยที่เอนทิตีนั้นเป็นเพียง ID และวิธีการช่วยเหลือบางอย่างที่ผูกส่วนประกอบหลายอย่างเข้าด้วยกันเพื่อสร้างวัตถุเกม เป้าหมายบางประการของที่รวมถึง:

  1. ส่วนประกอบประกอบด้วยสถานะ (เช่นตำแหน่งสุขภาพจำนวนกระสุน) => ตรรกะเข้าสู่ "ระบบ" ซึ่งดำเนินการส่วนประกอบเหล่านี้และสถานะของพวกเขา (เช่น PhysicsSystem, RenderSystem ฯลฯ )
  2. ฉันต้องการใช้ส่วนประกอบและระบบทั้งใน C # และผ่านการเขียนสคริปต์ (Lua) โดยทั่วไปฉันต้องการที่จะสามารถกำหนดส่วนประกอบและระบบใหม่ทั้งหมดใน Lua ได้โดยตรงโดยไม่ต้องคอมไพล์ต้นฉบับ C # ของฉันอีกครั้ง

ตอนนี้ฉันกำลังมองหาแนวคิดเกี่ยวกับวิธีจัดการสิ่งนี้อย่างมีประสิทธิภาพและไม่สม่ำเสมอดังนั้นฉันไม่จำเป็นต้องใช้ไวยากรณ์ที่แตกต่างเพื่อเข้าถึงคอมโพเนนต์ C # หรือส่วนประกอบ Lua ตัวอย่างเช่น

แนวทางปัจจุบันของฉันคือการใช้ส่วนประกอบ C # โดยใช้คุณสมบัติสาธารณะทั่วไปอาจตกแต่งด้วยแอตทริบิวต์บางอย่างที่บอกบรรณาธิการเกี่ยวกับค่าเริ่มต้นและเนื้อหา จากนั้นฉันจะมี "ScriptComponent" ระดับ C ซึ่งเพิ่งล้อมตาราง Lua ภายในด้วยตารางนี้ถูกสร้างขึ้นโดยสคริปต์และเก็บสถานะทั้งหมดของประเภทองค์ประกอบเฉพาะนั้น ฉันไม่ต้องการเข้าถึงสถานะดังกล่าวจากด้าน C # มากเท่าที่ฉันจะไม่ทราบในเวลารวบรวมสิ่งที่ ScriptComponents พร้อมกับคุณสมบัติใดบ้างที่จะมีให้ฉัน ถึงกระนั้นผู้แก้ไขจะต้องเข้าใช้งาน แต่อินเทอร์เฟซแบบง่าย ๆ ดังต่อไปนี้น่าจะพอเพียง:

public ScriptComponent : IComponent
{
    public T GetProperty<T>(string propertyName) {..}
    public void SetProperty<T>(string propertyName, T value) {..}
}

นี่จะเข้าถึงตาราง Lua และตั้งค่าและเรียกคืนคุณสมบัติเหล่านั้นจาก Lua และสามารถรวมอยู่ในองค์ประกอบ C # ได้อย่างง่ายดายเช่นกัน (แต่ใช้คุณสมบัติ C # ปกติจากนั้นผ่านการสะท้อนกลับหรือบางอย่าง) สิ่งนี้จะใช้ในการแก้ไขไม่ใช่โดยรหัสเกมปกติดังนั้นประสิทธิภาพจึงไม่สำคัญเท่านี้ มันจะทำให้มันจำเป็นในการสร้างหรือเขียนคำอธิบายองค์ประกอบบางอย่างที่ระบุว่าคุณสมบัติขององค์ประกอบบางอย่างมีอยู่จริง แต่นั่นไม่ได้เป็นปัญหาใหญ่และอาจเป็นไปโดยอัตโนมัติอย่างเพียงพอ

อย่างไรก็ตามวิธีการเข้าถึงส่วนประกอบจากด้าน Lua ถ้าฉันโทรหาบางอย่างเช่นgetComponentsFromEntity(ENTITY_ID)ฉันอาจจะได้รับส่วนประกอบของ C # ดั้งเดิมรวมถึง "ScriptComponent" เป็น userdata การเข้าถึงค่าจากตาราง Lua ที่ห่อไว้จะทำให้ฉันเรียกGetProperty<T>(..)เมธอดแทนการเข้าถึงคุณสมบัติโดยตรงเช่นเดียวกับส่วนประกอบ C # อื่น ๆ

อาจจะเขียนgetComponentsFromEntity()วิธีพิเศษเฉพาะที่จะเรียกจาก Lua ซึ่งจะส่งกลับส่วนประกอบ C # ทั้งหมดเป็น userdata ยกเว้น "ScriptComponent" ซึ่งมันจะส่งคืนตารางที่ห่อแทน แต่จะมีวิธีการที่เกี่ยวข้องกับองค์ประกอบอื่น ๆ และฉันไม่ต้องการทำซ้ำวิธีการเหล่านี้ทั้งที่ถูกเรียกจากรหัส C # หรือจากสคริปต์ Lua

เป้าหมายสูงสุดคือการจัดการองค์ประกอบทุกประเภทเหมือนกันโดยไม่มีไวยากรณ์กรณีพิเศษที่แตกต่างระหว่างส่วนประกอบดั้งเดิมและส่วนประกอบ Lua - โดยเฉพาะจากด้าน Lua เช่นฉันต้องการเขียนบท Lua เช่นนั้น:

entity = getEntity(1);
nativeComponent = getComponent(entity, "SomeNativeComponent")
scriptComponent = getComponent(entity, "SomeScriptComponent")

nativeComponent.NativeProperty = 5
scriptComponent.ScriptedProperty = 3

สคริปต์ไม่ควรสนใจองค์ประกอบที่เป็นจริงและฉันต้องการใช้วิธีการเดียวกันกับที่ฉันใช้จากฝั่ง C # เพื่อดึงข้อมูลเพิ่มหรือลบส่วนประกอบ

อาจมีการใช้งานตัวอย่างของการรวมการเขียนสคริปต์กับระบบเอนทิตีเช่นนั้น?


1
คุณติดอยู่กับการใช้ Lua หรือไม่? ฉันได้ทำสิ่งที่คล้ายกันในการเขียนแอปพลิเคชัน C # กับภาษา CLR อื่น ๆ ที่ถูกแยกวิเคราะห์ที่รันไทม์ (Boo ในกรณีของฉัน) เนื่องจากคุณใช้งานโค้ดระดับสูงในสภาพแวดล้อมที่มีการจัดการอยู่แล้วและเป็น IL ทั้งหมดภายใต้ประทุนจึงเป็นเรื่องง่ายที่จะแบ่งคลาสย่อยที่มีอยู่จากด้าน "สคริปต์" และส่งอินสแตนซ์ของคลาสเหล่านั้นกลับไปยังโฮสต์แอปพลิเคชัน ในกรณีของฉันฉันจะแคชชุดประกอบของสคริปต์เพื่อเพิ่มความเร็วในการโหลดและเพิ่งสร้างเมื่อสคริปต์เปลี่ยน
justinian

ไม่ฉันไม่ติด Lua โดยส่วนตัวแล้วฉันชอบที่จะใช้มันมากเพราะความเรียบง่ายขนาดเล็กความเร็วและความนิยม (ดังนั้นโอกาสของคนที่คุ้นเคยกับมันจะสูงขึ้น) แต่ฉันเปิดทางเลือก
มาริโอ

ฉันอาจถามว่าทำไมคุณถึงต้องการความสามารถในการนิยามที่เท่าเทียมกันระหว่าง C # และสภาพแวดล้อมสคริปต์ของคุณ การออกแบบปกติคือการกำหนดองค์ประกอบใน C # แล้ว 'ประกอบวัตถุ' ในภาษาสคริปต์ ฉันสงสัยว่ามีปัญหาเกิดขึ้นว่านี่เป็นวิธีแก้ปัญหาใช่หรือไม่
James

1
เป้าหมายคือการทำให้ระบบส่วนประกอบขยายออกไปจากภายนอกซอร์สโค้ด C # ไม่เพียงแค่ตั้งค่าคุณสมบัติใน XML หรือไฟล์สคริปต์เท่านั้น แต่ด้วยการกำหนดส่วนประกอบใหม่ทั้งหมดด้วยคุณสมบัติใหม่ทั้งหมดและระบบใหม่ที่ประมวลผล นี่เป็นแรงบันดาลใจส่วนใหญ่จากคำอธิบายของ Scott Bilas เกี่ยวกับระบบวัตถุใน Dungeon Siege ที่ซึ่งพวกเขามีส่วนประกอบ C ++ ดั้งเดิมเช่นเดียวกับ "GoSkritComponent" ซึ่งเป็นเพียงตัวห่อหุ้มสคริปต์: scottbilas.com/games/dungeon -siege
Mario

ระวังการตกหลุมพรางของการสร้างสคริปต์หรือส่วนต่อประสานปลั๊กอินเพื่อให้มีความซับซ้อนซึ่งใกล้ถึงการเขียนเครื่องมือดั้งเดิม (C # หรือ Visual Studio ในกรณีนี้)
3

คำตอบ:


6

ข้อสรุปของคำตอบของ FxIIIคือคุณควรออกแบบทุกอย่าง (ซึ่งจะสามารถแก้ไขได้) ในภาษาสคริปต์ก่อน (หรืออย่างน้อยก็เป็นส่วนที่ดีมากของมัน) เพื่อให้แน่ใจว่าตรรกะการรวมของคุณนั้นเป็นจริงสำหรับการดัดแปลง เมื่อคุณมั่นใจว่าการรวมการเขียนสคริปต์ของคุณนั้นมีความหลากหลายเพียงพอที่จะเขียนบิตที่ต้องการใน C #

ฉันเกลียดdynamicคุณลักษณะนี้ใน C # 4.0 (ฉันเป็นภาษาขี้ยาคงที่) - แต่ไม่ต้องสงสัยเลยว่าจะต้องมีสถานการณ์ที่ควรใช้และเป็นที่ที่ส่องแสงจริงๆ องค์ประกอบ C # ของคุณจะเป็นวัตถุเชิงพฤติกรรมที่แข็งแกร่ง / ขยายได้

public class Villian : ExpandoComponent
{
    public void Sound() { Console.WriteLine("Cackle!"); }
}

//...

dynamic villian = new Villian();
villian.Position = new Vector2(10, 10); // Add a property.
villian.Sound(); // Use a method that was defined in a strong context.

องค์ประกอบ Lua ของคุณจะเป็นแบบไดนามิกอย่างสมบูรณ์ (บทความเดียวกันข้างต้นควรให้แนวคิดเกี่ยวกับวิธีการใช้งาน) ตัวอย่างเช่น:

// LuaSharp is my weapon of choice.
public class LuaObject : DynamicObject
{
    private LuaTable _table;

    public LuaObject(LuaTable table)
    {
        _table = table;
    }

    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
    {
        var methodName = binder.Name;
        var function = (LuaFunction)_table[methodName];
        if (function == null)
        {
            return base.TryInvokeMember(binder, args, out result);
        }
        else
        {
            var resultArray = function.Call(args);
            if (resultArray == null)
                result = null;
            else if (resultArray.Length == 1)
                result = resultArray[0];
            else
                result = resultArray;
            return true;
        }
    }

    // Other methods for properties etc.
}

ตอนนี้เพราะคุณกำลังใช้dynamicคุณสามารถใช้วัตถุ Lua ราวกับว่าเป็นคลาสจริงใน C #

dynamic cSharpVillian = new Villian();
dynamic luaVillian = new LuaObject(_script["teh", "villian"]);
cSharpVillian.Sound();
luaVillian.Sound(); // This will call into the Lua function.

ฉันเห็นด้วยกับย่อหน้าแรกฉันมักจะทำงานของฉันด้วยวิธีนี้ การเขียนโค้ดที่คุณรู้ว่าคุณอาจต้องนำมันมาใช้ใหม่ในภาษาระดับต่ำกว่านั้นก็เหมือนการกู้เงินรู้ว่าคุณจะต้องจ่ายเพื่อผลประโยชน์ นักออกแบบด้านไอทีมักจะให้สินเชื่อ: สิ่งนี้เป็นสิ่งที่ดีเมื่อพวกเขารู้ว่าพวกเขาทำมันและพวกเขาสามารถควบคุมหนี้สินได้
FxIII

2

ฉันต้องการทำสิ่งที่ตรงกันข้าม: เขียนทั้งหมดโดยใช้ภาษาแบบไดนามิกแล้วติดตามขวด (ถ้ามี) เมื่อคุณพบหนึ่งคุณสามารถเลือกใช้ฟังก์ชันที่เกี่ยวข้องกับการใช้ C # หรือ (แทนที่จะ) C / C ++

ฉันบอกคุณนี้ส่วนใหญ่เพราะสิ่งที่พยายามทำคือการ จำกัด ความยืดหยุ่นของระบบของคุณในส่วน C #; ในขณะที่ระบบซับซ้อน C # "เครื่องยนต์" ของคุณจะเริ่มดูเหมือนล่ามแบบไดนามิกซึ่งอาจไม่ใช่ตัวแปลที่ดีที่สุด คำถามคือคุณยินดีที่จะลงทุนเวลาส่วนใหญ่ในการเขียนโปรแกรม C # ทั่วไปหรือเพื่อขยายเกมของคุณหรือไม่?

หากเป้าหมายของคุณคือเป้าหมายที่สองตามที่ฉันเชื่อคุณอาจใช้เวลาของคุณมุ่งเน้นไปที่กลไกเกมของคุณให้ล่ามที่มีประสบการณ์เพื่อรันโค้ดแบบไดนามิก


ใช่ยังคงมีความกลัวที่คลุมเครือของประสิทธิภาพที่ไม่ดีสำหรับบางระบบหลักถ้าฉันจะทำทุกอย่างผ่านสคริปต์ ในกรณีนี้ฉันต้องย้ายฟังก์ชันการทำงานนั้นกลับไปที่ C # (หรืออะไรก็ตาม) ... ดังนั้นฉันจึงต้องการวิธีที่ดีในการเชื่อมต่อกับระบบคอมโพเนนต์จากด้าน C # นั่นเป็นปัญหาหลักที่นี่: การสร้างระบบส่วนประกอบที่ฉันสามารถใช้งานได้ดีอย่างเท่าเทียมกันจากทั้ง C # และสคริปต์
Mario

ฉันคิดว่าC #ใช้เป็นสภาพแวดล้อมแบบ "เร่งเหมือนสคริปต์" บางอย่างสำหรับ C ++ หรือ C? อย่างไรก็ตามหากคุณไม่แน่ใจว่าภาษาใดที่คุณจะต้องจบลองเริ่มเขียนตรรกะใน Lua และ C # ฉันพนันได้เลยว่าคุณจะจบลงด้วยความรวดเร็วใน C # ส่วนใหญ่เนื่องจากเครื่องมือการแก้ไขและการดีบักขั้นสูง
Imi

4
+1 ฉันเห็นด้วยกับเหตุผลนี้แม้ว่า - ถ้าคุณต้องการให้เกมของคุณขยายได้คุณควรกินอาหารสุนัขของคุณเอง: คุณอาจรวมการทำงานของสคริปต์ไว้เพื่อค้นหาชุมชน modding ที่มีศักยภาพของคุณ มัน.
Jonathan Dickinson
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.