TyapLyapDev - Разработка игр | Движуха Сакутина
225 subscribers
127 photos
77 videos
13 files
26 links
Download Telegram
Начинаете делать игру, но вскоре забрасываете её на стадии прототипа? Без паники! Возможно вам просто нравится кодить, а как только архитектура становится понятной и остаётся только рутина, вам становится скучно и вы ищете что-то новое.

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

А если не знаете, где черпать мотивацию, попробуйте просто найти единомышленников.
Увидел, что делает одна из команд в этой группе https://t.me/IndieGameMill и захотелось повторить с нуля с небольшими доработками. Такое вот получилось. Билд можно скачать и потрогать, архив прикрепляю.
🔥1
TyapLyapDev - Разработка игр | Движуха Сакутина
Photo
Уже сутки дерусь с пользовательским редактором юнити, чтобы сделать эту систему ещё удобнее, настраиваемой в специальном окне. Думаю, это станет полноценной утилитой, чтобы легко настраивать и не лезть в код. Сначала меня пугала работа в редакторе. Сейчас тоже пугает, но меньше, т.к. я начал раскидывать логику по ответственностям и дебажить, расширять, модифицировать становится проще.

Такой вот совет: если перед вами страшный код, раскидайте его по скриптам и работайте с кусочками.
🔥2
Media is too big
VIEW IN TELEGRAM
Показыватель иконок в иерархии сцены 2.0! Теперь достаточно открыть окно, добавить элемент в список и выбрать там иконку и компонент, который висит на объектах для отрисовки. Чем это удобно? Ну, вы можете подсветить в иерархии сцены все объекты, на которые вашим коллегам по разработке надо обратить особое внимание. Я выделял объекты с дорожными модельками для человека, который собирал уровень. Оказалось очень даже удобно. Не тестил на разных версиях юньки, поэтому пишите, если найдёте баги 🙂
🔥4
TyapLyapDev - Разработка игр | Движуха Сакутина
Video
Какое было решение для снижения нагрузки: каждый кубик - объект, который внутри себя содержит триггер-коллайдер в виде сферы, чисто для плавности и саму модельку куба, которая выключена. Позиция триггера никогда не меняется, а вот позиция модельки при деактивации (и на старте) присваивает случайное сферическое отклонение и случайную дистанцию. При активации моделька получает ссылку на цель (игрока) появляется по направлению от цели на эту самую рандомную дистанцию, плюс рандомное отклонение и вращение. Чем меньше дистанция до цели, тем ближе моделька к нулевым координатам позиции и вращению.

Теперь игрок. У него есть детектор в виде сферы, который активирует кубы при входе в триггер и деактивирует при выходе. Физика настроена так, что детектор видит только кубы, а кубы детектор. Все остальные слои игнорируются.

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

В целом я постарался в оптимизацию, поэтому всё работает без лагов, сколько бы кубов на сцене не находилось.
🔥41
Сделай первую игру с нуля и получи грант на 1 000 000 рублей
Привет! Я веду этот канал как участник творческой мастерской Романа Сакутина. Наша цель создавать творческие игры и радовать игроков.
Становись разработчиком игр вместе со мной здесь - https://ijunior.ru/workshop-gamedev?utm_source=telegram_workshops&utm_medium=pinned_post&utm_campaign=tyaplyapdev
😁4
TyapLyapDev - Разработка игр | Движуха Сакутина pinned «Сделай первую игру с нуля и получи грант на 1 000 000 рублей Привет! Я веду этот канал как участник творческой мастерской Романа Сакутина. Наша цель создавать творческие игры и радовать игроков. Становись разработчиком игр вместе со мной здесь - https:/…»
Media is too big
VIEW IN TELEGRAM
Языковая локализация игры.

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

Самый простой, понятный и эффективный способ - накидывать на объекты с текстовыми компонентами кастомный скрипт, в котором есть несколько полей для записи. Вся логика уложилась в 3 скрипта, в которых суммарно 132 строчки кода - это уже с удобным переключением.

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

Пишите замечания, задавайте вопросы, с радостью пообщаюсь 🙂
🔥4👍2
Зачем кешировать transform?

Кеширование трансформа в Unity (например, _transform = transform) делают для повышения производительности. Вот основные причины:

1. Избежание повторных вызовов геттера
Свойство transform в Unity не просто возвращает поле - оно вызывает внутренний метод GetComponent<Transform>(), который ищет компонент в GameObject. Это требует вычислительных затрат.

Кеширование сохраняет ссылку на трансформ один раз (например, в Awake()), избегая повторного поиска.

2. Сокращение накладных расходов
В циклах (например, Update()) частые обращения к transform без кеширования могут снижать FPS, особенно на мобильных устройствах или при большом количестве объектов.

Пример без кеширования:
void Update() 
{
// Каждый раз вызывается геттер transform
transform.Translate(1, 0, 0);
transform.Rotate(0, 1, 0);
}


Пример с кешированием:
private Transform _transform;

void Awake()
{
_transform = transform; // Сохраняем ссылку один раз
}

void Update()
{
_transform.Translate(1, 0, 0); // Работаем с кешированной ссылкой
_transform.Rotate(0, 1, 0);
}


Важно!
Современные версии Unity (особенно с 2018+) оптимизировали геттер transform, и разница в производительности стала менее заметной. Однако для критичных к производительности проектов кеширование всё ещё рекомендуется.

Всегда кешируйте компоненты в Awake() или Start(), а не в Update(), чтобы избежать лишних операций.

Что ещё кешируют?
Другие компоненты: Rigidbody, Renderer, Canvas и т.д.
Ссылки на игровые объекты, если они используются часто.

Таким образом, кеширование — это простая и эффективная практика для оптимизации кода в Unity.
👍42🔥2
Хочу поделиться с вами своими соображениями по поводу комментариев в коде. Мне кажется это плохой практикой и вот почему:
1. Код желательно писать так, чтобы он сам говорил за себя. Для этого достаточно придумывать понятные названия для классов, полей и методов, а так же раскидывать логику по ответственностям.
2. Если в коде и комментариях одно и то же, то какой смысл в этих комментариях?!
3. Если вы изменили логику в коде, но не поправили пояснения в комментариях, то это скорее запутает, чем поможет.

Таким образом комментирование - скорее бесполезная штука и двойная работа.

Но не будем голословными. Для примера я качнул из ассет стора Starter Assets - ThirdPersonController .

Давайте посмотрим кусочки:

// cinemachine
private float _cinemachineTargetYaw;
private float _cinemachineTargetPitch;

// player
private float _speed;
private float _animationBlend;
private float _targetRotation = 0.0f;
private float _rotationVelocity;
private float _verticalVelocity;
private float _terminalVelocity = 53.0f;

// timeout deltatime
private float _jumpTimeoutDelta;
private float _fallTimeoutDelta;

// animation IDs
private int _animIDSpeed;
private int _animIDGrounded;
private int _animIDJump;
private int _animIDFreeFall;
private int _animIDMotionSpeed;


Вообще в этом классе 392 строки и он занимается сразу всем. Если раскидать по скриптам, то внезапно окажется, что поля с комментарием // cinemachine попадут в один класс, // player в другой класс и т.д. Это означает, что сама архитектура снимет с вас необходимость группировать поля таким способом. Но допустим, это прототип и не страшно, что здесь всего напихано. Смотрим ещё раз:

// cinemachine
private float _cinemachineTargetYaw;
private float _cinemachineTargetPitch;

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

Смотрим дальше:
private void Awake()
{
// get a reference to our main camera
if (_mainCamera == null)
{
_mainCamera = GameObject.FindGameObjectWithTag("MainCamera");
}
}

В комментарии говорится "// получите ссылку на нашу основную камеру". А в коде что? А вот: если ссылка на Камеру равна null , получить ссылку по тегу "MainCamera" и присоить её _mainCamera

Пока закроем глаза на использование тегов, но чем сам код здесь объясняет хуже комментария?

Дальше:
        private void Start()
{
...

// reset our timeouts on start
_jumpTimeoutDelta = JumpTimeout;
_fallTimeoutDelta = FallTimeout;
}


// reset our timeouts on start - // сбросьте наши тайм-ауты при запуске. А что мешает сделать метод:
private void ResetTimeouts()
{
_jumpTimeoutDelta = JumpTimeout;
_fallTimeoutDelta = FallTimeout;
}


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

Дальше просто поскидываю такие кусочки кода. Посмотрите, как это комично выглядит:
// set sphere position, with offset
Vector3 spherePosition = new Vector3(transform.position.x, transform.position.y - GroundedOffset,
transform.position.z);

// clamp our rotations so our values are limited 360 degrees
_cinemachineTargetYaw = ClampAngle(_cinemachineTargetYaw, float.MinValue, float.MaxValue);
_cinemachineTargetPitch = ClampAngle(_cinemachineTargetPitch, BottomClamp, TopClamp);

// Cinemachine will follow this target
CinemachineCameraTarget.transform.rotation = Quaternion.Euler(_cinemachineTargetPitch + CameraAngleOverride, _cinemachineTargetYaw, 0.0f);

// set target speed based on move speed, sprint speed and if sprint is pressed
float targetSpeed = _input.sprint ? SprintSpeed : MoveSpeed;

// reset the fall timeout timer
_fallTimeoutDelta = FallTimeout;
3👍1🔥1
Forwarded from Soulshift | ALPHA
🔥 Привет, герои Emberfall!

Рады сообщить, что регистрация и авторизация в игре работают на 100%! Теперь вы можете:

- Создать аккаунт: нажмите SIGN UP, придумайте уникальный Nickname и пароль
- Войти в игру: нажмите LOGIN или отметьте «Remember me» для быстрого доступа
- Восстановить доступ: нажмите Forgot Password? и получите ссылку для сброса пароля

Готовьтесь к новому приключению: собирайте отряды героев, прокачивайте умения и побеждайте в эпических сражениях!

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

Вперед, навстречу легендам Emberfall! ⚔️
🔥21👍1
Предположим мы меняем позицию и вращение объекта, например:
private void Update()
{
_transform.position = _position;
_transform.rotation = _quaternion;
}


IDE -шка выдаст вот такое сообщение: "Assigning position and rotation sequentially could be optimized." ("Можно оптимизировать последовательное назначение положения и поворота").

Послушаемся и применим рекомендации, получится следующее:
private void Update()
{
_transform.SetPositionAndRotation(_position, _quaternion);
}

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

В этом случае без предъяв. Но когда вы пишите собственные методы, избегайте "и" в их названии. Если в голову приходит назвать через "And", то скорее всего это 2 разных метода и логику надо на них разделить. Например, CalculateAndShoot() - тут одна часть вообще математическая, которую иногда даже нет необходимости хранить конкретно в этом классе, а вторая отвечает за стрельбу. Или, например, PlaySoundAndAnimate() - сомнительное название. А скорее всего они вообще окажутся в разных классах и встретятся лишь в каком-нибудь методе типа OnPressJump(), который подписан на событие _input.JumpPressed += OnPressJump.

Такая вот инфа.
Интересно ли вам углубиться в код-стайл?
🔥 - да
💩 - нет
🔥10