@ultrabeat, , Модели автомобилей и скутера взяты из сети для теста физики и отладки техпроцессов. Всё остальное сделано вручную бесплатно мною своими руками для своего этого проекта - здания, ландшафт, дороги, материалы, здания, элементы улицы и прочее. Я делаю оптимизацию, но я не делаю преждевременную оптимизацию. Тому, у кого слабое железо, можно понизить качество графики. Проект планируется на будущее железо. То, которое будет актуально на момент выхода + 5 лет жизни проекта (Видеокарты от GF3000 - до 5000 серий). Реализм - потому что я захотел реализм. Хочется сделать долгоживущий качественный проект, чтобы потомкам не пришлось делать ремастеры и ремейки. И да, я не инди-студия, которая в погоне за деньгами делает очередной хоррор или шутер.
Отличная новость, добавили в игру новую механику с зельями! Для нас это очень важны шаг и начала новой сущности - расходных предметов.
На старте доступно малое зелье энергии, которое восстанавливает от 1 до 2 единиц энергии. Сообщения о получения расходников, в отличии от получения экипировки не будут приходить в общий чат. Также исправили некоторые баги.
Полный список изменений:
В игру введено Малое зелье восстановления энергии
Новый вступительный текст при первом запуске бота
Кнопка Invite to Game не работала в английской локализации, исправлено
Сообщения от Глашатого теперь приходят в соответствии с локализацией чата
При первом запуске бота, исправлен неправильный формат закрепа, который не показывал здоровье в бою
Исправлен баг со стальной крысой, при котором она иногда не появлялась
Уведомление "Хочет что-то сказать..." теперь идёт в чат в соответствии с языком автора
А еще напоминаем, что каждый из вас может забрать до 3 уникальных шлемов с помощью нашей реферальной системы, играть вместе с друзьями намного интереснее!
Кому-то везет еще в школе удачно выбрать профессию, но всегда есть шанс не угадать и в итоге оказаться на нелюбимой работе. Если это как раз ваш случай, не спешите отчаиваться: радикально поменять специальность вовсе не так страшно и сложно, как кажется. Рассказываем о способах освоить новую профессию, чтобы устроиться на работу мечты.
Как понять, что пора менять профессию
Для этого составили краткий чек-лист — пройдитесь по списку и подумайте, насколько к вам относятся эти утверждения:
мне нравятся задачи, которые я выполняю;
меня устраивает текущая зарплата;
мне подходит мой график работы;
моя работа совпадает с моими внутренними ценностями и приоритетами;
у меня хорошие отношения с коллективом и руководством.
Если больше половины пунктов — не про вас, есть повод для размышлений. Иногда к мысли о том, что надо переучиваться, приводят сторонние обстоятельства: например, стресс домашних проблем или банальная усталость. Если перерабатывать, мало спать и плохо питаться, попутно решая семейные неурядицы, нервы не выдержат даже у самых стойких. Решить эти проблемы чаще всего можно отпуском, тайм-менеджментом и профилактикой выгорания.
Помните, что получать удовольствие от работы можно, радикально не меняя род деятельности, — перейти в смежную сферу. Например, если вы заскучали в тестировании, можно прокачаться и уйти в Python-разработку. Так вы сможете использовать свой опыт и быстрее найдете новое место.
Если же не помогает ни отдых, ни смена задач, тогда уже время задуматься о том, чтобы начать с нуля в совершенно новом направлении.
Как выбрать новую специальность
Если вы хорошо все обдумали и твердо решили освоить новую профессию, подойдите к выбору осознанно и учтите прошлый опыт.
1. Подумайте, что вас не устраивает сейчас и почему. В этом поможет чек-лист выше. Составьте список вещей, которые вас не устраивают, чтобы при поиске новой специальности не наступить на те же грабли.
2. Определитесь, чего хотите от новой профессии. Деньги, креативные задачи, карьерный рост, ощущение собственной полезности — подумайте, что для вас важно и мотивирует развиваться.
3. Изучите рынок. Наверняка с момента вашей учебы появились новые интересные специальности. Например, кто бы мог подумать лет 15 назад, что миру потребуются менеджеры маркетплейсов. А сейчас это популярная профессия для легкого старта в IT.
4. Подумайте над новой специальностью. Если хотите стартовать в IT, определиться с направлением поможет бесплатный тест на профориентацию от методистов МГУ и Практикума.
Какие есть варианты освоить новую профессию
Путь к новой карьере зависит от того, сколько времени, усилий и финансов вы готовы на него потратить. Вот несколько вариантов, как можно набраться знаний и развиваться в профессии.
Самообучение
Возможностей учиться самостоятельно сейчас много: методички и учебники, онлайн-уроки в интернете, профессиональные сообщества для обмена знаниями и опытом — в большинстве случаев все это бесплатно или не требует серьезных денежных вложений.
Сложность самообразования в том, что всеми организационными вопросами приходиться заниматься лично: составлять план и график, контролировать себя. К тому же без взгляда со стороны трудно оценить прогресс и выявить ошибки, а знания нечем будет подтвердить на собеседовании.
Обычно самообучение — это неплохой вариант попробовать новую специальность или начать подрабатывать на фрилансе. Полноценно стартовать в новой профессии таким способом сложно.
Онлайн-образование
Речь о профессиональных курсах — это один из популярных сейчас видов дополнительного образования. Такой формат подходит как для повышения квалификации, так и чтобы освоить новую профессию с нуля.
Программы для онлайн-курсов разрабатывают опытные специалисты. Учиться можно в своем темпе и без отрыва от работы. Сам процесс включает теорию и практику при поддержке экспертов — в Практикуме, например, всегда можно обратиться за помощью к наставнику или обсудить вопрос в сообществе с одногруппниками. А в конце курса студенты получают официальный документ, подтверждающий их знания.
Второе высшее
Диплом магистра ценится на собеседованиях, а знания, которые получают студенты, глубже и обширнее, чем при переподготовке и повышении квалификации. Такое образование можно получить в вузе или другом учреждении, которое имеет на это лицензию: например, в Практикуме есть программы с высшим образованием в IT. Но есть здесь и обратная сторона — учиться предстоит два года.
Боюсь менять профессию — что делать
Первый шаг навстречу новой профессии — важный и сложный поступок, торопиться с выбором специальности не стоит. Но и затягивать тоже нежелательно, ведь каждый месяц на нелюбимой работе не только демотивирует, но и задерживает развитие вашей карьеры.
Мы в Практикуме помогаем определиться с новой профессией, даем знания и поддерживаем на протяжении всего обучения. У нас большой выбор онлайн-курсов по разным направлениям IT. Любую цифровую профессию можно бесплатно попробовать: пройти вводную часть и потом окончательно решить, что именно вам подходит. Тестирование и программирование, дизайн, маркетинг, менеджмент, аналитика — выбирайте и учитесь. А потом стройте успешную карьеру в IT, занимаясь тем, что нравится!
Встроенный конвейер рендеринга — это конвейер рендеринга Unity по умолчанию. Это конвейер рендеринга общего назначения с ограниченными возможностями настройки. Универсальный конвейер рендеринга (URP) — это скриптовый конвейер рендеринга, который быстро и легко настраивается и позволяет создавать оптимизированную графику для широкого спектра платформ. Конвейер рендеринга высокого разрешения (HDRP) — это конвейер рендеринга с поддержкой сценариев, который позволяет создавать передовую высококачественную графику на высокопроизводительных платформах.
Версия Юнити : 2022.3.20f1
Встроенный :Не совместимо
УРП : Совместимый
HDRP : Не совместимо
Описание
Jelly Merge — полноценная игра от Watermelon Games.
Эта полностью настраиваемая игра-головоломка отличается простым, но очень увлекательным игровым процессом.
Идеальная отправная точка для вашего следующего хита!
Привет! Спустя несколько месяцев работы, я наконец решился показать демку!
Для демки я подготовил 16 уникальных персонажей, 10 видов построек и 1 игровой уровень! Каждый из персонажей имеет свои индивидуальные базовые статы, скиллы и оружие.
Меню выбора персонажа
Меню строительства
В ближайшее время я планирую обновить тизер чтобы он соответствовал всем изменениям и обновлениям, а далее сосредоточусь на полировке демки к предстоящим ей фестивалям. Сейчас демо версия уже доступна в Steam и в неё можно поиграть!
Как же приятно осознавать, что твоя маленькая мечта осуществилась самым интересным образом!
С 12 лет я пыталась что-нибудь озвучивать. Сперва это был «Наруто» , а после самые разные онгоинги того времени (мне 28 год идет, если что).
В какой-то момент даже удалось заработать 200-400 рублей за серию (чувство гордости)
Мы сколотили небольшую и дружную команду подростков, которые любили своё дело: озвучивали различные тайтлы, обменивались опытом друг с другом и даже сотрудничали с другими командами!
В то время я подрабатывала в магазине, чтобы купить свой первый микрофон. Да и уговаривала родственников потерпеть моё хобби, ведь…я так и не научилась нормально заниматься озвучкой, как бы иронично это ни звучало.
Зато хотелось бы отметить, что некоторые мои коллеги по цеху таки продолжили это дело и довольно успешно! Невероятно горжусь ими и слежу за их творчеством! (´。• ᵕ •。`) ♡
А также представляю вашему вниманию пару отрывков. Эт я пыталась найти более-менее хорошие! Только звук потише сделайте)
Возможно, спойлеры!!!
Тайтлы: Hamatora The Animation; Gunslinger Stratos The Animation; Koe no Katachi
А какая мечта была, а?
Я ХОТЕЛА ПРИНЯТЬ УЧАСТИЕ В ОЗВУЧКЕ ИГРЫ!
Это же так волнительно! Волнительно подарить свой голос персонажу! Волнительно слышать себя по ту сторону экрана. Даже сейчас появляются мурашки от этой мысли.
И как же я воплотила это?
Оч изощренно: устроилась QA в небольшую инди-компанию….и им было не отвертеться.
С моим прекрасным коллегой мы скооперировались, набросали список звуков, которые необходимо внедрить в игру и…началось
Конечно, это неполноценные фразы, ибо в нашей игре подобный формат не предусмотрен, но…я смогла записать свой голос для игры… Для игры, в разработке которой принимаю активное участие. И даже не просто голос, но и попытки озвучить какие-нибудь…предметы? Ага! Для этого уже много лет в заводских коробочках тусят микрофон и звуковая карта (очень даже неплохие!)
И вот! Прилагаю примеры того, какие попытки были предприняты + часть того, что вошло в игру)
Предупреждаю сразу, что это не вся озвучка и впереди нас ждет немало работы и для других персонажей!
Если будет интересно, запилю еще чего-нибудь про нашу работу и озвучку
Я пишу игру на игровую консоль Playdate на чистом C. Игра в жанре "выживальщик" наподобие Vampire Survivors. Так как в чистом C отсутствуют многие современные объектно-ориентированные удобства мне приходится по-всякому изворачиваться чтобы адаптировать свои идеи в код. В этих заметках ты узнаешь из первых уст как создаётся игруля с нуля от идеи до публикации.
В прошлой главе я описал как инициализирую сцену, как очищаю ресурсы, показал как заполняю сцену реквизитом и даже поэкспериментировал с генерацией этого самого реквизита. В этой главе я расскажу как работает самая важная функция GameUpdate, в частности, обработка ввода и процессинг данных.
GameUpdate это функция-колбэк, которая вызывается каждый тик. А значит её задача это реализовать святую троицу любой игры:
считать ввод от игрока
обновить состояние игры
отрисовать обновлённое состояние игры.
Если ты когда-нибудь был на собесе в геймдев контору, то 90% вероятность, что у тебя спрашивали про эти три шага. А ещё если ты когда-нибудь писал код для Arduino, то ты должно быть помнишь две функции, которые там всегда должны быть: setup и loop. Вот GameUpdate это как раз аналог loop.
На сцене есть машина, которая двигается от нажатия крестовины, и есть реквизит: кактусы, песчаные насыпи и перекати-поле. Перекати-поле двигается прям как в жизни. То есть, оно меняет позицию по-горизонтали (по оси X) и ещё прыгает вверх-вниз как бы отскакивая от земли. Чтобы реализовать движение нам нужно совладать со временем. Для этого нам в каждом тике нужно знать сколько точно прошло времени с прошлого тика. Из коробки этого параметра в событии Update нет, однако мы можем этот параметр высчитать при помощи функции API playdateApi->system->getElapsedTime();. Эта функция возвращает количество секунд прошедших с момента запуска игрули. Это не разница в тиках, но уже что-то. Для разницы времени в тиках надо ещё знать значение полученное из той же функции в прошлый тик. Потому в структуре Game есть поле float previousElapsedTime;. В конце функции GameUpdate мы сохраняем в это поле результат вызова getElapsedTime, а в начале GameUpdate мы вычитаем разницу между нынешним значением getElapsedTime и previousElapsedTime. Это значение и есть тот самый dt, который равняется количеству секунд прошедших с прошлого тика. Так как на старте игры в файле main.c в первой главе я установил FPS равный 30, то в среднем dt у меня равен 0.033 секунд.
Начало функции GameUpdate
Далее мы процессим инпут - собираем значения нажатых кнопок и в зависимости от них обновляем данные.
Процессинг инпута
PDButtons это битовая маска объявленая в Playdate SDK. Битовые маски в сишке реализуются либо как enum, либо просто как int в отличие от Свифта, где битовая маска это совершенно иной особый класс данных.
Описание битовой маски PDButtons
Битовая маска PDButtons содержит в себе список нажатых и ненажатых кнопок консоли.
Ещё, возможно, у тебя есть вопрос что за такая функция PlayerVehicleAngleCreateFromButtons на строке 72. Это способ определения одного из восьми направлений машины имея на руках нажатые кнопки девайса:
Реализация функции PlayerVehicleAngleCreateFromButtons
Зачем нужен параметр oldValue в ней? Дело в том, что нам надо что-то возвращать даже если ни одна кнопка не нажата. А что вернуть если ни одна кнопка не нажата? Какое направление? В Свифте/C++/C# я бы вернул зануляемое значение (Optional в Свифте, std::optional в C++ и Nullable в C#), но в сишке это не так удобно потому что нет дженериков/шаблонов, потому я решил передавать старое значение направления потому что в случае когда ни одна кнопка не нажата направление машины просто не меняется. Это логично потому что в жизни если ты не трогаешь руль, то направление автомобиля тоже не меняется. Вот потому мы передаём старое значение и возвращаем его тогда, когда в Свифте/C++/C# вернули бы null. Если бы я работал в корпорации с отжайл-митами, ретроспективами, эффективными менеджерами, тимбилдингами и код-ревью, то обязательно появился бы ревьювер, который мне рассказал, что аргумент oldValue, если посмотреть на ситуацию под определённым углом, переносит логику того как движется машина внутрь функции PlayerVehicleAngleCreateFromButtons, а это неправильно потому что если следовать SOLID, стремиться писать идеальный код, утром и вечером чистить зубы, ходить на йогу, участвовать в городских марафонах, отказаться от мяса, глютена, молока, сахара, соли, глутамата натрия и кока-колы, то эта функция должна отвечать исключительно за создание инстанса перечисления PlayerVehicleAngle и больше ничего, а логика передачи старого значения обязательно, прям кровь из носу, век воли не видать, без разговоров, обсуждений и переговоров, должна находиться за пределами функции PlayerVehicleAngleCreateFromButtons потому что чисто теоретически у нас может эта функция использоваться не только для машины, а для чего-либо другого, что имеет также 8 направлений, но в случае если ничего не нажато направление будет, например, сбрасываться вверх. И пофиг ревьюверу на то, что такое случится примерно когда рак на горе свистнет, в четверг после дождя и ровно в следующую секунду после второго пришествия.
Если отбросить иронию и сформулировать ответ для занудного воображаемого ревьювера, то он (ответ) будет таким: значение oldValue это прекрасный подход реализации кода очень схожий с построением электрических цепей. Значение словно ток по цепи идёт сквозь функцию, и при определённых условиях оно может измениться на выходе, а может остаться таким же. Вообще код в стиле электрических цепей популярен в сишке, и при этом не так популярен в объектно-ориентированных языках. Я, понятное дело, не призываю всех на сишке писать именно в такой парадигме, но за себя я отвечаю вот таким вот образом.
Фух. Далее. Есть ещё функция GameAnyArrowIsPressed.Она возвращает 1 если хотя бы одна кнопка на крестовине нажата и 0 в противном случае:
Реализация функции GameAnyArrowIsPressed (возможно, следовало разнести операторы & по отдельным строкам для красоты кода)
Штош, мы пришли к следующему невероятно важному шагу нашего жизненно-важного тика - обработка перекати-поле.
Процессинг перекати-поле в тике
Констанцията screenSize пока не нужна - она пригодится позже. Далее мы проходимся по массиву старым проверенным методом: достаём количество объектов в нём и описываем цикл for. Получив очередной объект перекати-поля на строке 118 я готов его менять (потому указатель на Tumbleweed неконстантный). Разминаю руки и говорю себе "делай красиво!". Первым делом процессим позицию потому что перекати-поле перекатывается по полю по-горизонтали. Каждый тик двигающийся объект сдвигается (вот это поворот!), а значит мы должны проделать нехитрые манипуляции с позицией. Это требует базовые знания раздела механики из физики (того самого про "скорость равняется пройденный пуць делить на время", а ещё я обожаю слово "пуць" которое я подцепил в Беларуси когда жил там три года. Я стараюсь говорить "пуць" везде вместо слова "путь" и рекомендую тебе тоже так делать так как от такого русский язык станет только краше!). Чтобы лучше понять как происходит процесс движения в коде в первую очередь надо понять что нам нужно в конце концов сделать за тик. За тик нам нужно изменить позицию каждого объекта перекати-поле. Точнее, понять насколько изменилась позиция объекта перекати-поле относительно старой позиции за тик. Это изменение как раз хранится в константе dTumbleweedPosition, которая создаётся на строке 121. Высчитывается она очень просто: скорость перекати-поле умножается на dt, то есть, скорость умножаем на прошедшее время за один тик. А далее изменение позиции dTumbleweedPosition просто прибавляется к позиции этого же перекати-поле.
Подобным образом движение работает у всего вообще везде, не только в моей игре, а во всех играх и не только играх - всякие плавно двигающиеся кнопки в пользовательском интерфейсе, прыгающая иконка загрузки в яблочном браузере Safari, всплывающее окно антивируса Avast, падающие пуш-уведомления на iOS и многое другое что можно перечислять тут ещё до полуночи.
Окэй, с движением мы разобрались. Идём далее. А далее мы процессим прыжок. Дело в том, что перекати-поле подпрыгивает в движении. Значит нам в нашем мире который мы создаём своей мыслью и кодом нужно запрограммировать аналогичные прыжки перекати-поле и желательно чтобы результат выглядел правдоподобно, а не топорно как анимация в Героях 4. Вот только как сделать подпрыгивания чтобы они выглядели достаточно правдоподобно? Просто линейно как движение? Но это будет обсосно так как в реальности в любом движении по-вертикали участвует ускорение свободного падения, а это делает функцию движения квадратичной, а значит линейное движение не подойдёт. Функция нужна точно квадратичная, то есть, аргумент в ней обязательно должен хотя бы в одном месте возводиться в квадрат. Самое банальное это парабола. Она самая подходящая тут потому что в реальной жизни всё падает по параболе (конечно если игнорировать ветер и вообще если экспериментировать на сферических цыплятах в вакууме). Но если перекати поле будет лениво лететь в сторону земли по параболе, то тогда при столкновении с землёй мне надо будет иметь реализованную логику этого самого столкновения для отскока. Тут я оценил-взвесил прям как Экшон-мэн (помнишь такого супергероя? я в детстве обожал мультик "Экшон-мэн", и особенно мне нравилась его трёхмерная компьютерная рисовка. Тогда мне казалось, что это лучшая графика на свете. Недавно я решил пересмотреть этот мультик и офигел от того какая оказывается ужасная графика там на самом деле! RDR2 меня разбаловала! В общем, Экшон-мэн в момент кульминации каждой серии произносил "оценить, взвесить", просчитывал свои движения до мельчайшей точности, а в следующие 10 секунд нагибал всех врагов ультой) и решил сделать проще: я использую уравнение окружности, точнее, уравнение косинуса (или суниса если угодно, потому что график синуса это график косинуса сдвинутый на 90 градусов).
График синуса собственной персоной y = sin(x)
Вот только для наших целей мы график синуса чуть модернизируем - засунем его в модуль. Засовывание любой функции в модуль делает с её визуальным отображением занимательный фокус - отображает нижнюю половину вверх словно ось x превратилась в зеркало.
График модуля синуса y = abs(sin(x))
И вот такой вариант прям идеально похож на траекторию движения перекати-поля, и при этом нам не нужно писать логику столкновения с землёй и последующего отскока. Это тот редкий случай когда та фигня, которой тебя пичкали в школе, тебе пригодилась в работе!
Для адаптации данного математического фокуса в код нам нужно чтобы каждый объект перекати-поля имел значение "поворота" прыжка, а также скорость этого поворота (на сколько радиан значение поворота изменится за 1 секунду). Почему поворот? Потому что график синуса принимает в качестве переменной именно направление. Для пущего понимания на это можно смотреть как на фазу, которая крутится. Таким образом, у структуры Tumbleweed есть поля jumpVelocity и jumpAngle. На строке 125 мы высчитываем значение dTumbleweedJumpAngle равное количеству радиан на которое изменился jumpAngle, на строке 126 прибавляем это значение к jumpAngle, а на строке 127 нормализуем jumpAngle. Нормализация направлений это вещь, которую иногда следует делать если работаешь с направлениями - примерно как убирать какашки за кошкой если ты живёшь с кошкой (или она с тобой, лол). Так как значение направления циклично (0 радиан и 2*π радиан это одно и то же значение, например), можно для чистоты кода, совести и кредитной истории после операций над направлением приводить его в диапазон [0; 2*π) если вдруг это направление вышло за пределы (если кошка покакала мимо лотка надо всё вытереть, потому что сама кошка это вряд ли сделает).
Реализация функции normalizeAngle
Вообще будь у нас С++ я, возможно, нормализацию бы засунул прям внутрь класса Angle в оператор присваивания, который можно невозбранно перегружать. А может и нет - неявности порой делают код хуже. Как бы там ни было, именно таким образом мы процессим прыжки перекати-поля.
Итого, мы разобрались с процессингом позиции перекати-поля, прыжков (на самом деле процессить прыжки это лишь полдела, надо ещё их кошерно отрисовать, а это я покажу далее), осталось запроцесить кадр. Да, перекати-поле в моей игруле имеют несколько кадров для красивости. Я так сделал так как иначе если бы у перекати-поля был бы один кадр это выглядело бы обсосно. А я не хочу чтобы моя игруля выглядела обсосно. Вот для процессинга кадра я в структуру Tumbleweed добавил поле frameIndex. Вообще в игре у много чего будет такое поле и подобная логика. Ну и скорость изменения frameIndex тоже есть: это поле frameIndexVelocity. Да, это поле есть у каждого объекта Tumbleweed, хотя у всех объектов оно имеет одинаковое значение. Можно было бы не добавлять это поле потому что вроде как оно избыточно, но пусть будет - вдруг я решу сделать скорость разной у разных инстансов перекати-поля (а такие мысли в момент написания кода у меня были), а экономить память на спичках это путь в сумасшедший дом. Всего кадров у перекати-поля сделано 4. В одной из прошлых глав ты видел константу TumbleweedSpritesCount = 4 - вот это про это. frameIndex - это число с плавающей точкой, которое меняется в диапазоне [0; 4) со скорость указанной в frameIndexVelocity. Логика строк 130 - 134 осуществляет именно это.
Вот так устроен процессинг перекати-поля. Как тебе? Меня лично вставляет. Идём дальше.
Порой надо создавать перекати-поле, а не только процесить. Для этого надо решить по какой логике оно будет создаваться. Когда я усердно играл в Minecraft я частенько читал вики по нему. И в вики по Майнкрафту рассказывали каким образом спаунятся различные сущности. И логика спауна примерно такая: шанс один из десяти тысяч что в конкретном тике заспаунится сущность. Вот такую же логику я решил впиндюрить потому что это просто и понятно.
Создание перекати-поля
Строка 139 говорит нам, что с шансом 1 к 100 (tumbleweedSpawnChangePercentage равна 1) создастся новое перекати-поле в тике. На строке 154 создаётся инстанс перкати-поля функцией TumbleweedCreate, а на следующей строке этот инстанс отправляется (на самом деле копируется) в массив game->tumbleweeds.
Для создания перекати-поле нам нужно 4 аргумента: позиция на карте, скорость передвижения, скорость подпрыгивания и скорость изменения кадра. Позиция на карте высчитывается суперхитрым образом - новое перекати-поле появляется просто ровно за границей экрана левой либо правой. И едет в сторону машины игрока по-горизонтали. Можно, конечно спаунить "по-честному" в случайной точке достаточно большого игрового поля, но тогда игрок просто будет редко видеть перекати-поле, особенно в начале игры, а это ухудшает пользовательский опыт. Скорость подпрыгивания это количество радиан прошедших за секунду для значения от которого мы считаем синус график которого я ранее показывал. А про скорость изменения кадра ты уже и так знаешь: у перекати-поля 4 кадра, как я говорил ранее, и их надо с определённой скоростью менять.
Далее на строке 158 смещению камеры присваивается позиция машины чтобы машина всегда была в центре экрана куда бы она не ехала. А на строке 160 вызывается функция GameDraw, которая весь описываемый мной тут балаган отрисовывает чтобы игрок видел что происходит, иначе зачем всё это?
Отрисовку мы рассмотрим в следующей главе, а тебе спасибо что дочитал. Если нравится как я пишу и хочешь меня поддержать денюшкой, то я есть на патреоне и бусти.