Некоторое время я интересовался системой сущностей, основанной на компонентах, и читал бесчисленные статьи по ней ( игры Insomiac , довольно стандартная Evolve Your Hierarchy , T-Machine , Chronoclast ... и многие другие).
Кажется, что все они имеют внешнюю структуру:
Entity e = Entity.Create();
e.AddComponent(RenderComponent, ...);
//do lots of stuff
e.GetComponent<PositionComponent>(...).SetPos(4, 5, 6);
И если вы привнесете идею общих данных (это лучший дизайн, который я когда-либо видел, с точки зрения не дублирования данных везде)
e.GetProperty<string>("Name").Value = "blah";
Да, это очень эффективно. Тем не менее, это не совсем легко читать или писать; он чувствует себя очень неуклюже и работает против вас.
Я лично хотел бы сделать что-то вроде:
e.SetPosition(4, 5, 6);
e.Name = "Blah";
Хотя, конечно, единственный способ добраться до такого дизайна - вернуться к иерархии Entity-> NPC-> Enemy-> FlyingEnemy-> FlyingEnemyWithAHatOn, которую этот дизайн пытается избежать.
Кто-нибудь видел дизайн такого рода компонентной системы, который по-прежнему гибок, но при этом поддерживает уровень удобства для пользователя? И в этом отношении, удастся ли обойти (вероятно, самая сложная проблема) хранилище данных хорошим способом?
Какие проекты существуют для системы сущностей на основе компонентов, которые являются удобными для пользователя, но все же гибкими?
источник
Я бы посоветовал какой-то класс Interface для ваших объектов Entity. Он может выполнять обработку ошибок с проверкой, чтобы убедиться, что сущность содержит компонент соответствующего значения в одном месте, поэтому вам не придется делать это везде, где вы получаете доступ к значениям. С другой стороны, большинство разработок, которые я когда-либо делал для системы на основе компонентов, я имею дело с компонентами напрямую, запрашивая, например, их позиционный компонент, а затем напрямую / обновляя свойства этого компонента.
Очень базовый на высоком уровне, класс принимает рассматриваемый объект и предоставляет простой в использовании интерфейс с нижележащими компонентами следующим образом:
источник
В Python вы можете перехватить часть setPosition
e.SetPosition(4,5,6)
через объявление__getattr__
функции в Entity. Эта функция может выполнять итерацию по компонентам, находить соответствующий метод или свойство и возвращать его, так что вызов функции или ее назначение направляются в нужное место. Возможно, в C # есть похожая система, но это может быть невозможно из-за статической типизации - она, вероятно, не может быть скомпилирована,e.setPosition
если в интерфейсе не установлено setPosition.Возможно, вы также можете заставить все ваши сущности реализовывать соответствующие интерфейсы для всех компонентов, которые вы когда-либо добавляете к ним, с помощью методов-заглушек, которые вызывают исключение. Затем, когда вы вызываете addComponent, вы просто перенаправляете функции объекта для интерфейса этого компонента на компонент. Хотя немного неуверенно.
Но , пожалуй , проще всего было бы перегрузить оператор] [на классе Entity искать присоединенные компоненты на наличие действительного имущества и возврата , что, так что он может быть назначен следующим образом:
e["Name"] = "blah"
. Каждый компонент должен реализовать свое собственное GetPropertyByName, и Entity вызывает каждого из них по очереди, пока не найдет компонент, отвечающий за рассматриваемое свойство.источник
GetProperty
Единственным недостатком является манипулирование строками и сравнение. Но это спорный вопрос.Чтобы расширить ответ Kylotan, если вы используете C # 4.0, вы можете статически ввести переменную, которая будет динамической .
Если вы наследуете от System.Dynamic.DynamicObject, вы можете переопределить TryGetMember и TrySetMember (среди множества виртуальных методов), чтобы перехватывать имена компонентов и возвращать запрошенный компонент.
Я написал немного о системах сущностей за эти годы, но я предполагаю, что не могу конкурировать с гигантами. Некоторые примечания ES (несколько устаревшие и не обязательно отражающие мое текущее понимание систем сущностей, но это стоит прочитать, imho).
источник
object
параметр, но все это будет разрешено во время выполнения. Я думаю, что это прославленный способ сохранить свободный интерфейс над то есть, entity.TryGetComponent (compName, out компонент). Однако я не уверен, что понял вопрос :)Я вижу большой интерес здесь в ES на C #. Проверьте мой порт отличной реализации ES Artemis:
https://github.com/thelinuxlich/artemis_CSharp
Кроме того, пример игры, которая поможет вам начать работу (с использованием XNA 4): https://github.com/thelinuxlich/starwarrior_CSharp
Предложения приветствуются!
источник
Я решил использовать отличную систему отражения C #. Я основал это на общей системе компонентов, плюс смесь ответов здесь.
Компоненты добавляются очень легко:
И может быть запрошен либо по имени, типу или (если общие, такие как Health и Position) по свойствам:
И удалил:
К данным, опять же, обычно обращаются через свойства:
Который хранится на стороне компонента; меньше накладных расходов, если у него нет HP:
Intellisense в VS помогает набирать текст в больших количествах, но по большей части печатать мало - то, что я искал.
За кулисами я храню
Dictionary<Type, Component>
иComponents
переопределяюToString
метод проверки имени. В моем оригинальном дизайне в основном использовались строки для имен и вещей, но с ним стало работать свинья (о, смотри, мне приходится использовать повсюду, а также отсутствие доступных для доступа свойств).источник