Код в проекте
Привет. Расскажу про то, как устроен код в проекте, чего придерживался, что пришлось менять. Тут есть, о чем поговорить. Я поделюсь своим опытом.
Разграничения
Как уже писал в предыдущей статье, пространства имен – это отличный способ разграничить островки игровой логики в проекте в крупном масштабе. На более низком уровне идет разбиение на классы. Помните, Божественный объект – это антипаттерн. К слову, один заказчик немного разбирался в программировании и всерьез хотел, чтобы мы делали именно божественные объекты в играх.
Тогда еще совет. Вы – специалист своего дела, который знает технические подробности реализации проекта, и это ваша обязанность – настоять на правильном обоснованном техническом решении.
Альманах
Не забываем про абстракцию. Если того требует ситуация, сначала делаем абстрактный класс или интерфейс, потом более конкретную реализацию. Не беспокойтесь, никогда не поздно начать извлекать методы и выделять классы.
Интерфейс или абстрактный класс? Абстрактный класс уместней использовать при очевидной общности наследников, интерфейс же – лишь модель поведения, которую могут реализовать объекты, не имеющие ничего общего между собой.
Совет. Учи рефакторинг, это реально полезно. Учи горячие клавиши в среде программирования, это реально полезно. На новом месте работы я научился многому у старших коллег, и в то же время мне тоже было что рассказать и показать им.
Пример абстракции. Когда главный герой знакомится с новым видом противника, то игроку показывается описание нового врага и его особенности, характерные черты поведения. То же самое происходит с новыми подобранными игроком свитками. Логично сделать некую основу, в которой будут меняться детали. Поэтому данные о свитке и данные о новом противнике унаследованы от общего класса от ScriptableObject. Страница описания противника, и страница описания свитка тоже имеют сходство, но в описании противника мы еще показываем список характерных черт, а в свитках есть только описание.
Только спустя пару лет практики программирования начинаешь понимать принципы SOLID и ООП по-настоящему. Не просто так в каждом ролике, в каждой статье или интервью с разработчиками повторяется как мантра: каждая механика – это отдельный островок, который работает независимо от других; удаляя один компонент, другой не должен сломаться… Старайтесь придерживаться принципов программирования, это в любом случае дешевле обойдется, чем потом переделывать все, поправляя одно, ломая тем самым другое.
Совет. Не переставайте менять именование полей, методов и классов до тех пор, пока не станет максимально удовлетворять вас. Именование должно лаконично передавать суть и все. Классы именуются как имя существительное, методы как глагол. События привычно писать со слова On (OnHit, OnLoaded, OnError), но методы, которые на них подписываются, должны передавать свою суть (GetDamage, Unfade, SetDefaultValues). Помните, большую часть времени вы прежде всего читаете код, нежели непосредственно пишите его. Решиться на изменения в коде гораздо легче, если хорошо владеть IDE (например, переименование и извлечение метода).
Совет. Используйте ключевое слово field в атрибутах, оно позволяет воспринимать свойства как поля, отображая их в инспекторе, но все еще инкапсулировать данные. Не пренебрегайте ограничением доступа к данным. Этот трюк сокращает количество строчек в 2 раза, Карл! Этот десяток строк мог выглядеть как 20 строк.
Спаун противников
Тут вроде все просто. Враги появляются по волнам. Волны, количество противников и период их создания во время волны определяется в SpawnerData. Они могут появляться на протяжении всей волны, могут в начале волны или только в конце; по одному в секунду или сразу скопом.
С самого начала я не до конца продумал как именно будут появляться противники на арене, а я очень хотел внести какую-то оригинальность в моей игре. Поэтому мне пришлось переопределить архитектуру этой части кода. Теперь точка появления и логика этого события делегируются в отдельный класс SpawnDealer. Он стал отвечать за то, как именно и где появится противник. Например, арахниды появляются с помощью червя Нидуса, если такого нет на сцене, сначала появится червь, проиграется анимация, а уж потом появится противник из него. Духи появляются только в темных участках арены, а демоны в точках, где есть огонь (факел, костер, пожар). Некоторые противники появляются с помощью молний, только на участках арены под открытым небом.
Красными точками отмечены темные места для появления Духов, а белыми точки под открытым небом для создания молнии и спауна противников из них.
Урон и здоровье
Эту часть игры тоже пришлось переписывать после первых попыток. Итогом стали абстрактный класс Health и его наследники HealthPart и HealthParent. Предполагалось, что попадание в разные части тела (HealthPart) дает множитель на получаемый урон, передавая значения на HealthParent, который у противника есть в единственном экземпляре. Это решение из общей папки с кодовой базой, на самом деле в проекте используется только HealthParent.
Так как в игре есть стихийный урон и различные мета-данные нанесения урона (с какой стороны прилетел удар, точка удара, физическая сила удара, префабы попадания), я сделал структуру HitData, а также DamageDealer для удобства заполнения этих данных.
Совет. Старайтесь подписываться на события (если это не дочерние компоненты), нежели напрямую завязывать внешние объекты между собой. Это уменьшит связность проекта, позволит убирать или добавлять что-либо на сцену, не ломаю остальное.
Завершение
Понимаешь диаграммы классов? Что-то стоит поправить? Что думаешь? Есть польза от написанного? Критикуй, пиши, прочтем. Я очень надеюсь, что делаю какое-то полезное дело, в надежде повысить свою и вашу грамотность в написании кода.
Разработчик | Проект | YouTube | e-mail | Поддержка