Туториалы для геймдева #0 StaticData

Привет, Пикабу!

Я тут обещал какое-то время назад серию туториалов для геймдева, ну в общем вот первый пост)

Требования для урока:

1)Мозг

2)Умение читать ОЧЕНЬ ОЧЕНЬ ОЧЕНЬ много текста

3)Умение читать доки

4)c#

5)Json

6)Примеры будут на Unity, т.к. мой проект на нём, но про альтернативу для Unreal расскажу

StaticData. Что это? Зачем оно нужно?

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


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

Первым порывом будет написать что-то такое:

\\код не мой, я просто разместил объяву


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



obj1 = new Class();

obj.a = 1;

obj.b = 2;



obj2 = new Class();

obj2.a = obj1.a * 1.05;

obj2.b = obj1.b * 4;


Ну вы поняли...

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


Опционально, если при инициализации ваш клиент пытается скачать таблицу из облака и обновить баланс игры. Т.е. для обновления баланса игроку надо будет выкачать пару МБ, а не новый билд весом пару сотен метров (некоторые движки не позволяют патчить части себя, а только полностью менять билд).


Так что такое StaticData? Это эта самая таблица, которая иницалзирует сама себя и хранит все значения которые не зависят от действий игрока. А главное, к ней можно обращаться в любой момент.

Туториалы для геймдева #0 StaticData Gamedev, Мануалы, Tutorialsgamedev, Progrmming, Csharp, Json, Unity, Unreal Engine 4, Длиннопост

Ну а теперь ОДНА ИЗ ВОЗМОЖНЫХ реализаций этого.

Unreal engine - Класс Data Table. Идите гуглите) Таблички можно создавать прямо в редакторе, а можно подгружать из csv. Очень удобно и всё такое. И мануалы простые легко гуглятся.


Unity - Тут я собственно на примере буду рассказывать как оно устроено и работает. На примере своего проекта.

Сначала немного о базовых механиках построения таблиц:


KeyName - наше основное значение. Это id. Уникальная строковая переменая, по которой всегда можно найти в StaticData объект.


Каждый лист таблицы занимает или список классов (строка - экземпляр\шаблон объекта), Либо один цельный объект. Примером второго является например Settings. Не настройки игрока, а базовые параметры игры. Для кода сверху, это были бы масса и радиус земли.


Имена переменных должны совпдать с аналогичными переменными в вашем коде.

Итак, как же у меня в игре устроен ОТРЯД СОЛДАТ, а вот так:

Лист называется SquadArchtype

Туториалы для геймдева #0 StaticData Gamedev, Мануалы, Tutorialsgamedev, Progrmming, Csharp, Json, Unity, Unreal Engine 4, Длиннопост

keyName - ключевое имя отряда (по нему например более глобальный класс Company (армия) тащит свои составляющие. Не буду показывать, чтобы не перегружать вас) Дальше идут типы отряда, 6 юнитов, флаг может ли этот отряд быть использован игроком и сила для расчёта мощи армии противника.


Давайте внимательнее посмотрим на юнитов (лист UnitArchtype)

Туториалы для геймдева #0 StaticData Gamedev, Мануалы, Tutorialsgamedev, Progrmming, Csharp, Json, Unity, Unreal Engine 4, Длиннопост

Всё ещё куча ключевых слов и никаких параметров. Заметьте по ключевым словам можно догадаться о чём тут вообще идёт речь! Тут можно заметить поля правой и левой руки, доспеха и активируемого предмета. baseUnit это класс базовых характеристик (а-ля раса, но не совсем). Взглянем например на оружие. (Weapon)

Туториалы для геймдева #0 StaticData Gamedev, Мануалы, Tutorialsgamedev, Progrmming, Csharp, Json, Unity, Unreal Engine 4, Длиннопост

Ого чиселки! Но урона у оружия нет) Вы же не думали что всё так просто? В моей архтектуре удар оружием не отличается от фаербола. Это всё активные способности, некоторые могут иметь или не иметь урон. Глубже в таблицу уходить не будем, думаю смысл понятен)


НО:


Тут заметим интересную вещь. Изменилсь цвета. Зелёный - вотчина геймдизайнера (в данном случае меня) это баланс. Синяя - вотчина художника. Вот например в sprite записаны имена спрайтов которые подгружаются в соответсвующее оружие. О том как и зачем - в следующих урока, но поверьте, удобно)

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

Самые внимательные могли заметить, что табличка хранится в гуглдоке. Итак, как же нам её оттуда достать.


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


https://assetstore.unity.com/packages/tools/utilities/google...


Что он делает - выкачивает гуглдок в Json и сохраняет в папке Resurces (для тех кто не шарит, в юнити это такая папка специальная, файлы откуда всегда уодят в билд, даже если на ни нет явных ссылок). Каждый листик сохраняет отдельно. Тут есть свои плюсы и минусы, но в большинстве случаев нам пофигу.

Качаем, ставим, читаем доки по заполнению таблиц, пользуемся.

Туториалы для геймдева #0 StaticData Gamedev, Мануалы, Tutorialsgamedev, Progrmming, Csharp, Json, Unity, Unreal Engine 4, Длиннопост

Прочитали? Ну и славно, едем дальше.

Собственно Static Data.

Есть два варианта - простой и сложный (внезапно)


Сложный - создаём класс с namespace которое всем доступно, инициализируем и тягаем оттуда инфу.


Простой - создаём GameObject на сцене, вешаем на него скрипт StaticData, он при запуске делает тоже самое. Если объекту нужны данные он его пинает и тащит данные.

Чтобы объект загружался из Json, он должен быть серриализуем.

Туториалы для геймдева #0 StaticData Gamedev, Мануалы, Tutorialsgamedev, Progrmming, Csharp, Json, Unity, Unreal Engine 4, Длиннопост

Вот примерно так выглядит класс Weapon (табличка сверху).

Теперь как же его собственно выцепить из таблички? Ну вот примерно так:

Туториалы для геймдева #0 StaticData Gamedev, Мануалы, Tutorialsgamedev, Progrmming, Csharp, Json, Unity, Unreal Engine 4, Длиннопост

Тут сейчас будет немного магии, специально для юнити. Кому не интересно, листают до следующего перерыва на кофе.

Json - в данном случае просто строковая переменная.

LoadResourceTextFile - вспомогательная функция для выкачки текста. вот она:

Туториалы для геймдева #0 StaticData Gamedev, Мануалы, Tutorialsgamedev, Progrmming, Csharp, Json, Unity, Unreal Engine 4, Длиннопост

Странное добавление по бокам - Json встроенный в юньку малось туповат, ему надо явно указывать что это список объектов, а не цельный объект.


Weapons - вспомогательный класс для серриализации. Требуется из-за того-же несовершенства Json. Вот он:

Туториалы для геймдева #0 StaticData Gamedev, Мануалы, Tutorialsgamedev, Progrmming, Csharp, Json, Unity, Unreal Engine 4, Длиннопост

weapons - список оружий с которым позже мы будем уже работать. (точнее сам список в Weapons.list, но не суть).

Туториалы для геймдева #0 StaticData Gamedev, Мануалы, Tutorialsgamedev, Progrmming, Csharp, Json, Unity, Unreal Engine 4, Длиннопост

Итак, мы почти у конца! Что же с этим делать? Ну для начала надо бы написать инициализатор который подтащит по ключевым именам (строкам) уже готовые и ранее серрилизованные объекты.

Туториалы для геймдева #0 StaticData Gamedev, Мануалы, Tutorialsgamedev, Progrmming, Csharp, Json, Unity, Unreal Engine 4, Длиннопост

Легчайше!


Ну а вот собственно например инициализатор оружия:

Туториалы для геймдева #0 StaticData Gamedev, Мануалы, Tutorialsgamedev, Progrmming, Csharp, Json, Unity, Unreal Engine 4, Длиннопост

Собсно что тут делать - дело ваше. У меня например только подтаскиваются пассивные и активные убилки из той же самой StaticData.

Итоги:


Мы получили МОЩНЕЙУЮ систему для хранения и доступа к данным. Которую из-за гуглдока также сложно потерять (ну если вы вдруг не умеете пользоваться гитом).


Пусть это не самый эффективный и аккуратный вариант реализации, но даже он сэкономит вам ТОННЫ времени) Удачи,  до встречи в следующем туториале!

P.S. Да, наконец то! Думал у меня пальцы отвалятся пока пишу! Фуууух)

Лига Разработчиков Видеоигр

6.6K постов22.1K подписчиков

Добавить пост

Правила сообщества

ОБЩИЕ ПРАВИЛА:

- Уважайте чужой труд и используйте конструктивную критику

- Не занимайтесь саморекламой, пишите качественные и интересные посты

- Никакой политики


СТОИТ ПУБЛИКОВАТЬ:

- Посты о Вашей игре с историей её разработки и описанием полученного опыта

- Обучающие материалы, туториалы

- Интервью с опытными разработчиками

- Анонсы бесплатных мероприятий для разработчиков и истории их посещения;
- Ваши работы, если Вы художник/композитор и хотите поделиться ими на безвозмездной основе

НЕ СТОИТ ПУБЛИКОВАТЬ:

- Посты, содержащие только вопрос или просьбу помочь
- Посты, содержащие только идею игры

- Посты, единственная цель которых - набор команды для разработки игры

- Посты, не относящиеся к тематике сообщества

Подобные посты по решению администрации могут быть перемещены из сообщества в общую ленту.

ЗАПРЕЩЕНО:

- Публиковать бессодержательные посты с рекламой Вашего проекта (см. следующий пункт), а также все прочие посты, содержащие рекламу/рекламные интеграции

- Выдавать чужой труд за свой

Подобные посты будут перемещены из сообщества в общую ленту, а их авторы по решению администрации могут быть внесены в игнор-лист сообщества.


О РАЗМЕЩЕНИИ ССЫЛОК:

Ссылка на сторонний ресурс, связанный с игрой, допускается только при следующих условиях:

- Пост должен быть содержательным и интересным для пользователей, нести пользу для сообщества

- Ссылка должна размещаться непосредственно в начале или конце поста и только один раз

- Cсылка размещается в формате: "Страница игры в Steam: URL"

6
DELETED
Автор поста оценил этот комментарий

А почему бы и не ScriptableObject? Зачем делать богомерзкий GameObject.Find, если можен синглетон? Зачем JSON с кучей лишнего текстах, если можно CSV с меньшим объемом текста?


Как по мне эту задачу можно было решить в разы эффективнее:

1. Использовать ScriptableObject для хранения данных.

2. Использовать синглетон для доступа к данным.

3. Написать скрипт редактора, который по команде из главного меню будет делать WWW запрос к GoogleDocs, получать нужный лист в формате CSV, и потом парсить его в указанный ScriptableObject.


Таким образом мы всю мароку о заполнении данных переносим из Runtime в Editor.

раскрыть ветку
5
Автор поста оценил этот комментарий

По таким урокам только врагов в лес заводить :) Думаю они будут не очень популярны.


Может кодите вы и хорошо, но учить и кодить это разные вещи.

раскрыть ветку
6
Автор поста оценил этот комментарий

обещались мего уроки по разработке,

- одну треть говорим о табличках в гугл доке,

- одну треть об элементарной сериализации из джонсона, о которой в интернете 1000 и 1 урок написаны уже.

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


2)Умение читать ОЧЕНЬ ОЧЕНЬ ОЧЕНЬ много текста

Ты его столько не пишешь, не льсти себе.

Чтобы так выебываться, твои посты должны быть похожи на это https://habrahabr.ru/post/243471/


Фиг поймешь на кого ориентированы эти "уроки".

"Вот это вы пиздуйте сами прочитайте и вникайте, а сейчас вам элементарное расскажу"


p.s. и да, реально удобно писать с таким стилем скобок? это же пиздец.

Тому кто это придумал в visual studio на с# ставить по умолчанию, нужно руки оторвать.

раскрыть ветку
1
Автор поста оценил этот комментарий

Имхо, конкретно в юнити совсем не всегда целесообразно создавать внешние таблицы данных.
Сериализация в инспекторе это достаточно мощная вещь.Можно создать "референс класс", который будет содержать описания всех нужных игровых обьектов и данных( в том числе, хранить ссылки на прототипы префабов для спавна), а затем брать все это оттуда как-то так:
GlobalReference.Instance.GetCharacterDesc(CharType _type); //CharType is enum

Из очевидных преимуществ:
1)быстро поменял прямо в редакторе, запустил, работает. Тормознутость сериалайзд классов, в принципе, можно решить путем каста в не сериалайд парент класс.(у меня, правда, не возникало таких проблем)
2) Не нужно ничего читать и загружать - юнити сама подгрузит все объекты указанные где-либо в инспекторе.
3) Защита от школьника. Клиенту будет чуть сложнее править конфиги и читить.

раскрыть ветку
1
Автор поста оценил этот комментарий
Раз это Статик дата, то зачем использовать геймобжект файнд? Мне кажется что синглтон будет удобнее использовать.
раскрыть ветку