sundwell.dev | Хроники инди-разработки – Telegram
sundwell.dev | Хроники инди-разработки
693 subscribers
224 photos
105 videos
136 links
Всё про инди от реального человека - девлоги, процесс маркетинга, геймдизайн, разработка и пиксель-арт
Download Telegram
День 82

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

— Начало математических извращений —

Что я понял вообще - что lerp это плавный путь к достижению цели НО без достижения самой цели, то есть у нас есть функция, которая вызывается каждый кадр (будь-то 10, 60 или 300 кадров в секунду) это все работает идеально именно из-за этого "НО" и просчитывается все практически пропорционально, например:

У нас есть delta - время, которое прошло с момента предыдущего кадра, если FPS 60 - то каждый кадр delta = секунда / кол-во кадров = 1 / 60 = 0.0167, если бы кадров было 2 в секунду - то delta = 1 / 2 = 0.5, что делает игру независимой от кол-ва кадров (как-то расскажу об этом тоже), ну щас о другом

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

Максимальная скорость закрывания двери - 60км/ч (нормальный пинок кста), назовём эту скорость DOOR_SPEED, и каждую секунду дверь замедляется наполовину от текущей скорости, т.е.:
1 секунда - 60км/ч
2 секунда - 30км/ч
3 секунда - 15км/ч
...и так далее, и она никогда не будет равна нулю (странная дверь, закрываться не хочет и останавливаться тоже)

Как нам воссоздать такую же логику в нашей игре? На помощь приходит та самая линейная интерполяция - linear_interpolation или сокращенно lerp. Функция описывается так: lerp(начальное_значение, целевое_значение, коэффициент_увеличения)

Что у нас означает замедление - банально уменьшение скорости от 60 до 0, вот формула lerp для такого:
lerp(текущая_скорость_двери, 0, 1 - (delta * 30) = lerp(текущая_скорость_двери, 0, 1 - (0.0167 * 30) = lerp(текущая_скорость_двери, 0, 0.5) => это значит, что каждый кадр скорость двери будет уменьшаться наполовину от текущей скорости, что в свою очередь значит.. что дверь за долю секунды практически закроется с бешеной скоростью..

У нас 60 кадров в секунду в игре
1 кадр - 60км/ч
2 кадр - 30км/ч
3 кадр - 15км/ч
...
То есть буквально за 7-8 кадров (это почти десятая часть секунды) дверь уже будет закрыта, но нам же надо закрывать дверь плавно, а не быстро, каждую секунду надо, а не кадр

Я упущу расчеты коэффициента.. для уменьшения вдвое каждую секунду он должен быть 0.6931
lerp(текущая_скорость_двери, 0, 1 - (delta * 0.6931) => lerp(текущая_скорость_двери, 0, 1 - (0.0167 * 0.6931) => lerp(текущая_скорость_двери, 0, 0.0116

То есть каждый кадр при 60 кадрах в секунду у нас текущая скорость двери приближается к желаемой со скоростью 1.16% за кадр (т.к. 1 = 100%, 0.0167 = 1.16%). В таком случае каждую секунду у нас будет уменьшаться скорость двери примерно вдвое
0 кадр - 60.0000
1 кадр - 59.3040
2 кадр - 58.6161
3 кадр - 57.9241
4 кадр - 57.3282
...
60 кадр - 30.0000
...
120 кадр - 15.0000

Вообще лучше это делать через экспоненциальную интерполяцию, типа через e (2.71828), это число используется для плавного затухания/сглаживания, но это уже другая история (я еще сам не до конца догоняю)

— Конец математических извращений —

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

Еще улучшил стейт-машину - я раньше явно передавал во все начальные состояния delta, даже туда, где оно не надо было, я задал про это вопрос преподу и он ответил, что можно использовать get_physics_process_delta_time функцию и, грубо говоря, это та же delta с того же кадра, но доступ можно получить отовсюду и работает оно точно так же, что написано в документации + препод подтвердил

Рандом спавн еще не сделал - но завтра доделаю, логику уже понял

Сегодня занимался обустройством рабочего места, а сейчас монтирую видео об этом, поучиться особо не получилось, зато будет интересный (наверное) контент из жизни, не просто же читать посты от непонятного эфемерного лица

#godot #ritkarampage #разработка
🔥3
День 83-84

Двойной пост (не в новинку) - догонять то надо

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

У меня проблема в комнате - есть жуткая реверберация в углу (это когда в конце каждого звука такой гул продолговатый), а место моё в углу значит тут я рабочее пространство себе и "назначил", да вот только в этом же пустом углу у меня ужасная акустика, этот гул напрягает очень сильно и ну прям мешает жить. Вот можете глянуть пример реверберации тут - https://youtu.be/cGBn7sU6m3k, у меня Small Room тип ревёрба, а хочеться простого, человеческого Clean Loop (чистый звук)

Мне очень хотелось сделать видео как можно интереснее, динамичнее, смешнее и в общем прикольное, так что я запарился с монтажем видео, прикреплю два видео к посту - одно это превьюшка, второе это более полное видео со всем процессом, а в комментариях закину видос, где я 2 минуты разговариваю ни о чем жалуюсь на реверберацию и показываю своё рабочее место

#life
1🔥1😁1
Хотели бы вы время от времени видеть видео из моей повседневной жизни, а не только о разработке игр?
Anonymous Poll
77%
Да, это интересно (но в меру)
23%
Предпочитаю видеть только разработку
День 85

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

Уровень сложности измеряется.. "уровнями", начинается все с "0" и каждые 5 секунд увеличивается на единицу, то есть каждую минуту уровень сложности повышается на 12

Вот как у препода сейчас это все сделано в EnemyManager (скрипт, который отвечает за спавн врагов и все что с этим связано):
1. Делаем ресурс "Enemy" у которого есть weight и enemy_scene, про вес уже рассказывал, а сцена - это просто враг, который будет спавниться
2. В EnemyManager мы ставим всех начальных врагов, описывая их вес
3. Когда сложность повышается до условных 20 единиц - добавляем в список врагов нового врага

У меня же будет совсем иначе, сейчас попытаюсь объяснить (очень доволен этой простой, но гибкой системой)

Главные переменные, отвечающие за логику:
1. TTS - Time To Spawn / Время для спавна врага в секундах. Если значение 1.0s - каждую секунду будет спавниться враг, если же значение 0.5 - каждые пол секунды
2. Ресурс "Enemy", который будет иметь следующие настройки:
2.1 enemy_scene - сам враг, которого мы будем спавнить
2.2 weight - вес врага
2.3 count - кол-во врагов, которое будет спавниться за раз (полезно для мелких врагов, типа Bird Swarm, которые будут группами спавниться)
3. И, самое главное - ресурс "DifficultyLevel". Он будет вместе собирать пункт 1 и 2 выше и потом использоваться в EnemyManager, вот что в нём будет:
3.1 enemy_resources- список настроек врагов (их вес, кол-во спавна и сами объекты)
3.2 difficulty_level - сложность, при которой будут применяться эти настройки
3.3 time_to_spawn - TTS из первого пункта

А в самом EnemyManager у меня будет список из гибко настраиваемых ресурсов "DifficultyLevel", в редакторе это нереально удобно делается (не надо создавать каждый раз новое - просто прям в редакторе добавляешь/редактируешь/удаляешь сколько хочешь), и будет проверка на DifficultyLevel, который должен применяться на данный момент

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

Прикреплю скрины как я это вижу (визуализировал же) для Meadow (Луг) локации

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

Забыл поделится инструментом, в котором "визуализирую" - Excalidraw (вот ссылка чтобы скопировать текущее состояние моего проектика)

#godot #ritkarampage #разработка
👍1
День 86

Прогрессивная сложность - готово ✔️

Теперь можно гибко настраивать повышение сложности, как я описывал в предыдущем посте. Реализовал всё, кроме "spawn_count", враги все еще спавнятся только по одному, но когда я добавлю птичек в игру, то займусь этим "spawn_count" тоже, но сейчас все работает как часы (кроме места спавна - за ареной все еще появляются враги 😢

Я настолько довизуализировался, что понял - описывать что-то общее, накидывать логику и всякое такое простенькое это вот реально офигенно рисовать в каком-то блокнотике или в том же Excalidraw, да вот только когда я подошел к проработке таблиц.. вручную это нереально делать, так что я потратил добрых пару часов на изучение основ Excel (вообще Google Spreadsheets, но не столь важно) и сделал документ для построения уровней сложности - я прям очень горжусь тем, что я сделал

Вот ссылка на таблицу (будет обновлятся, я им пользуюсь) - https://docs.google.com/spreadsheets/d/1h0yJn5iCdGbwNw9AluOc30mQPv30KUZ34NJrTOjpJzE/edit?usp=sharing, она ридонли, так что просто скопируйте её и можете редактировать и поиграться с ней

Что я вообще сделал в таблице:
1. Автоматический рассчет Difficulty start time, Spawn Chance и Total Difficulty Level Weight, где:
1.1 Difficulty start time - время начала сложности в секундах => Difficulty Level * 5 (каждые 5 секунд сложность увеличивается на 1)
1.2 Spawn Chance - шанс спавна врага в процентах => Enemy Weight / Total Difficulty Level Weight
1.3 Total Difficulty Level Weight - общий вес всех врагов в пределах этой сложности => СУММА(Enemy Weight ГДЕ Difficulty Level ОДИНАКОВЫЙ)

Честно сказать - после добавления этой прогрессивной сложности и накатывания пары уровней той самой сложности игра.. стала играться 😶

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

Прикрепил пару видео - первое это короткое превью с результатом (хоть и мемное), второе более полное со всем процессом разработки системы сложности

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

#godot #ritkarampage #разработка
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2
День 87

Поднятие апельсинов (aka Orange Energy) было плоским, так что сегодня я очень даже хорошо и детально разобрался с Tween, как и где их использовать (т.к. есть AnimationPlayer, но он не всегда подходит)

Поднятие опыта я заанимировал и теперь апельсины немного отлетают в противоположную сторону от персонажа, а потом летят к нему и в этом время крутятся, а на полпути начинают резко уменьшаться в размере, а при подборе опыта появляются визуальные эффекты "разлетающихся апельсинов"

В конце я как-то более 3х минут плейтестил (играл) и.. макбук видимо не вывез и годот умер, видео отчет прикрепляю 👀

Сейчас занимаюсь добавлением летающих чисел урона, а завтра уже буду заниматься или улучшением "улучшений" или же визуальной составляющей (чтобы менюшки и карточки были красивыми)

Костяк игры уже на 90% готов - осталось только добавить сохранения и мета-прогрессию и, можно сказать, будет полноценная игра (ну, "полноценная" 🥲)

Три видео в посте:
1. Короткое превью результата
2. Видос где все залагало и умерло (конец видео постанова есчо (нет))
3. Полный прогресс анимации подбора апельсинов

#godot #ritkarampage #разработка
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
Я вот уже грубо говоря три месяца пишу посты, они почти все прям длиннющие, и, фактически, не очень-то и насыщенные (по большому счету это мои мысли)

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

Если много кому нравятся ежедневные посты - в теории могу создать отдельный канал "sundwell.dev | Девлог" и туда постить посты больше про техническую часть и каждодневные апдейты, как и раньше

P.S. Если будет что-то интересное, то буду делать небольшой пост об этом тоже, так что не будет "недельного" затишья перед бурей постом 👀
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
This media is not supported in your browser
VIEW IN TELEGRAM
День 88

Летающие числа - это классно 1️⃣

Вчера занимался настройкой темы для проекта и летающими числами (урона и не только)

Пока что в моей мега уникальной теме только шрифт 👀

А насчет чисел поинтереснее, препод напрямую создавал числа в компоненте HurtboxComponent, но мне показалось, что это не совсем корректно, ведь Hurtbox отвечает за получение урона, а не за отображение чисел 🤔

Создал два компонента - первый с коротким названием FloatingTextSpawnerComponent и второй с еще более коротким названием HealthChangedTextSpawnerComponent

FloatingTextSpawnerComponent отвечает за спавн чисел, в него передаю сам текст и тип текста - "урон | лечение | поднятие апельсина". Этот компонент я напрямую добавил к сцене апельсина и вызываю spawn_text("+1", FloatingText.Type.ORANGE_PICK_UP) при поднятии последнего

А вот чтобы показывать изменение жизней (урон или лечение) - мне надо было персонажу и всем врагам добавлять этот компонент и прописывать так же само вручную в каждом скрипте спавн текста, вместо этого я сделал еще один компонент - HealthChangedTextSpawnerComponent, к нему я просто подключаю HealthComponent и этот новый спавнер слушает сигнал "damaged" или "healed" (еще нет такого, но будет) и спавнит текст с подходящим типом, обожаю компоненты и композицию - проект превращается в супер-гибкий конструктор 💃

Еще вот по мере роста этого огрызка недоигры у меня появился вопрос: "А как дебажить игры побольше? Мне уже неприятно"

Мне ж когда надо протестить будет поднятие сорока апельсинов мне надо будет их вручную создавать, а потом удалять. Когда надо врага потестить я отключаю спавнер врагов и добавляю нужного врага, и так далее абсолютно со всем

Но мне уже это как бы не очень приятно делать - я часто забываю что-то вернуть на место. Например при тестировании системы опыта и уровней я захардкодил значение "experience_to_next_level = 1", то есть мне надо только один апельсин поднять для нового уровня (для легкости дебага), но там должна быть цифра "5", а я забываю о таких вещах бывает (да-да, надо смотреть ченжи перед пушем в репозиторий, но епт) и это уже причиняет некоторую попаболь, поэтому мне прям интересно - как же тестируют/добавляют что-то в огромные игры?

#godot #ritkarampage #разработка
Please open Telegram to view this post
VIEW IN TELEGRAM
1👍1🔥1
Хороший вопрос - это хорошо, а полезный ответ - еще лучше

Вы даж не представляете как я радуюсь, когда вижу уведомление "вы получили ответ на вопрос ..." с юдеми или же уведомляшки с каналов в телеге, где я тоже бывает задаю вопросы

Но этот Firebelley (препод с курса) - он прям монстр, я реально за 5 лет веб-разработки просмотрел ну просто миллиарды платных и бесплатных курсов, некоторые с них были в основном абстрактными, некоторые теоретическими, некоторые чисто прикладными (практическими) без даже минимального объяснения деталей

А в этом курсе просто все бомба, мне даже не верится в то, что мне так повезло на него наткнутся и бустануть своё обучение настолько сильно

Я уже говорил что за курс я прохожу, но вот еще разочек линка на курс по Godot 4, оно на английском, но даже если вы слабенько понимаете на слух, то там есть субтитры, оно того стоит
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3