Обработка скриптовых и «родных» компонентов в системе компонентов на основе компонентов

11

В настоящее время я пытаюсь реализовать систему сущностей, основанную на компонентах, где сущность - это в основном просто ID и несколько вспомогательных методов, связывающих кучу компонентов вместе для формирования игрового объекта. Некоторые цели этого включают:

  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. Например, я бы хотел написать сценарий Lua следующим образом:

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

nativeComponent.NativeProperty = 5
scriptComponent.ScriptedProperty = 3

Скрипт не должен заботиться о том, какой компонент он на самом деле получил, и я хотел бы использовать те же методы, которые я использовал бы на стороне C #, для извлечения, добавления или удаления компонентов.

Может быть, есть примеры реализации интеграции сценариев с такими системами сущностей?

Марио
источник
1
Вы застряли на Lua? Я делал подобные вещи, создавая сценарии для приложения C # с другими языками CLR, анализируемыми во время выполнения (Boo, в моем случае). Поскольку вы уже выполняете высокоуровневый код в управляемой среде, и все это скрыто, вы можете легко разделить существующие классы со стороны «сценариев» и передать экземпляры этих классов обратно в хост-приложение. В моем случае я бы даже кэшировал скомпилированную сборку скриптов для увеличения скорости загрузки и просто перестраивал ее, когда скрипты менялись.
Юстиниан
Нет, я не застрял с помощью Lua. Лично я бы очень хотелось , чтобы использовать его из - за своей простоты, малый размер, скорость и популярность (так что шансы людей уже будучи знакомы с ним , кажется , выше), но я открыт для альтернатив.
Mario
Могу я спросить , почему вы хотите , чтобы иметь возможности равные четкости между C # и среды сценария? Нормальный дизайн компонент определение в C # , а затем «объект сборка» на языке сценариев. Мне интересно , что вопрос возник , что это решение?
Джеймс
1
Цель состоит в том, чтобы сделать компонент системы расширяемым снаружи источника C # код. Не только путем установления значений свойств в XML или файлов сценариев, но определяя целые новые компоненты с целыми новыми свойствами и новых систем обработки их. Это во многом вдохновлен описания Скотта Bilaş системы объекта в Dungeon Siege, где они должны были „родные“ компоненты C ++, а также „GoSkritComponent“, которая сама по себе лишь обертка для сценария: scottbilas.com/games/dungeon -siege
Марио
Остерегайтесь попадания в ловушку построения сценариев или плагин интерфейс настолько сложный , что он приближается к переписывание исходного инструмента (C # или Visual Studio в данном случае).
3Dave

Ответы:

6

Следствием ответа FXIII в том , что вы должны разработать все (что было бы moddable) на языке сценариев первого (или , по крайней мере, очень приличная часть) , чтобы гарантировать , что ваша логика интеграции на самом деле положения для моддинга. Когда вы уверены , что ваша интеграция сценариев является универсальным достаточно переписать необходимые биты в 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 # или расширению своей игры?

Если ваша цель - вторая, как я полагаю, вы, вероятно, будете тратить свое время на то, чтобы сосредоточиться на игровой механике и позволить опытному интерпретатору запускать динамический код.

FXIII
источник
Да, все еще существует смутный страх плохой производительности для некоторых основных систем, если я сделаю все через скрипты. В этом случае мне пришлось бы перенести эту функциональность обратно на C # (или что-то еще) ... так что в любом случае мне нужен хороший способ взаимодействия с системой компонентов со стороны C #. В этом основная проблема: при создании системы компонентов я могу одинаково хорошо использовать как C #, так и скрипт.
Марио
Я думал, что C # используется в качестве некоторой «ускоренной скриптовой» среды для таких вещей, как C ++ или C? В любом случае, если вы не уверены, на каком языке вы окажетесь, просто начните пытаться написать логику на Lua и C #. Бьюсь об заклад, в конце концов, в большинстве случаев вы будете работать быстрее на C # из-за расширенных возможностей редактора и отладки.
Imi
4
+1 Я согласен с этим по другой причине - если вы хотите, чтобы ваша игра была расширяемой, вы должны есть свою собачью еду: вы можете использовать встроенный скрипт-движок только для того, чтобы найти ваше потенциальное сообщество моддингов, которое на самом деле ничего не может сделать с Это.
Джонатан Дикинсон