24

Туториалы для геймдева #1 Локализация и Класс Logic

Привет, Пикабу! Пока писал эту часть, понял что из разбивки которую сразу наметил этот пост получится слишком маленьким, решил объеденить две темы.


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


Рекомендуется прочтение предыдущего поста: https://pikabu.ru/story/tutorialyi_dlya_geymdeva_0_staticdat...

Logic


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


Сейчас объясню попонятнее.


Представьте что у вас есть БОЛЬШОЙ и глобальный класс, который хранит текущее состояние игры - (например этот класс потом уходит в сейв). У него есть параметр ЗОЛОТО.  При этом этот параметр изменяется подбором лута, магазином и т.д.


В таком случае в каждом классе вы пишете что-то вроде:


Logic.AddGold(50); \\использовать его можно также как и StaticData

Вопрос - А почему бы также не обращаться сразу к классу для сейва?


Имхо самый правильный ответ, это почему вместо:


public int a;


пишут:


public int a {get; set;};


Т.е. правильное управление доступом. В классе сейвов по хорошему должны быть только серриализуемые переменные.


Если вы не поняли что делает "public int a {get; set;};" и зачем оно может использоваться, то вот простое объяснение: Идеологически так сложилось.

И ещё пара маленьких плюсов от класса Logic.


1)Удобно управлять рандомом. Как всем известно настоящего рандома в играх нет. Logic позвляет аккамулировать все события которые могут использовать рандом и вы можете напрямую контролировать их порядок вызова.


Пример (простой, но высосаный из пальца):


У вас есть два коллайдера, происходет столкновение. У обоих есть onHit ивент.


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


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


2)Удобно хранить ивенты в одном классе. И точно также удобно вешать на них слушателей. Не надо бегать по 20 классам и смотреть, а убрал ли я всех слушателей, а везде ли ивент сработал  так далее.

Переходим к локализации.


Когда-нибудь вы соберётесь переводить игру на другие языки. Или сразу будете делать скажем на английском и русском. Вам потребуется как-то аккуратно хранить текст. Аккуратно - это значит в таблице) Создаём лист с названием Texts.

Собственно всё также как и в остальных листах. Разве что удобнее превращать не в список объектов кастомных классов, а в список словарей (DICTIONARY) например. А потом вытаскивать по названию языка, но это детали.


Помимо удобства использования это даёт нам удобство перевода. Исправление ошибок, муки выбора надписей.


\\Мы как-то раз недел жонглировали именами ACCEPT, ACCEPT JOB, OK. Представьте какой ад был бы если бы надо было лезть в редактор и везде править названия, а тут геймдизу даже не надо пинать программистов. Сел и поменял поле в таблице.


А также с таблицей могут работать переводчики! Естественно вы не пустите их в код, а вот перевести строки таблицы они вполне способны.


Также бонусом идёт то, что Unity UI (и UI Widget в UE4) поддерживают Rich Text, а значит мы можете сделать шрифты жирными или цветыми прямо в таблице.

Итак, как с этим работать.

Ну собственно аналогично Logic и StaticData. Заметьте, что мы не передаём язык, о текущем языке всё знает сам Localizer. Он уже выдаст нам текущую строку.


На объекте с текстом висит вот скрипт TextLocalizer, в который мы передаём какие-то значения и он уже сам решает как их заполнить. Например обратите внимание на #VAL0 в строке с золотом. Вот как я с ними работаю:

Передали список значений, и они сами обновились.


Главное не забудтье в редакторе прописать публичное поле KeyName нужным вам ключом)

Вопрос, а как инициализировать стартовые значения всяких кнопок ОК, EXIT и т.д.


Элементарно,Ватсон!


Вешаем на текстовый объект наш TextLocalizer. Прописываем ему соответствущй ключ. Он при старте вызывает SetValues(null); И текс записался. Всё)

Ну и последняя заметка - Смена языка. Есть 2 способа, простой и сложный.


Простой: При старте игры игрок выбирает язык, он устанавливается в TextLocalizer и дальше работает всё вышеописанное. После игрок язык уже не может поменять.


Сложный: в Logic создаётся ивент смены языка, и его слушает TextLocalizer, меняет язык в себе и вызывает ивент обновления текстов на интерфейсах. Интерфейсы этот ивент слушают и вызывают SetValues от последних запоменных значений.


Надеюсь это поможет кому-то с архитектурой проекта) Спрашивайте что не понятно) Остался ещё 1 пост наверное)

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

ЗАПРЕЩЕНО:

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

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

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


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

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

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

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

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

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

Блять. Да кто с вами спорит, конечно удобно, поэтому все так и делают. Это стандартная реализация.

Я вам вам вещаю за то, что ваши уникальные решения, нихуя не уникальные. И об этом учат и так с самого начала.

Для новичков ваши уроки нихуя не понятны. Для знающих хоть что-то, они нахуй не нужны.

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

Собственно я нигде и не говорил что это уникальные решения, даже напротив.

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

Интересненько.

И сколько раз за игровую сессию у вас игроки меняют язык интерфейса?) Наверное часто, раз вы так на счет этого заморочились.

Когда вызывается функция смены языка, то она от этих значений обновляется (от последних запомненных)

Меняете язык и пробегаете по всем кнопкам и обновляете их отдельной функцией? Типа button.updateTextAfterLangChange()? Потому как вашей функцией set это сделать не получится.


А почему вы не используете актуальные значения из вашей логики?

Есть у вас например, кнопка "Продать 200 хуев ежей"

пишите что-то типа


ваши стринги которые не важно где лежат
string TEXT_SELL_BUTTON = " Продать {0} {1}"
string TEXT_HUI_EZHEI = "ХУЕВ ЕЖЕЙ"

public void updateButtonSellItem() {
buttonSell.text = String.Format(Localize.get(TEXT_SELL_BUTTON), logic.getCountSellItem(), logic.getNameSellItem());
}
где logic.getCountSellItem() - возвращает количество объектов .
где logic.getNameSellItem() - возвращает название, в нашем случае Localize.get(TEXT_HUI_EZHEI ).

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

У вас же какая-то немасштабируемая хуета, а не код.

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

Да не часто. Собственно уже я это и не использую, как раз по этой причне. Только не бегал по всем кнопкам а слушателя повесил.

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

Блять. Да кто с вами спорит, конечно удобно, поэтому все так и делают. Это стандартная реализация.

Я вам вам вещаю за то, что ваши уникальные решения, нихуя не уникальные. И об этом учат и так с самого начала.

Для новичков ваши уроки нихуя не понятны. Для знающих хоть что-то, они нахуй не нужны.

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

Да, стандартная. Но многие даже об этом не знают. Для того и туториал.

1
Автор поста оценил этот комментарий
Вы каждый раз вбиваете его в поле когда создаёте кнопку?

А у вас можно подумать при создании кнопки текст силой мысли записывается)

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

И вся заслуга ваша в том, что в переменных вы храните не сам текст, а тэг текста.

И того вы сделали самую стандартную реализацию локализации программного продукта.

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

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

Записывать тег намного удобнее чем сам текст. Потому что тег НЕ меняется в редакторе после создания, а текст можете хоть обменятся. Причём не надо даже движок запускать.

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

ну так как вы lastValue используете то? Сколько вас пытать то можно тогда?)

раскрыть ветку (1)
0
Автор поста оценил этот комментарий
Когда вызывается функция смены языка, то она от этих значений обновляется (от последних запомненных)
показать ответы
1
Автор поста оценил этот комментарий

"доступный туториал не привязанный к движку"

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

"компонентом текста" оказался в итоге объект ui движка для отображения текста на экране.

Прикольно что автор очень сильно боится показывать код. И все объяснение происходит в виде, "вот есть у меня класс мной же написанный, и он работает".

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

ну если вы пишете так,

button1.text = "ok"

button2.text = "ok"

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

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

ну, можете то кусочек кода показать? Мне просто интересно что вы подразумеваете под "компонент текста", и как используете переменную "lastValue" в дальнейшем.

Насколько я понимаю, вы сделали по классу объекту на каждую надпись?

Просто так как я себе это вообще представляю, по этим гайдам, получается вообще дичайщая жопа. 

раскрыть ветку (1)
0
Автор поста оценил этот комментарий
Нет, так не присваивается. Как по вашему пишется текст на кнопке? Вы каждый раз вбиваете его в поле когда создаёте кнопку?
показать ответы
1
Автор поста оценил этот комментарий

"доступный туториал не привязанный к движку"

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

"компонентом текста" оказался в итоге объект ui движка для отображения текста на экране.

Прикольно что автор очень сильно боится показывать код. И все объяснение происходит в виде, "вот есть у меня класс мной же написанный, и он работает".

раскрыть ветку (1)
0
Автор поста оценил этот комментарий
Поэтому я и не писал Unity UI text. В Unreal это будет UI widget, и там точно также можно повесить блюпринт со слушателем на него
0
Автор поста оценил этот комментарий

Не ну это нормально, если я правильно вас понял.

Тут еще прикол в том, что Localize в статье, это походу какая-то библиотека юнити, которую автор еще и использует по не стандартному. И пытается на своем примере кого-то научить)


Я когда на андроид переехал помню, очень понравилась реализация многоязычности в приложениях. Хранишь себе стринги в хml "string", хочешь русский - делаешь новый файл "string_RU", хочешь английский "string_EN". А тягаешь строки при этом одним единственным способом, через R.string.textName. И если нет предпочтений к языку, адроид сам проверяет язык системы, и ищет нужный файл, нет если, использует дефолтный. Охуенно же.


Еще автор конечно странно строки преобразует, нет чтобы делать что-то похожее на

String text = "Gold %d coins"
public string getText(double arg) {
return String.format(text, arg)
}

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

раскрыть ветку (1)
0
Автор поста оценил этот комментарий
Нет не библиотека. Вы не поняли. Это кастомный класс, такой же как и StaticData из предыдущего поста. С примерно тем же функционалом. Просто разделил чтобы не путать
8
Автор поста оценил этот комментарий

Что мы ожидали

Я анонсирую серию постов "Настоящие туториалы для геймдева".

Что мы получили

Этот пост НЕ претендует на Каноничность, Идеальность кода и архтектуры. Тут могут быть рассказаны не самые аккуратные и эффективные решения.
Представьте что у вас есть БОЛЬШОЙ и глобальный класс

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


Если вы не поняли что делает "public int a {get; set;};" и зачем оно может использоваться, то вот простое объяснение: Идеологически так сложилось.

Охуенное объяснение для туториала, я считаю. Стоило бы тогда хотя бы сказать, что подобная запись актуально наверное только для C#, а в другом языке каком-нить написав такое, компилятор отправит вас нахуй.


Мы как-то раз недел жонглировали именами ACCEPT, ACCEPT JOB, OK. Представьте какой ад был бы если бы надо было лезть в редактор и везде править названия

Не было бы ничего, точно так-же меняется 1 строка в коде, не нужно ничего придумывать. Если же вы конечно не хардкодите там все что попал.


На объекте с текстом висит вот скрипт

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


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

раскрыть ветку (1)
Автор поста оценил этот комментарий
Нет, такой объект я создавать не предлагаю.

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

Обьект с компонентом текста, вы же его хотите отобразить. Сохраняется для возможности смены языка и переинициализации текста.
показать ответы
8
Автор поста оценил этот комментарий

Что мы ожидали

Я анонсирую серию постов "Настоящие туториалы для геймдева".

Что мы получили

Этот пост НЕ претендует на Каноничность, Идеальность кода и архтектуры. Тут могут быть рассказаны не самые аккуратные и эффективные решения.
Представьте что у вас есть БОЛЬШОЙ и глобальный класс

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


Если вы не поняли что делает "public int a {get; set;};" и зачем оно может использоваться, то вот простое объяснение: Идеологически так сложилось.

Охуенное объяснение для туториала, я считаю. Стоило бы тогда хотя бы сказать, что подобная запись актуально наверное только для C#, а в другом языке каком-нить написав такое, компилятор отправит вас нахуй.


Мы как-то раз недел жонглировали именами ACCEPT, ACCEPT JOB, OK. Представьте какой ад был бы если бы надо было лезть в редактор и везде править названия

Не было бы ничего, точно так-же меняется 1 строка в коде, не нужно ничего придумывать. Если же вы конечно не хардкодите там все что попал.


На объекте с текстом висит вот скрипт

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


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

раскрыть ветку (1)
Автор поста оценил этот комментарий
Эти туториалы помогут тем кто хочет делать более аккуратную архитектуру, людей которые делают архитектуру хуже чем моя, очень много.

Да, не претендует на каноничность. Да можно сделать лучше. Но это получится сложнее.

Темы

Политика

Теги

Популярные авторы

Сообщества

18+

Теги

Популярные авторы

Сообщества

Игры

Теги

Популярные авторы

Сообщества

Юмор

Теги

Популярные авторы

Сообщества

Отношения

Теги

Популярные авторы

Сообщества

Здоровье

Теги

Популярные авторы

Сообщества

Путешествия

Теги

Популярные авторы

Сообщества

Спорт

Теги

Популярные авторы

Сообщества

Хобби

Теги

Популярные авторы

Сообщества

Сервис

Теги

Популярные авторы

Сообщества

Природа

Теги

Популярные авторы

Сообщества

Бизнес

Теги

Популярные авторы

Сообщества

Транспорт

Теги

Популярные авторы

Сообщества

Общение

Теги

Популярные авторы

Сообщества

Юриспруденция

Теги

Популярные авторы

Сообщества

Наука

Теги

Популярные авторы

Сообщества

IT

Теги

Популярные авторы

Сообщества

Животные

Теги

Популярные авторы

Сообщества

Кино и сериалы

Теги

Популярные авторы

Сообщества

Экономика

Теги

Популярные авторы

Сообщества

Кулинария

Теги

Популярные авторы

Сообщества

История

Теги

Популярные авторы

Сообщества

Недвижимость и ремонт

Теги

Популярные авторы

Сообщества