Приемы при проектировании архитектуры игр

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

    Для примеров будет использоваться популярный движок Unity3D. Рассматриваются подходы, применимые в больших играх. Написано из моего личного опыта и из того, как я это понимаю. Конечно, где-то я могу быть не прав, где-то можно лучше сделать. Я тоже все еще в процессе набирания опыта и набивания новых шишек.

    В публикации рассматриваются следующие темы:
    • Наследование VS компоненты
    • Сложные иерархии классов юнитов, предметов и прочего
    • Машины состояний, деревья поведений
    • Абстракции игровых объектов
    • Упрощение доступа к другим компонентам в объекте, сцене
    • Сложные составные игровые объекты
    • Характеристики объектов в игре
    • Модификаторы (баффы/дебаффы)
    • Сериализация данных


    Наследование VS компоненты


    В больших играх довольно сложная архитектура. Сложные сущности и сложное взаимодействие между классами. Если пытаться разрабатывать игры, используя стандартный ООП подход, то гарантированы постоянные переделки куча кода и сильное увеличение длительности разработки.

    Проблема кроется в наследовании (проблема хрупких базовых классов — ситуация, когда изменить реализацию типа-предка невозможно, не нарушив корректность функционирования типов-потомков). При поиске решений для борьбы с этой проблемой был сформирован Компонентно-ориентированный подход (КОП). Вкратце, суть КОП следующая:
    «Есть некий класс-контейнер, а также класс-компонент, который можно добавить в класс-контейнер. Объект состоит из контейнера и компонентов в этом контейнере.»

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

    КОП упрощает повторное использование написанного кода – использование одного компонента в разных объектах. Также из различных комбинаций уже существующих компонентов, можно собрать новый тип объекта.

    Для примера, возьмем объект «персонаж». С точки зрения ООП – это был бы один большой класс, возможно, наследуемый от чего-то. С точки зрения КОП – это набор компонентов, составляющих объект «персонаж». Например: характеристики/статы персонажа – компонент «Stats», управление персонажем “CharacterController”, анимация персонажа – “CharacterAnimationController”, обработчик столкновений – «CharacterCollisionHandler».

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

    Сложные иерархии классов юнитов, предметов и прочего


    Многие разработчики, незнакомые или плохо знакомые с КОП, делают одну и ту же ошибку при проектировании сложных систем классов для юнитов, предметов. Или даже правильно выделяют компоненты, но зачем-то делают наследование AI компонентов.

    Рассмотрим подробнее иерархию классов юнитов разных типов. Для сокращения текста, юниты в данном контексте могут быть и персонажами и зданиями.



    Красными линиями показаны проблемные места – наследуемый тип должен обладать свойствами, поведением разных типов. В данном примере, что ни придумывай – ничего не решит проблему постоянных изменений всех классов в данной иерархии.

    На картинке ниже показано, как часть этой схемы могла бы выглядеть при использовании КОП.



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

    Машины состояний, деревья поведений


    Схема в предыдущей части была очень упрощенной. На самом деле компонентов понадобится гораздо больше. Также понадобится организовывать их взаимодействие друг с другом. Для упрощения работы с объектами со сложным поведением (юниты, персонажи) используются машины состояний, деревья поведений.

    1. Машина состояний
    Логика объекта разбивается на состояния, события, переходы, а также может разбиваться еще и на действия. Вариации реализации этих элементов могут заметно отличаться.
    • Состояние объекта – может быть как классом без игровой логики, просто хранящий некие данные, например, название состояния объекта: атака, перемещение. Либо класс «состояние» может описывать поведение объекта в конкретном состоянии.
    • Действие – функция, которая может выполниться в данном состоянии.
    • Переход – связь между 2-мя состояниями. Указывает, из какого состояния в какое возможен переход.
    • Событие – некое сообщение/команда, передаваемое в машину состояний или вызываемое внутри нее. Служит для указания, что надо выполнить переход в другое состояние, если это возможно из текущего состояния.

    На скриншоте изображена диаграмма (граф) машины состояний, сделанная в PlayMaker.



    Интересно реализована машина состояний в плагине Behaviour Machine. Там состоянием является MonoBehaviour компонент, отвечающий за логику работы в этом состоянии. Причем, состоянием также может быть дерево поведения.

    2. Иерархическая машина состояний
    Когда состояний много, увеличивается количество связей между ними, что усложняет работу с графом машины состояний. Для упрощения работы с ним, можно использовать иерархическую машину состояний. Она отличается тем, что в качестве состояния можно использовать вложенную машину состояний. Таким образом получается древовидная иерархия состояний.

    3. Дерево поведения
    Для упрощения написания AI, в играх используются деревья поведений (Behavior Tree).

    Дерево поведения — это древовидная структура, в качестве узлов которой выступают небольшие блоки игровой логики. Из различных блоков логики разработчик конструирует в визуальном редакторе древовидную структуру, настраивает узлы дерева. Эта структура будет отвечать за принятия решений персонажем и его взаимодействие с игровым миром.

    Каждый узел возвращает результат, от которого зависит, как будут обрабатываться остальные узлы дерева. Варианты возвращаемого результата обычно следующие: успех, неудача, выполняется.

    Основные типы узлов в дереве поведения:
    • Action Node (действие)
      Просто некоторая функция, которая должна выполниться при посещении данного узла.
    • Condition (условие)
      Обычно служит для того, чтобы определить, выполнять или нет следующие за ним узлы. При true вернет Success, а при false возвращает Fail.
    • Sequencer (последовательность)
      Выполняет все вложенные узлы по порядку, пока какой-либо из них не завершится неудачей (в таком случае возвращает Fail), либо пока все они успешно не завершатся (тогда возвращает Success).
    • Selector (селектор)
      В отличие от Sequencer, прекращает обработку, как только любой вложенный узел вернет Success.
    • Iterator (итератор — выполняет роль цикла for)
      Используется для выполнения в цикле серии действий некоторое число раз.
    • Parallel Node
      Выполняет все свои дочерние узлы «одновременно». Здесь не имеется ввиду, что узлы выполняются несколькими потоками. Просто создается иллюзия параллельного выполнения, аналогично корутинам в Unity3d.

    На скриншоте изображено дерево поведения, сделанное в плагине Behaviour Machine.



    Когда лучше использовать машину состояний, а когда дерево поведения?
    В книге «Artificial Intelligence for Games II» на 370-ой странице говорится, что деревья поведения сложнее реализовать, если им нужно реагировать на события извне. Также там предлагается возможное решение – ввести понятия «задача» (например: патрулирование, преследование противника, атака противника) в дерево поведения. Т.е. предполагается, что контроллер дерева поведения будет переходить на другой узел-задачу, чтобы изменить поведение. Также в книге предлагается альтернативный вариант – объединить дерево поведения с машиной состояний. Кстати, в плагине Behaviour Machine это уже реализовано.

    Пробовал использовать первый вариант – ввести узлы-задачи в дерево поведения. Сильно усложняется работа, т.к. надо не только реализовать смену задач, но и реализовать «обнуление» переменных завершенной/отмененной задачи.

    От себя добавлю — если AI сам получает данные из мира и не получает каких-либо команд, то Behavior Tree подходит для него. Если же AI чем-то управляется – человеком или другим AI (например, юнит в стратегиях управляется игроком-компьютером или отрядом), то лучше использовать стейт-машину.

    Абстракции игровых объектов


    Не стоит рассматривать, что управляемый игроком персонаж – это объект Player. Также не стоит считать, что этим персонажем может управлять только человек или только компьютер. Кто знает, как в будущем будет переделан геймплей.
    Player (может быть как компьютер, так и человек; в одном классе смешивать их не стоит) – это отдельный объект. Персонаж/юнит – также отдельный объект, которым может управлять любой игрок. В стратегических играх к тому же можно отдельно вынести объект «отряд».

    Разделить игровую логику на подобные объекты не сложно. К тому же она будет применима к множеству различных игр.

    Упрощение доступа к другим компонентам в объекте, сцене


    При большом количестве компонентов в объекте, появляется неудобство при надобности обращения к ним. Постоянно приходится заводить в каждом компоненте поля для хранения ссылок на другие компоненты или обращаться к ним через GetComponent().

    Паттерн медиатор натолкнул меня ввести некий компонент-посредник, через который компоненты могли бы обращаться друг к другу. К тому же это позволит вынести проверку существования других компонентов в данный компонент и код понадобиться писать только 1 раз. Такой компонент у разных типов объектов тоже стоит делать разным, т.к. используются разные наборы компонентов. В данном случае – это не реализация паттерна медиатор, а просто кэширование ссылок в одном классе для удобства доступа к другим компонентам объекта.

    Пример:

    public class CharacterLinks : MonoBehaviour
    {
    	public Stats stats;
    	public CharacterAnimationController animationController;
    	public CharacterController characterController;
    
    	void Awake()
    	{
    		stats = GetComponent<Stats>();
    		animationController = GetComponent<CharacterAnimationController>();
    		characterController = GetComponent<CharacterController>();
    	}
    }
    
    public class CharacterAnimationController : MonoBehaviour
    {
    	CharacterLinks _links;
    
    	void Start()
    	{
    		_links = GetComponent<CharacterLinks>();
    	}
    
    	void Update()
    	{
    		if (_links.characterController.isGrounded)
    			...
    	}
    }
    


    В сценах аналогичная ситуация. Можно в неком объекте синглтоне сделать ссылки на часто используемые компоненты, чтобы в инспекторе конкретных компонентов не приходилось постоянно указывать ссылки на другие объекты.

    Пример:

    public class GameSceneUILinks : MonoSingleton<GameSceneUILinks>
    {
    	public MainMenu MainMenu;
    	public SettingsMenu SettingsMenu;
    	public Tooltip Tooltip;
    }
    

    Использование:
    GameSceneUILinks.Instance.MainMenu.Show();
    

    Т.к. компоненты нужно указать только в одном объекте, а не в нескольких, немного уменьшается объем работы в редакторе, да и кода в целом будет меньше.

    Сложные составные игровые объекты


    Персонажи, UI элементы и некоторые другие объекты могут состоять из большего числа компонентов-скриптов и множества вложенных объектов. Если плохо продумана иерархия подобных объектов, то это может сильно усложнить разработку.
    Рассмотрим 2 случая, когда важно продумывать иерархию объекта:
    • отдельные части объекта должны заменяться другими в процессе игры;
    • часть скриптов объекта должна работать только в одной сцене, а другая часть – в другой.

    Для начала рассмотрим первый случай.
    Можно полностью заменять объект, но если он после замены должен находиться в том же состоянии и иметь те же данные, что и до замены, то задача усложняется.
    Для упрощения замены какой-либо части объекта, его структуру можно организовать, например, так:
    Character
    • Data
    • ControlLogic (скрипты для управления персонажем)
    • RootBone (рутовая кость персонажа; компоненты Animator и скрипты для работы с IK должны быть здесь, иначе они не будут работать)
    • Animation (прочие скрипты для работы с анимацией)
    • Model

    Подобное разбиение позволит изменить внешний вид объекта или контроллер анимации, не сильно затрагивая остальные компоненты.

    Теперь рассмотрим второй случай.
    Например, есть некий объект со спрайтом и данными. При клике на него в сцене улучшений, нужно улучшить данный объект. А при клике на него в сцене игры должно выполниться какое-то игровое действие.

    Можно сделать 2 префаба, но тогда, если объектов много, придется настраивать в 2 раза больше префабов.

    Можно пойти другим путем и организовать структуру следующим образом:
    ObjectView (картинка объекта)
    • Data (данные объекта, используемые в обоих сценах)
    • UpgradeLogic (кнопка и скрипты для сцены улучшений)
    • GameLogic (кнопка и скрипты для сцены игры)

    Поле targetGraphic в кнопках должно ссылаться на картинку в ObjectView.
    Данный подход проверялся в uGUI.

    Характеристики объектов в игре


    Во многих играх у персонажей, предметов, способностей и т.д. есть какие-то характеристики (здоровье, мана, прочность, продолжительность). Причем у различных типов, набор характеристик отличается.
    Из своего опыта могу сказать, что будет удобней работать с характеристиками, если их вынести в отдельный компонент(ы). Другими словами — отделить данные от функционала.

    Чтобы не создавать кучу классов для хранения различных характеристик в сложной системе, лучше хранить их в словаре в отдельном классе, контролирующим работу с этим словарем.

    Также, скорее всего, понадобиться завести специальный класс для хранения самих значений в словаре. Таким образом, словарь будет хранить не сами значения, а экземпляры класса-обертки для этих значений.
    Что может быть в таком классе:
    • Событие, вызываемое при изменении значения;
    • Значение, которое может быть нескольких видов, например:
      1. текущее значение;
      2. минимальное и максимальное значения (например, случайная сила атаки в диапазоне значений 10 — 20);
      3. текущее и максимальное значения (например, здоровье).

    Универсальности добиться довольно сложно. Наборы статов у предметов одни (прочность), у персонажей другие (здоровье, мана), у способностей – третьи (длительность перезарядки). Эти наборы не должны пересекаться, чтобы не возникало путаницы. Лучше их хранить в разных enum-ах, а не в одном. К тому же появляется проблема задания характеристик в инспекторе, т.к. словари и тип “object” не сериализуются в Unity3D.

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

    Модификаторы (баффы/дебаффы)


    Характеристики персонажа/предмета/способности могут меняться из-за воздействия каких-либо наложенных эффектов или эффектов одетых предметов. Сущность, которая изменяет эти характеристики, в данном контексте я буду называть модификатором. Сам я с модификаторами не работал, но тема важная. Поэтому опишу свои соображения по тому, как это можно организовать в коде.

    Модификатор – некий компонент, в котором перечислены характеристики, на которые он влияет, и величины влияния. Возможно будет даже лучше, если модификатор будет влиять только на одну указанную характеристику. Когда на персонажа накладывается эффект, к нему добавляется компонент-модификатор. Далее модификатор вызывает функцию «применить себя к такому-то объекту», и выполняется пересчет характеристик объекта. Причем только тех характеристик, которые он затрагивает. При удалении модификатора – аналогично выполняется перерасчёт. Скорее всего, понадобиться хранить 2 словаря характеристик – актуальных (рассчитанных) и изначальных.

    Перерасчет нужен, чтобы не приходилось при каждом обращении к данным персонажа постоянно вычислять актуальные значения. Т.е. обычное кэширование для увеличения производительности.

    Сериализация данных


    При разработке игр все еще очень часто используют XML, хотя есть альтернативы, зачастую более удобные – JSON, SQLite, хранения данных в префабах. Конечно, выбор зависит от задач.

    При использовании XML или JSON, многие используют довольно неоптимальные способы работы с ними. С кучей кода для чтения/записи/создания структур в данных форматах, да еще и с необходимостью указывать в строковом виде названия имен элементов, к которым нужно обращаться.

    Вместо всего этого можно использовать сериализацию. Структура XML и JSON в этом случае будет генерироваться из кода (Code First подход).

    Для сериализации XML в Unity3D можно использовать встроенные в .NET средства, а для JSON можно использовать плагин JsonFx. Работоспособность обоих решений я проверял на Android. Вроде как должно работать и на других платформах, т.к. используемое API применяется в сторонних кроссплатформенных плагинах.

    Пример использования XML сериализации:
    Saving and Loading Data: XmlSerialize

    Что можно почитать по архитектуре в играх


    В электронном виде есть перевод следующих книг на русский язык:
    • Роллингз Э., Моррис Д. Проектирование и архитектура игр (второе издание, 2006 г.)
    • Game Programming Patterns

    Также полезно поразбирать сторонние системы визуального скриптинга, например: PlayMaker, Behaviour Machine, Behavior Designer, Rain AI (полезен для изучения, но не удобен в реальных проектах). Из них можно почерпнуть некоторые идеи, посмотреть на какие логические блоки можно разделить игровые классы.
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 30
    • +6
      Хотел бы я почитать про архитектуру какой-нибудь популярный игры, например Dota 2.
      • 0
        Что именно вас интересует?
        • –3
          Сразу определимся — а есть что рассказать?
          • 0
            Ну я многое знаю про устройство Source Engine и как оно работает в контексте Dota 2. Если бы вы мне подсказали, какой аспект интересует — я думаю статью бы написал.
            • 0
              Конкретно мне интересно послушать про организацию способностей, что они из себя представляют, как реализованы на уровне исходного кода. Если играли, то наверняка замечали, что их там великое множество, довольно четко прописаны правила их взаимодействия, все прямо скажем непросто. Это «что именно». Но и в целом про основной цикл, распределение ролей по клиенту и серверу и т.д.

              P.S. не нужно воспринимать мой довольно резкий вопрос как большинство (минусами в карму), просто не хотел тратить время на пояснения, а в ответ ничего не услышать.
      • +4
        Спасибо, полезная статья.
        • +7
          «машины состояний» (state machines) — это то, что в общепринятой русскоязычной терминологии называется конечными автоматами
          • +1
            Тогда уж finite-state machine (fsm) — «конечный автомат».
            • +2
              Мне показалось, что «машина состояний» будет понятней большему числу читателей, чем «конечный автомат». Не все в институте учились, и не у всех была теория автоматов. Но вы правы, стоит упомянуть в статье про конечный автомат.
              • 0
                >>> «машины состояний» (state machines) — это то, что в общепринятой русскоязычной терминологии называется конечными автоматами

                Использование кальки «машина состояний» позволяет избежать смешения с математической абстракцией «конечный автомат». Программистская машина состояний принадлежит к другому классу мощности, нежели математический конечный автомат. В частности, допускает «бесконечное» множество состояний (параметризация состояний), бесконечный алфавит (параметризация событий), наличие состояний у состояний (sic!), зависимость переходов не только от явного входа и текущего состояния, но и от состояния «мира».
              • +1
                нигде нет более менее полной публикации на тему проектирования архитектуры в играх

                А как же Game Coding Complete?
                • +2
                  Спасибо за ссылку на книгу! Надо будет глянуть, в том числе и другие ее части.
                  Правильнее было бы сказать, что я не встретил книги или публикации, где собраны воедино описания реализаций типовых игровых механик, таких как:
                  характеристики объектов;
                  баффы/дебаффы;
                  способности/заклинания;
                  организация процессов экипировки персонажа, изучения навыков, покупки, особенно в клиент-серверных играх;
                  система диалогов;
                  система квестов.

                  Пока что пролистал мельком книгу. Про КОП, FSM, Behavior Tree в ней написано. А вот про перечисленное выше я не увидел. Большинство книг по архитектуре игр обычно ориентированно скорее на создание архитектуры движка, чем самой игры.
                • 0
                  нигде нет более менее полной публикации на тему проектирования архитектуры в играх


                  Да ладно.
                  • +3
                    Вам тоже спасибо за книгу! Хотя ранее она мне уже попадалась. Вот только времени почитать не нашлось.
                    Могу сказать только то же, что и в моем комменте выше.
                  • +1
                    Хорошая статья. Я тоже пришел к аналогичному приему. Но с некоторыми поправками. Использую MVC, где Unity3d view и иногда часть модельной логики (через интерфейсы). Unit — модельный класс, имеющий базовые поля и словарь стратегий, во вью аналогичное решение но с юнити компонентами. А на уровне общения только IAction (для контроллеров) и IEvent для (View или сети).
                    • 0
                      С XML существует проблема на WinRT. Рекомендую использовать Xml.Linq XElement и тд. Но с ней существует проблема в Web player. Лечится это, через копирование System.Xml.Linq.dll (лучше вытянуть из папки unity) в Plugins и указанием билда только под веб.
                      • +1
                        Характеристики объектов в игре

                        Предложение автору. Как я решил подобную проблему. Все Unit имееют стратегии. Не важно что это heath mana и тд. С бафами можно обыграть так.
                        Код
                        namespace Engine
                        { 
                           
                            public abstract class UnitStrategyBase 
                            {
                        
                                public virtual EventHandler Handler { get; set; }
                        
                                public virtual Unit CurrentUnit { get; set; }
                        
                                public virtual void OnAdd(Unit unit)
                                {
                                    CurrentUnit = unit;
                                }
                                public virtual void OnRemove(Unit unit)
                                {
                                    CurrentUnit = null;
                                }
                        
                                public virtual void Send(IEvent @event)
                                {
                                    if (Handler != null)
                                    {
                                        Handler.Send(@event);
                                    }
                                }
                            }
                        }
                        
                        


                        При добавлении стратегии, например баф. Сам баф сохраняет дельту, и обновляет нужные данные. После удаления, по этой самой дельте вернуть изначальное состояние. И так далее. Однажды пробовал обратную связь. Тоже работает, мол Armor например ищет бафы и сам возвращает результат. Но такой подход не очень, когда бафов становится огромное n количество.
                        • +1
                          Спасибо! Толковое предложение!
                          Я тоже предполагал, что при добавлении/удалении лучше только 1 операцию сделать (прибавить/отнять), а не все баффы брать для данной характеристики и вычислять. Но т.к. не делал, не был уверен, что получиться избежать перерасчёта при наличии разных операций: умножить, увеличить на заданный процент и т.д.
                        • +1
                          Про ооп vs коп: у вас в обоих случаях описывается ооп. Когда говорите об ооп подразумеваете наследование, а коп агрегацию. Действительно, обычно лучше агрегироваться, а не наследоваться. Коп — это уровень сборки приложения, когда у компонента есть не только код, но и данные и ui.

                          Для кругозора можно еще почитать про аспекты (ваши компоненты на них сильно похожи) и мультиагентное программирование.
                          • +2
                            КОП как бы более продвинутое ООП. ООП. Родственные методологии.

                            Коп — это уровень сборки приложения, когда у компонента есть не только код, но и данные и ui.
                            Тут просто одно и то же название используется в разных контекстах. То, о чем вы говорите – скорее модулями правильней называть, а не компонентами.
                            https://ru.wikipedia.org/wiki/Модульность_(программирование)
                            Шаблоны игрового программирования.Компонент

                            Для кругозора можно еще почитать про аспекты (ваши компоненты на них сильно похожи) и мультиагентное программирование.
                            Полезно конечно будет почитать. Но из того, что я сейчас прочитал про аспекты – не сказал бы, что мои компоненты на них похожи. Я так понял, что если какой-то общий функционал вынести в отдельный метод, и как-то организовать его вызов при выполнении любого метода в любом классе, к которому он привязан, то эта дополнительная привязанная функциональность и будет называться аспектом.
                            Аспектно-ориентированное программирование на C#
                            • 0
                              КОП не продвинутое ООП, а надстройка над ним. Согласен с вами по сути изложенного, но не согласен по форме: нельзя противопоставлять ООП и КОП, иначе, прочитав вашу статью, может сложиться впечатление, что ООП — устаревшее зло без права на всплытие.
                              В вашем примере, кстати, компоненты, отвечающие за урон ближнего боя и урон дальнего боя, вполне логично пронаследовать от одного компонента, отвечающего за урон в принципе.
                              Согласен, что не нужно пытаться использовать ООП для объектов игрового мира, т.к. они заведомо собираются из различных компонент (начиная от transform и rigitbody, заканчивая контроллерами баффов и собственных действий), но его можно и нужно использовать для реализации аспектов, действительно связанных отношениями родитель-потомок.
                              • 0
                                Статья не идеальная, что тут сказать. Тема компонентного подхода плоховато раскрыта.

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

                                В вашем примере, кстати, компоненты, отвечающие за урон ближнего боя и урон дальнего боя, вполне логично пронаследовать от одного компонента, отвечающего за урон в принципе.
                                Согласен. Да и упоминал немного в статье, что наследование компонентов – вполне нормально.
                          • –2
                            Еще, если действительно нужна динамическая динамичность, то можно использовать динамические языки на платформе .net. Позволит проще/чище реализовать нужную динамичность. Не знаю насколько это Юнити поддерживает.

                            И наоборот, если даже делать универсальное хранилище для характеристик объектов, то апи доступа к ним стоит сделать типизированным (не object GetPropertyByName, а int GetLivesCount).
                            • +1
                              И наоборот, если даже делать универсальное хранилище для характеристик объектов, то апи доступа к ним стоит сделать типизированным (не object GetPropertyByName, а int GetLivesCount).
                              Кстати, я делал аналогично для доступа к данным из аниматора в Mecanim. Становиться удобней работать.
                              public class AnimatorParams
                              {
                              	private Animator _animator;
                              
                              	public float Speed
                              	{
                              		get { return _animator.GetFloat("Speed"); }
                              		set { _animator.SetFloat("Speed", value); }
                              	}
                              ...
                              }
                              • 0
                                по string ключу дорого. Лучше используйте хеш.
                                 int [animation]key= Animator.StringToHash("Speed");
                                ...
                                 _animator.SetFloat( [animation]key, speed);
                                
                            • 0
                              Компонент медиатр это мега ошибка в коп, и каждый человек пытается создать свой класс для удобного доступа. В новой юнити наконец то все такие ссылки пометили как обсолет.
                              • 0
                                Можно чуточку пояснить Вашу мысль?
                                • +2
                                  Вот и мне непонятно. Не скажете, где говорится, что медиатор или что-то подобное плохо в коп? Мне таким образом стало куда удобней работать, чем в каждом компоненте заводить несколько ссылок на другие компоненты и вызывать getComponent() для получения каждого.

                                  Компоненты какого-то сложного объекта в любом случае как-то должны общаться друг с другом. Поэтому не вижу проблемы добавления класса-посредника для конкретного типа объекта.
                                  В случае посредников для объектов сцены, особенно если их разделять по подсистемам (гуи, звук), тоже не вижу проблем. Объектам сцены все равно как-то потребуется обращаться друг к другу.

                                  Касательно Unity3d. В нем раньше было сделано так, что у компонента были ссылки на кучу других компонентов, которые он не использует, что приводит к ненужной связанности компонентов. И это скорее создавало проблемы разработчикам движка, а не разработчикам, которые пишут на движке.
                                  • 0
                                    Ссылок не было, фактически, это были геттеры, каждый вызов которых выполнял запрос через GetComponent(typeof(T)), что было не быстро. Теперь они просто выпилили эти геттеры, что немного почистило апи.
                                • 0
                                  Статью не читай @ сразу отвечай.

                                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.