Кот и код
563 subscribers
178 photos
31 videos
1 file
97 links
Про разработку на Unity и .Net, радость и боль программирования.
Автор: @KotikovD

Про искусство и игрострой:
@svolochandborshch
Download Telegram
Давайте мериться у кого больше.

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

Это была проблематика, а подвести я хочу к вопросу. Мне интересно узнать, сталкивались ли вы на своих проектах с оптимизацией загрузки. Интересно узнать о проектах любых масштабов, замерах и удачных решениях, которые улучшили показатели. Свой кейс тоже напишу в комментах к посту.
🤔3
Кто какие профильные каналы читает в ТГ? Предлагаю поделиться в комментариях. На тему разработки, игр и геймдева в целом. Большие или маленькие - не важно. Даже если у вас есть свой канал - тоже делитесь. Прикрепите ссылку и пару слов чем вам нравится.
Технический приём, который может сильно пригодиться на практике, но не только лишь каждый его вспомнит и применит. Покажу на примере. Допустим у нас шариться логика на сервере и клиенте. Там у нас есть класс инвентаря, он хранит Dictionary c ресурсами и количеством. Его мы не можем в чистом виде отдавать свойством, потому что это небезопасно. При этом данные из него нужны не только на сервере, но и на клиенте (например, инициализация UI ресурсов, заполнение окна инвентаря). Как быть? На самом деле все просто.

Наследование класса от IEnumerable даёт возможность бегать по классу в цикле. Обратите внимание, что в GetEnumerator отдаём копию.

class Inventory : IEnumerable<KeyValuePair<Resource, int>>
{
Dictionary<Resource, int> _inventory;

public IEnumerator<KeyValuePair<Resource, int>> GetEnumerator()
{
lock (_inventory)
return new Dictionary<Resource, int>(_inventory).GetEnumerator();
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}

Ещё один вариант, который не исключает, а дополняет первый приём - индексатор. С помощью него вы можете брать значение по ключу и обращаться к классу как к дикшарю. При этом можете сделать все обработки и безопасное обращение внутри проперти. Ещё индексатор можно декларировать в интерфейсе. Обращение к классу может выглядеть так: var amount = _Inventory[Resource.Gold];

class Inventory
{
Dictionary<Resource, int> _inventory;

public int this[Resource resource]
{
get
{
lock (_inventory)
{
if (_inventory.TryGetValue(resource, out int amount))
return amount;
return 0;
}
}

set
{
lock (_inventory)
_inventory[resource] = value;
}
}
}
#техничка
👍6
Жизненная тема. Как прокачиваетесь на работе? Есть ли у вас какие-то способы для этого?

Для меня одна из главных задач на работе помогать ребятам (и себе) развиваться. И вглубь и по софтам. Для этого постоянно приходиться что-то придумывать и балансировать между "надо в качество" и "спринт горит". Обычно все не просто: придумай, продай идею, внедри, оцени велью. Одни процессы приживаются хорошо, другие умирают почти сразу. Такой постоянный путь проб и ошибок фидбека.

У нас хорошие примеры того, что действует на полную:
- Процесс код ревью. Да как бы банально не звучало, но ключевое тут "процесс". Все задачи от определённой сложности после выполнения и до проверки попадают в состояние ревью. Её может посмотреть любой кодер с проекта. И так у каждого программиста есть обмен знаниями, обмен контекстом разных фичей и, конечно, меж командная коммуникация.

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

- Митапы. На постоянной основе, раз в неделю. Все клиентские отдела собираются и обсуждают широкий набор актуальных тем, рассчитанный либо на решение существующих проблем, либо на обучение: новинки и сложности текущих фичей, таски для технических спринтов, курсы, паттерны, че там в C# и в Unity. Темы подбираются заранее и если нужно подготавливаются кем-то из кодеров в виде презентаций. Иногда можем даже кого-то позвать из вне проекта.

Это не все что есть, но мои фавориты. Можно будет поговорить об этом побольше, если будет интересно. Сейчас же я работаю над расширением способов развития и коммуникации. Мне очень интересно узнать - как у вас, есть ли что-то подобное? Если нет, то хорошо вам или плохо от этого? Если есть, то чувствуете пользу? Какие ещё у вас есть события?
#работа
👍4🤔1
Контрразведка. Очень примечательный проект. Российская бесплатная синглплеер игра на мобилку с обучающей исторической составляющей. Симпатичная, много механик, созданная на деньги грантов. Искренне приглашаю скачать и ознакомиться - RuStore, Play Market и App Store. У меня же тут мои впечатления.

Минусы: Точки роста:
⁃ Смертельно кривое управление. Это то, с чем ты борешься всю игру. Тут даже сказать нечего, думаю просто не дотянули. На джойстике было бы идеально.
⁃ Иногда ты в ступоре потому что не знаешь куда идти, что делать. Мне напоминает старые игры, где игрока ещё не водили за руку. Так было на сеге или в старых квестах.
⁃ Левел дизайн. В самом начале игры ты упираешься в невидимую стену и выглядит этот именно как баг. Потом тоже упираешься куда попало, но уже знаешь как с этим быть.
⁃ Баги. Их много. Какие-то триггеры на уровнях не срабатывают и казалось бы все на этом - дедлок. Помогают кривые коллайдеры, можно их продавить и пройти дальше. Вынуждающих прекратить игру багов при этом нет.

Часть 2
#игры
4
Это вторая часть про Контрразведку. Часть 1
Порадовало:
⁃ Игра честно бесплатная и это сингл плеер на мобилку. Даже звучит странно. На сколько могу заметить, например, Apple Arcade себя позиционирует как стор с премиальными мобильными играми. И вот только там я видел подобные примеры.
⁃ Стильная графика. Хороший баланс проработки, деталей и выбранный визуальный стиль. Да, есть к чему прикопаться, но эмоции приятные, разглядывать хочется.
⁃ Хорошей анимации и их много. Хотя и многих переходов между состояниями нет, это не мешает.
⁃ Не напряжено вписали исторические сводки. Вообще этот момент очень напоминает мне 2 вещи. Я делал обучающий детский проект для музея на деньги гос. гранта. И там была специфика, что проект должен однозначно реализовывать тему заявки гранта. У меня там все внимание было на обучающих хинтах, фотках к ним и озвучке этого справочного материала. Тут тоже есть такой след. Второй проект, который приходит на ум, бесспорно крутой и известный - Чёрная книга. Про мифы и сказания Пермского края. Там все мифы вписаны в геймплей, вам реально надо разбираться в ста сортах чертей, чтобы решать игровые задания и влиять на сюжет. А народные песни излечивают главного героя. Это мои домыслы, но не удивлюсь, если среди инвестиций Чёрной книги тоже есть гранты.

Впечатления от контрразведки положительные, очень надеюсь, что они докрутят недочёты, но сильно сомневаюсь в этом. В целом вообще чудо, что такой проект вышел. Немного находясь в теме, знаю, что денег в грантах в притык и даже меньше. Буду следить за новостями.
#игры
👍3
Почему когда мы пишем [SerializeField] напротив поля List оно появляется в инспекторе, а когда напротив поля типа Dictionary инспектор нам его не рисует?

Если вкратце, то Unity ленивые жопы. Либо, как сказал мой друг, они планируют купить Odin, где это реализовали.

Суть в том, что Unity добавили стандартную сериализацию для всех базовых и встроенных типов, а также типов наследуемых от UnityEngine.Object. Плюс для любых (почти) классов, явно помеченных атрибутом [System.Serializable].

Так вот, List по сути это дженерик класс. А дженерики не сериализуются, то есть во вьюшке так писать нельзя:

public class MyView : MonoBehaviour
{
// Инспектор ничего не нарисует
[SerializeField] private MyClass<AnyType> _myClass;
}

Магия в том, что Unity написали персональный серилизатор для листа. А для дикшаря не написали. При настоящем и неподдельном желании можно написать свой сериализатор самостоятельно. А также есть способ добавить свой собственный класс, унаследованный от Dictionary или любого дженерика и выводить его в инспекторе. Суть в том, чтобы сразу определить тип, таким образом:

public class GenericClass<T>
{
public T Value;
}

[Serializable]
public class MyClass : GenericClass<int> { }

public class MyView : MonoBehaviour
{
// Будет в инспекторе
[SerializeField] private MyClass _myClass;
}

Для Dictionary тоже могу написать пример, если тема интересна. В целом проблема старая как Юнити и есть костыли для решения. Даже не знаю почему решил об этом рассказать, как будто просто пригорело в очередной раз. Может кто-то, знает причину почему у юнитистов руки до сих пор так и не дошли до этой фичи.
#техничка
👍5
Недавно делал пост про то, что мы ищем Юнити-девелоперов себе в компанию. И хочется немного подтолкнуть тех, кто может ещё, думает мне написать и не хватает решающего толчка чтобы это сделать.
Ищу я классного чела, не просто в компанию, а конкретно себе в проектную команду. Работать будем над мидкорным Idle RPG - MightyParty. Широкий стек технологий и масса пространства для творчества.
Кроме моего проекта, есть и другие. А также есть вариант присоединиться к межпроектной технической команде над созданием разных тулз и расширений. Это если вы любите копать в глубину и у вас айтишная бородатая душа.
Чтобы лучше донести, что нужно будет делать я лично переписал разделы в описании вакансии на hh "Чем предстоит заниматься" и "Круто, если ты". В общем ребят, пишите в ЛС, расскажу подробнее какие проекты.

PS: Реально старались сделать особое описание вакансии, чтобы передать общий вайб. Получилось?)
👍2
Чем отличается Composition Root от Service Locator?

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

Composition Root
Его назначение внедрить зависимости во все объекты, которые создаёт. В ручном режиме, в методе сборки систем есть определённый порядок инициализации. Сначала создаются системы, которые не имеют зависимостей, например, WebClient, InputSystem. Далее системы с зависимостью на первую группу, например, контроллер управления персонажем с зависимостью на InputSystem. Далее третья группа с зависимостями на первые две. И так далее.

Таким образом создаются все объекты игры, которые имеют все необходимые зависимости на этапе создания. Тоже самое но в автоматическом режиме и со своими наворотами делают DI-контейнеры, например, Zenject. Хранить эти объекты и предоставлять к ним доступ CR не обязан, но может.

Service Locator
Должен хранить в себе все сервисы, которые создаёт и отдавать их всем кто попросит. При регистрации своих сервисов локатор не обязан заниматься внедрением зависимостей, например, он может регистрировать созданные ранее объекты.

Таким образом вы в любом месте игры можете обратиться по статическому методу у локатора и попросить нужную службу. Именно по этому его называют анти-паттерном. Можете сказать чем он лучше синглтона? 😏

А по факту в игре вы можете увидеть гибрид, который отвечает признакам и того и другого. Но главный признак локатора - это метод, с помощью которого вы можете получить прямой доступ к какой-либо службе откуда угодно. Совсем кратко: правильный CR внедряет зависимость, SL предоставляет доступ.

Вот такие мысли в слух. Появились, когда тут на днях пошёл изучить новый проект. Если у вас есть свои истории и дополнения про эти паттерны - делитесь, интересно почитать.
#техничка
🔥4👍1
Наблюдал тут за перепиской в чате неигровых .NETчиков, при этом тема зашла про разработку игр. Очень порадовал один комментарий. При том видно, что автор коммента никак не хотел никого обидеть.
Когда Hogwarts Legacy и Atomic Heart борются за то, кого из них геймеры пройдут первым, я решил запустить BioShock.

В BioShock есть мини игра для взлома устройств (на скрине). Сама игра 2007 года. Мини игры обычно позволяют разбавить основной геймплей. И получается! Чувствуешь жёсткую скуку занимаясь взломом. Я попытался вспомнить примеры годных мини игр, но такое ощущение, что их не делают интересными в больших проектах.

Например, взлом замков в Thief. Но он тривиальный. Взлом устройств в Киберпанке - на мой вкус вообще неказистый. Хороший пример, пожалуй, Гвинт в Ведьмаке.

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

Интересно, почему для мини игр часто используют неинтересные механики. Хотелось бы вдохновиться качественной подборкой. Наподобие такого ролика про 100 игровых механик или вот такой сайт, где тоже разложена сотня механик. Может кто-то, может накинуть ещё подобных референсов.
#игры
Из разряда очень интересно, но бесполезно или не бесполезно. Кому как. Почему при переборе List<T> через foreach нельзя менять коллекцию?

Устроено это таким образом, что List хранит номер своей версии и при создании Enumerator'а в этот номер записывается в аналогичное поле в нем. И далее при вызове MoveNext(), происходят проверки этой версии каждый вызов. Таким образом, если версия не совпадает - вылетает знакомая ошибка "Collection was modified; enumeration operation may not execute."

Что может изменить версию листа? Её изменение, а точнее инкрементация происходит во время операций над листом: запись по индексу, изменение количества элементов в коллекции, изменения порядка элементов (сортировки).

Зачем это сделано? Здесь уже мои личные рассуждения. Например, при работе с for таких проблем у нас нет. Дело в том, что в цикле for мы самостоятельно можем контролировать текущий индекс и логику по которой будем бегать по списку. Например, сделать обратный цикл с конца к началу или зачем-то брать только чётные индексы. Кстати, foreach под капотом содержит тот же for. Но у нас нет к нему доступа. Можем только перебрать все элементы по порядку. Таким образом, изменение коллекции извне становится небезопасным и неуправляемым действием.

Если представить аналогию с автомобильной коробкой передач, то for - ручная, а foreach - автоматическая. И логично, что автоматическая устроена сложнее, но имеет защиту от ошибок использования.
#техничка
🔥3
Кто IAP настраивал? Напрягает проблема, что Unity не сетапит Google Licence Key через настройку в сервисах. Если попытаться отправить ключ будет сообщение "You are not authorized to set the license key". Ошибка на не зависит от пользователя, под которым я залогинен, даже для владельца она воспроизводится. На Юнити-форуме, данная проблема засветилась ещё в 2020. Предлагается альтернативный сетап через Dashboard, но в Юньке данное поле все равно останется пустым. А я не люблю неподконтрольные настройки, по ощущениям как будто играть в игрушку, к которой нет инструкции. (Спасибо нейронке за уместную аналогию для сравнения, лучше и не скажешь) В общем, кто-то в курсе на сколько это критично?
🤔2🍓1
Почта 1 день-min.gif
13.3 MB
Давно хотел показать наш проект. "Горюшко в след собакою" - это визуальная новелла с сложным и эмоциональным сюжетом. История основана на воспоминаниях главной героини, сохранённых в диктофонных записях, заметках и письмах. Игра ведёт повествование от лица девочки Нади, которая служит почтальонкой и вынуждена доставлять родным солдат вести с фронта. История повествует о надеждах, заботе и ценности семьи.

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

Мы, как разработчики игр, обладаем творческим потенциалом и в разных наших проектах можем сосредоточить его на разных задачах. Мы можем развлечь игрока, помочь убить время, "заставить" его платить, чему-то научить, что-то рассказать и многое другое. Этот проект является носителем исторической составляющей, хранит память о событиях Великой Отечественной Войны и повествует о жизни людей в тот период.
#девлог
👍7🔥6
Если бы вас кто-то спросил “Зачем нам заниматься поддержкой архитектуры в проекте?”, вы бы смогли чётко ответить сходу? Продолжаю неспеша читать Мартина и мне очень понравился абзац, где он кратко отвечает на этот вопрос. Думаю, всем пригодится для следующих серьёзных переговоров, где вы будете бороться за технические спринты.

"Главное предназначение архитектуры — поддержка жизненного цикла системы. Хорошая архитектура делает систему лёгкой в освоении, простой в разработке, сопровождении и развёртывании. Конечная её цель — минимизировать затраты на протяжении срока службы системы и максимизировать эффективность программиста."
#техничка
This media is not supported in your browser
VIEW IN TELEGRAM
У меня, как правило, все пет-проекты, это какой-то челендж, испытательный полигон для новых подходов, плагинов, технологий и так далее. Но в ГВС я могу позволить себе делать такие штуки)
#девлог
🔥5👍2🍌2