Поводження зі сценаріями та "нативними" компонентами в системі компонентів на базі компонентів


11

Наразі я намагаюся реалізувати компонентну систему сутностей, де суть є лише ідентифікатором та деякими допоміжними методами, пов’язуючи купу компонентів разом для формування об’єкта гри. Деякі цілі:

  1. Компоненти містять лише стан (наприклад, положення, стан здоров'я, кількість патронів) => логіка переходить у "системи", які обробляють ці компоненти та їх стан (наприклад, PhysicsSystem, RenderSystem тощо)
  2. Я хочу реалізувати компоненти та системи як у чистому C #, так і за допомогою скриптів (Lua). В основному я хочу мати можливість визначати абсолютно нові компоненти та системи безпосередньо в Lua без необхідності перекомпілювати джерело C #.

Тепер я шукаю ідеї, як ефективно та неупереджено поводитися з цим, тому мені не потрібно використовувати різні синтаксиси для доступу до компонентів C # або компонентів Lua, наприклад.

Мій сучасний підхід полягав би в тому, щоб реалізувати компоненти C # за допомогою регулярних, загальнодоступних властивостей, ймовірно, прикрашених деякими атрибутами, що розповідають редактору про значення за замовчуванням та інші речі. Тоді я мав би клас C # "ScriptComponent", який просто загортає таблицю 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", як користувацькі дані. Доступ до значень із загорнутої таблиці Lua призведе до того, що я викликаю GetProperty<T>(..)метод замість прямого доступу до властивостей, як і для інших компонентів C #.

Можливо, напишіть спеціальний getComponentsFromEntity()метод лише для виклику з Lua, який повертає всі рідні компоненти C # як дані користувача, за винятком "ScriptComponent", де він замість цього поверне обгорнуту таблицю. Але будуть інші методи, що стосуються компонентів, і я не хочу дублювати всі ці методи як для виклику з коду C #, так і з скрипту 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 під кришкою, легко підкласирувати існуючі класи з боку "скриптування" та передавати екземпляри цих класів назад до хост-програми. У моєму випадку я б навіть кешував складену збірку сценаріїв для збільшення швидкості завантаження та просто відновлював її, коли сценарії змінювалися.
Юстиніан

Ні, я не зациклювався на використанні Lua. Особисто мені дуже хотілося б використовувати його через його простоту, невеликий розмір, швидкість та популярність (тому шанси людей, які вже знайомі з цим, здаються вищими), але я відкритий для альтернатив.
Маріо

Чи можу я запитати, чому ви хочете мати однакові можливості визначення між C # та середовищем сценарію? Звичайний дизайн - це визначення компонентів у C #, а потім 'об'єкт об'єкта' мовою сценаріїв. Мені цікаво, яке питання виникло, що це рішення?
Джеймс

1
Мета - зробити систему компонентів розширюваною за межами вихідного коду C #. Не тільки шляхом встановлення значень властивостей у файлах XML чи скриптів, але шляхом визначення цілих нових компонентів із цілими новими властивостями та нових систем, що їх обробляють. Це в значній мірі натхнене описами об'єктної системи Скотта Біласа в Dungeon Siege, де вони мали "рідні" компоненти C ++, а також "GoSkritComponent", який сам по собі є лише обгорткою сценарію: scottbilas.com/games/dungeon -siege
Маріо

Остерігайтеся потрапляння в пастку побудови інтерфейсу сценаріїв або плагінів настільки складним, що він наближається до перезапису оригінального інструменту (C # або Visual Studio в цьому випадку).
3Dave

Відповіді:


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 #, так і скрипт.
Маріо

Я думав, що C # використовується як якесь "прискорене сценарійне" середовище для таких речей, як C ++ чи C? У будь-якому випадку, якщо ви не впевнені, на якій мові ви опинитеся, просто почніть намагатися написати певну логіку в Lua та C #. Я думаю, що врешті-решт ви швидше станете на C # через велику кількість інструментів редактора та налагодження.
Імі

4
+1 Я погоджуюся з цим ще з іншої причини - якщо ви хочете, щоб ваша гра була розширюваною, вам слід їсти власну собачу їжу: ви можете виїхати, інтегруючи механізм сценаріїв лише для того, щоб знайти вашу потенційну модульну спільноту насправді нічого не зможете зробити це.
Джонатан Дікінсон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.