Я что-то в последнее время сидел думал над Component/Entity-based System применительно к разработке игр. Вот единственная вменяемая ссылка для тех, кто шпрехает на инглише. Если кратко резюмировать, то получается как-то так.
Есть у нас игра и какие-то Сущности в ней (entity — сущность поанглицки). Например, игрок и мобы. В терминах ооп есть цепочка наследования. Игрок и моб наследуются от каких-то классов в цепочке MovableObject, ControlledObject итд. Дальше от моба наследование идет в сторону FlyingMob, UndergroundMob итд. Какие-то типы мобов с разными особенностями и поведением. Очень вероятно, что когда-нибудь нам понадобится летающий, плавающий и одновременно с этим синий моб, а наша цепочка наследования такого не позволяет (привет множественное наследование). Теперь о component-based системах. В данном случае Сущность — это контейнер с уникальным ID для компонентов, которые описывают эту сущность. То есть, то что моб летающий, плавающий и синий мы говорим просто добавляя к нему FlyingComponent, SwimmingComponent и BlueComponent. Просмотрев несколько примеров, я увидел, что компоненты могут быть как пассивными хранилищами данных, так и большими кусочками логики. Но это не важно.
UI.
Так вот, забавно было бы применить такую же идею к UI. То есть вот есть Сущность, она кнопка. А я говорю — стань-ка ты List’ом, и кнопка становится листом с какими-то последствиями. В общем, я вскоре на это забил, потому что получается какая-то неведомая йухня. Для UI как нельзя хорошо работает старое доброе дерево наследования.
Data (Model).
Потом я думал над применением только к данным. Ведь, это то, для чего народ и использует сей подход. Данным в смысле MVC.
Данные самопроизвольно принимают структуру ориентированного графа, где нодами являются объекты со своими свойствами, а стрелочки — отношения. Например, есть у нас Сущность user — некоторый игрок в системе. У него есть items, которыми он владеет и slots — слоты, куда можно эти итемы засунуть. Картинка снизу примерно это и показывает.

У сущности сверху есть свойство, что оно user. А также коллекция итемов и слотов. Сущность итема тоже говорит, что оно item, плюс к этому свойства owned (принадлежит) и equipped (надето, да, спелчекер подсказал, что пишется с двумя p, но я уже закрыл исходник картинки), которые ссылаются на соответствующие Сущности. Снизу slot (слоты), в одном из них есть ссылка equipment, то есть в него что-то положили. Если разделить взаимные ссылки (например, items и owned), то получается ориентированный граф.
Понятия.
Основные понятия получаются следующие:
- Entity (сущность) — некий объект, который сам по себе. У сущностей есть id, я думаю уникальный в системе вообще, то есть любую сущность можно всегда найти по id.
- Property (свойство) — то, что называют еще компонентом или тэгом. Свойство сущности, которое определяет ее как “является тем-то”.
- Link (ссылка) — ссылка/ссылки на другие entity. По сути, ребра в графе. На самом деле являются частным случаем свойсв.
С ссылками не все так просто, они могут быть одновременно и коллекциями (много ссылок с одинаковыми названиями) или ребрами гиперграфа, но гиперграф не умещается в моем миниатюрном мозге. Могут быть следующие типы ссылок:
- 1 .. много — в примере items –> item x *
- много .. 1 — наоборот, item x * –> items
- 1 .. 1 — equipment –> equiped
- много .. много — если, например, может быть несколько хозяев у итема
Ссылки могут быть двусторонними, например items –> owned — двусторонняя ссылка. Проще говоря, если я делаю ссылку из user в некий item (добавляю item к юзеру), то на Сущности итема появляется свойство-ссылка owned. Если она там уже есть, то так как это ссылка типа 1 .. много, этот итем убирается из предыдущего хозяина и прицепляется к новому хозяину. Что-то напоминает? Да, частным случаем евляется отношение parent-child. Ссылка slots односторонняя, слоту в данном случае совершенно пофиг кому он принадлежит (как определять выполнимость 1 .. много в данном случае яхз).
Вроде бы должно быть все более-менее просто.
События.
Во фреймворке компании TimeZero есть прикольная фишка, которая мне нравится — баблинг событий по дереву данных. Как в дереве дисплейлиста. То есть, я могу быть ленивой скотиной и не подписываться ко всякой милюзге, а тупо подписаться на событие где-то сильно выше у родителей. Нужное мне событие туда всплывет. Здесь можно сделать также, если считать, что ссылки (даже двусторонние) имеют начало и конец. То есть баблинг идет с конца. Иначе попадаем на движение событий по кругу в совершенно ненужные части, в идеале зацикливание гг.
То есть, допустим итема есть свойство Damaged. Оно там у себя внутри считает и хранит насколько поломано оружие. Как только оружие полностью сломалось, оно шлет событие. Это событие доходит до слота, слот может что-то с ним сделать, если заинтересован в таком событии. Дальше оно идет до юзера. На юзере какие-то свойства могут учесть эту инфу и сделать какие-то выводы. Ну и дальше это же событие всплывает еще выше, пока его дерзко не обрывают или оно не упирается в последний элемент иерархии.
Итог.
Получается забавно, во всяком случае, пока я не заметил существенных косяков. Собственно, хочется услышать кто что думает. Изобрел я велосипед? Слишком сложно?
Я уже вижу некоторые возможные забавные расширения.