<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Миша пишет движок</title><description>Личный блог о разработке игрового движка.</description><link>https://misharemmele.ru/</link><language>ru</language><lastBuildDate>Tue, 24 Feb 2026 19:20:23 GMT</lastBuildDate><ttl>60</ttl><image><url>https://misharemmele.ru/favicon-96x96.png</url><title>Миша пишет движок</title><link>https://misharemmele.ru/</link><width>96</width><height>96</height></image><item><title>Кастомные шейдеры</title><link>https://misharemmele.ru/posts/2026-02-15/kastomnye-shejdery/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2026-02-15/kastomnye-shejdery/</guid><description>Последняя задача по рендерингу, которую я планировал сделать – это возможность писать и использовать кастомные шейдеры. В Pixi для решения этой задачи можно использовать фильтры.</description><pubDate>Sun, 15 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Последняя задача по рендерингу, которую я планировал сделать – это возможность писать и использовать кастомные шейдеры.&lt;/p&gt;
&lt;p&gt;В Pixi для решения этой задачи можно использовать &lt;a href=&quot;https://pixijs.com/8.x/guides/components/filters&quot;&gt;фильтры&lt;/a&gt;. Это такой механизм для добавления пост обработки. Фильтры влияют не только на сам объект, но и на всех его потомков. Именно поэтому я вносил доработки в рендерер и добавлял дерево, чтобы при использовании фильтров использовать это преимущество.&lt;/p&gt;
&lt;p&gt;На практике, когда я собрал первый черновик и пошел тестировать – ничего не заработало. Как я выяснил позднее, фильтры плохо дружат со &lt;a href=&quot;https://pixijs.com/8.x/guides/concepts/render-layers&quot;&gt;слоями&lt;/a&gt;, которые я использовал чтобы контролировать порядок отрисовки. Я завел &lt;a href=&quot;https://github.com/pixijs/pixijs/issues/11826&quot;&gt;issue&lt;/a&gt; на гитхабе Pixi, чтобы убедиться, что я понимаю все правильно и спустя месяц получил ответ мол, да это такое ограничение у RenderLayers.&lt;/p&gt;
&lt;p&gt;Раздосадованный я продолжил искать другие варианты и остановился на &lt;a href=&quot;https://pixijs.com/8.x/guides/components/scene-objects/mesh&quot;&gt;мэшах&lt;/a&gt;. Это более низкоуровневый вариант отображения графики чем спрайт, фигура или текст, которые я уже добавил в движок. Тут нужно самостоятельно описывать вершинный и фрагментный шейдеры, задавать текстуру через uniform переменную и все что еще потребуется для отрисовки.&lt;/p&gt;
&lt;p&gt;По итогу я реализовал новый компонент Mesh, который очень похож на уже существующий Sprite, за тем исключением, что через поле material ему можно указать скрипт с шейдерами для отрисовки. От фильтров я тоже полностью отказываться не стал и оставил возможность повесить фильтры на все сцену целиком для случаев если захочется добавить эффекты вроде CRT. И то, и другое можно создавать из шаблонов через редактор.&lt;/p&gt;
&lt;p&gt;Шейдеры можно использовать не только для каких-то мудреных эффектов, но и для решения простых задач, вроде рисования выделения вокруг объекта или эффекта вспышки, чтобы показать получение урона. Это намного удобнее, чем тратить время на рисование дополнительных кадров в спрайте и обновлять стейт машину анимации.&lt;/p&gt;
&lt;p&gt;Полагаю, что с отрисовкой я пока что закончил, поскольку делать одно и то же несколько месяцев – сильно утомляет. Вернусь к улучшению редактора, к тому же хочется посмотреть как с моими задачами справится Codex.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/videos/2026-02-15/1.mp4&quot;&gt;Смотреть видео&lt;/a&gt;&lt;/p&gt;</content:encoded><enclosure url="https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/images/2026-02-15/1.avif" length="0" type="image/avif"/></item><item><title>Итоги 2025 года</title><link>https://misharemmele.ru/posts/2025-12-30/itogi-2025-goda/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2025-12-30/itogi-2025-goda/</guid><description>Раньше я не подводил итоги года, но после того как я перестал вести доску задач я решил, что заметок в Телеграме мне достаточно и лучше я буду детальнее описывать над чем работаю в свой канал. В целом, за год получилось сделать довольно много крупного и полезного.</description><pubDate>Tue, 30 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Раньше я не подводил итоги года, но после того как я перестал вести доску задач (из Trello меня погнали, потом и из Notion) я решил, что заметок в Телеграме мне достаточно (надежное же место, да?) и лучше я буду детальнее описывать над чем работаю в свой канал. В целом, за год получилось сделать довольно много крупного и полезного.&lt;/p&gt;
&lt;h2&gt;По задачам&lt;/h2&gt;
&lt;p&gt;– Мультивыбор элементов в редакторе: выделение рамкой, выбор с шифтом в проводнике. Копирование, удаление и вставка выборки по ctrl+C, ctrl+V, ctrl+X.
Очень долго этого не хватало, любое серьезное редактирование сцены превращалось в кошмар, из-за необходимости работать с каждым объектом по отдельности.&lt;/p&gt;
&lt;p&gt;– Звуковая система. Реализация довольно простая без поддержки пространственного звука, но можно разделять звуки на разные дорожки, управлять из кода воспроизведением через события и регулировать громкость индвидуальных треков или дорожки целиком.&lt;/p&gt;
&lt;p&gt;– Переработал жизненный цикл сцен и систем. Ранее под каждую сцену нужно было собирать свой список систем, дублировать все, включая рендерер, физику и обработку ввода. Теперь список систем общий на всю игру, есть системы которые живут и пересоздаются в рамках каждой сцены, а есть глобальные, которые живут пока запущена игра. Стало проще шарить состояние игры между сценами/уровнями и не надо копипастить длинные списки систем под каждую сцену.&lt;/p&gt;
&lt;p&gt;– Одна из самых крутых задач на мой взгляд – это декораторы для регистрации игровых систем и компонентов. Ранее нужно было писать много кода, чтобы сообщить редактору как отображать кастомные компоненты и системы. А при изменениях нужно было каждый раз перезагружать редактор. Теперь через декораторы редактор самостоятельно собирает по игре все с чем умеет работать. Все изменения в коде редактор подхватывывает автоматически и отображает без необходимости перезапуска.&lt;/p&gt;
&lt;p&gt;– Cнова переписал рендерер. На этот раз на Pixi.js. Надеюсь что в ближайшие годы я больше не буду его переписывать, а только расширять. Помимо спрайтов научился рисовать различные геометрические фигуры и bitmap текст. На подходе удобный способ добавлять шейдеры. Сам рендеринг за счет более подходящей библиотеки для 2D стал быстрее да и бандл немного уменьшился.&lt;/p&gt;
&lt;p&gt;– Ну и доработки по Transform-у о которых я рассказывал в последний раз.&lt;/p&gt;
&lt;h2&gt;По джемам&lt;/h2&gt;
&lt;p&gt;Написал две игры на людумах. Игры все еще маленькие, но уже выглядят более законченными чем то, что делали в прошлом году. Над одной из игр снова поработал в команде, хоть и небольшой: я и моя жена.&lt;/p&gt;
&lt;p&gt;От джемов устал как-то больше обычного. Слишком мало времени: за три дня кое-как получается собрать что-то работающее и слишком много всего приходится дропать или делать в спешке.&lt;/p&gt;
&lt;h2&gt;Что было еще&lt;/h2&gt;
&lt;p&gt;Сходил на подкаст к @xufostation. Поговорили про разработку моего движка и джемы. Опыт интересный хоть и было немного не по себе, будучи ноунеймом идти на подкаст. В очередной раз спасибо Сергею, он и с докладом на Holy мне очень помог в свое время, и новых людей сюда привел.&lt;/p&gt;
&lt;h2&gt;Планы&lt;/h2&gt;
&lt;p&gt;Помимо шейдеров, нужно пересмотреть то как я работаю с шаблонами. Сейчас неудобно смотреть в редакторе что именно наследует актор от шаблона, а еще неудобнее эти значения переопределять. Плюс хотелось бы добавить возможность наследовать один шаблон от другого.&lt;/p&gt;
&lt;p&gt;Вдобавок ко всему в движке не хватает сущности, которой можно было бы описать объекты, которые не являются акторами, но которые можно к этим акторам привязать. Это различные предметы, квесты, способности и тому подобное. Все это хочется описывать в редакторе, а не хардкодить портянки объектов в коде.&lt;/p&gt;
&lt;p&gt;Пока не знаю хочу ли я продолжать с джемами. Возможно сделаю перерыв с людумами, но попробую посетить GMTK если он снова будет продолжаться 4 дня или больше.&lt;/p&gt;
&lt;p&gt;Вообще хотелось бы написать более крупную игру, такую, которую можно сделать за год. Что-то все еще небольшое, но чтобы я смог успеть реализовать все хотелки, нормально нарисовать всю графику, а не за один день. Этим я и планирую заняться в следующем году.&lt;/p&gt;
</content:encoded></item><item><title>Строим дерево сцены для отрисовки</title><link>https://misharemmele.ru/posts/2025-12-29/stroim-derevo-sceny-dlya-otrisovki/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2025-12-29/stroim-derevo-sceny-dlya-otrisovki/</guid><description>После переписывания рендеринга на Pixi. js, в планах было добавить возможность удобно писать кастомные шейдеры или быстро применять встроенные в Pixi фильтры вроде размытия на один или группу акторов. Повесить фильтр/шейдер на один объект несложно, а вот с группами возникла проблема.</description><pubDate>Mon, 29 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;После переписывания рендеринга на Pixi.js, в планах было добавить возможность удобно писать кастомные шейдеры или быстро применять встроенные в Pixi фильтры вроде размытия на один или группу акторов. Повесить фильтр/шейдер на один объект несложно, а вот с группами возникла проблема.&lt;/p&gt;
&lt;p&gt;Хоть сцена и акторы в движке представляют собой дерево, при отрисовке через Pixi я собирал все объекты для отображения в один плоский список. Вложенность при отрисовке мне была не нужна и так было проще сортировать спрайты по слоям и координатам чтобы те, что отображаются ниже на экране, перекрывали спрайты выше.&lt;/p&gt;
&lt;p&gt;Переделать список на дерево для Pixi оказалось несложно, так как Pixi сцена тоже дерево, а для работы со слоями и сортировкой в последнем мажоре рендерера есть Layer API. Более серьезная проблема меня ожидала в текущей реализации компонента Transform.&lt;/p&gt;
&lt;p&gt;Transform компонент отвечает за все трансформации актора в пространстве – смещение по координатам, поворот и масштабирование. Чтобы дочерние акторы учитывали смещение родителя, я в компоненте оставлял ссылку на такой же компонент родительского актора и считал общее смещение складывая родительские значения с дочерними. Это работало для очень простых сценариев, но если к примеру повернуть родителя, то дочерний объект повернется не относительно родителя как должно быть, а относительно своей оси.&lt;/p&gt;
&lt;p&gt;Когда рендерер начал работать со сценой как с деревом, то появился рассинхрон между тем как объекты рисуются и как они воспринимаются остальными системами, которые работают с координатами объектов. Рендерер начал рисовать сцену правильно, а другие системы вроде физики продолжили использовать некорректно посчитанные координаты из Transform.&lt;/p&gt;
&lt;p&gt;В результате я полностью переписал Transform компонент и начал использовать матрицы 3 на 3. Матрицы позволяют описать все преобразования по смещению, повороту и масштабированию объекта так, что при перемножении матриц дочернего объекта и родителя мы можем получить итоговые значения по всем преобразованиям.&lt;/p&gt;
&lt;p&gt;Вся работа с матрицами спрятана под капот компонента, поэтому снаружи достаточно обращаться к привычным полям вида x, y, rotation и тд.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const transform = actor.getComponent(Transform);

// абсолютные координаты актора в мировом пространстве
console.log(transform.world.position.x, transform.world.position.y);

// относительно координаты актора в пространстве родителя
console.log(transform.local.position.x, transform.local.position.y);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Как обычно очередная фича в движке потребовала переработки того, что уже есть и было написано лишь бы работало, но теперь все подготовительные работы завершены, и можно приступать к поддержке кастомных шейдеров. Но это уже в следующем году.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/videos/2025-12-29/1.mp4&quot;&gt;Смотреть видео&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Перемещение камеры и зум с помощью трекпада</title><link>https://misharemmele.ru/posts/2025-11-15/peremeshchenie-kamery-i-zum-s-pomoshchyu-trekpada/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2025-11-15/peremeshchenie-kamery-i-zum-s-pomoshchyu-trekpada/</guid><description>Спустя месяц отдыха пора приниматься за работу. Начал с простой фичи, которой давно не хватало – возможности двигать камеру и менять зум используя трекпад или колесико мыши, а не только по выбору отдельного инструмента в тулбаре.</description><pubDate>Sat, 15 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Спустя месяц отдыха пора приниматься за работу.&lt;/p&gt;
&lt;p&gt;Начал с простой фичи, которой давно не хватало – возможности двигать камеру и менять зум используя трекпад или колесико мыши, а не только по выбору отдельного инструмента в тулбаре. Сделал очень просто с помощью события &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Element/wheel_event&quot;&gt;wheel&lt;/a&gt;. Событие похоже на скролл, но в отличие от него срабатывает на областях, которые нельзя прокручивать, что мне и было нужно для моего канваса. При жестах зума на трекпаде, в объекте события автоматически выставляется флаг ctrlKey, поэтому одним обработчиком получается реализовать зум и для трекпада и для колесика мыши с зажатой клавишей Ctrl.&lt;/p&gt;
&lt;p&gt;Теперь редактировать сцену стало удобнее. При демонстрации доработки жене я получил ожидаемый вопрос: &quot;А куда смотреть? Что изменилось?&quot;. Очередная функция, которая ожидается в редакторе по умолчанию и заметить можно разве что ее отсутствие.&lt;/p&gt;
&lt;p&gt;Тяжело восстанавливаться после трехдневной разработки игры. Возможно два джема в год для меня многовато. В следующем году, наверное, стоит пропустить людумы и вместо этого принять участие в &lt;a href=&quot;https://gamemakerstoolkit.com/jam/&quot;&gt;GMTK&lt;/a&gt; – еще один очень популярный джем, который проходит в конце лета и длится чуть дольше: четыре дня вместо трех.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/videos/2025-11-15/1.mp4&quot;&gt;Смотреть видео&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Финал Ludum Dare 58</title><link>https://misharemmele.ru/posts/2025-10-07/final-ludum-dare-58/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2025-10-07/final-ludum-dare-58/</guid><description>Третий день и окончание людума. В этот раз на реализацию механик ушло больше времени чем раньше, но как будто первоначальную задумку особо и не порезали.</description><pubDate>Tue, 07 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Третий день и окончание людума. В этот раз на реализацию механик ушло больше времени чем раньше, но как будто первоначальную задумку особо и не порезали.&lt;/p&gt;
&lt;p&gt;На рисование осталось мало времени, поэтому пришлось собирать что-то в попыхах и упрощать внешний вид. Звуки добавил как и в прошлый раз в последний момент, точнее переиспользовал все что было из предыдущей игры.&lt;/p&gt;
&lt;p&gt;Над балансом еще нужно поработать, потому что в какой-то момент можно пройти игру в афк режиме.&lt;/p&gt;
&lt;p&gt;Устали страшно. Подумываю над тем, что два раза в год для меня – это, пожалуй, перебор. Но теперь можно отдыхать.&lt;/p&gt;
&lt;p&gt;Вдвоем проще и веселее, но нужно закладывать время на обучение как работать с моим движком. Когда-нибудь я обязательно напишу документацию.&lt;/p&gt;
&lt;p&gt;Ссылка на игру: &lt;a href=&quot;https://ludum-dare-58.netlify.app/&quot;&gt;https://ludum-dare-58.netlify.app/&lt;/a&gt; (можно с телефона, но лучше в вертикальном положении, в горизонтальном верстка едет)&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/videos/2025-10-07/1.mp4&quot;&gt;Смотреть видео&lt;/a&gt;&lt;/p&gt;</content:encoded><enclosure url="https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/images/2025-10-07/1.avif" length="0" type="image/avif"/></item><item><title>Второй день Ludum Dare 58</title><link>https://misharemmele.ru/posts/2025-10-06/vtoroj-den-ludum-dare-58/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2025-10-06/vtoroj-den-ludum-dare-58/</guid><description>До рисования дойти не успели, но на экране появилось определенно больше кругов и прямоугольников. За сегодня добавили разные виды оружия: дробовик, самонаводящуюся ракету, рикошет и тд.</description><pubDate>Mon, 06 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Второй день людума. До рисования дойти не успели, но на экране появилось определенно больше кругов и прямоугольников.&lt;/p&gt;
&lt;p&gt;За сегодня добавили разные виды оружия: дробовик, самонаводящуюся ракету, рикошет и тд. Добавили усиление врагов со временем и увеличение частоты спавна.&lt;/p&gt;
&lt;p&gt;От баррикадирования проходов решили отказаться и вместо этого начали работу над модификаторами оружия. Они покупаются за деньги, которые иногда выпадают с противников. Модификаторы есть трех видов: взрыв при уничтожении снаряда, заморозка противника и отравление. Модификатор навешивается на каждое оружие по отдельности.&lt;/p&gt;
&lt;p&gt;На экране теперь происходит куча всего сразу и как это сбалансировать пока не очень понятно. Но будем пытаться.&lt;/p&gt;
&lt;p&gt;Завтра добьем модификаторы, я сяду рисовать и в целом будем доделывать все что успеем. В целом новых фич добавлять больше не планировали, поэтому надо привести в приемлимый вид то что уже есть.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/videos/2025-10-06/1.mp4&quot;&gt;Смотреть видео&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Первый день Ludum Dare 58. Тема Collector</title><link>https://misharemmele.ru/posts/2025-10-05/pervyj-den-ludum-dare-58-tema-collector/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2025-10-05/pervyj-den-ludum-dare-58-tema-collector/</guid><description>Делаем с женой игру пока непонятно про кого, но он защищает свои сокровища (желтый круг) от воришек. Как обычно, начали с цветных кругов и квадратов.</description><pubDate>Sun, 05 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Первый день людума. Тема Collector.&lt;/p&gt;
&lt;p&gt;Делаем с женой игру пока непонятно про кого, но он защищает свои сокровища (желтый круг) от воришек. Как обычно, начали с цветных кругов и квадратов.&lt;/p&gt;
&lt;p&gt;Сегодня сделали спавн противников, их перемещение до сокровищ, механику воровства и боевую систему. Персонаж стреляет автоматически по случайному врагу. Если вора поймать и убить, то из него выпадают монеты, которые можно вернуть в казну. За каждого поверженного противника дается опыт, а при получении нового уровня появляется меню выбора улучшений (пока выбирать не из чего и единственный вариант улучшения – только качать основную атаку). В общем, нашел повод сделать клон Vampire Survivors.&lt;/p&gt;
&lt;p&gt;Завтра планируем добавлять дополнительные варианты оружия для прокачки, разные виды врагов и возможность баррикадировать проходы.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/videos/2025-10-05/1.mp4&quot;&gt;Смотреть видео&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Новый рендерер: фигуры и текст</title><link>https://misharemmele.ru/posts/2025-09-27/novyj-renderer-figury-i-tekst/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2025-09-27/novyj-renderer-figury-i-tekst/</guid><description>Работа над новым рендерером занимает чуть больше времени чем хотелось, но основа уже готова. Добавил возможность рисовать геометрические фигуры и bitmap текст.</description><pubDate>Sat, 27 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Работа над новым рендерером занимает чуть больше времени чем хотелось, но основа уже готова. Добавил возможность рисовать геометрические фигуры и bitmap текст. Теперь можно рисовать буковки прямо на сцене и что-то с ними делать.&lt;/p&gt;
&lt;p&gt;В дополнение к этому избавился от костылей в редакторе. Старый рендерер умел рисовать только спрайты, поэтому для рамок выделения или сетки приходилось наворачивать дополнительные слои: грид рисовал через css градиент, а прямоугольники для выделения объектов – через канвас. Теперь для всего используется pixi рендерер.&lt;/p&gt;
&lt;p&gt;На следующей неделе очередной людум, поэтому придется взять паузу. Из того что еще нужно сделать: добавить фильтры с пользовательскими шейдерами, а также решить, что делать с освещением. На three js я использовал решение из коробки, а на pixi такого нет. Нужно что-то сообразить либо найти готовый плагин.&lt;/p&gt;
&lt;p&gt;Вторая половина лета вышла не очень продуктивной, во многом из-за переезда, но зато в ближайшее время заниматься этим больше не придется. Взял пару дней отпуска на людум. Нужно сделать что-то поинтереснее чем в прошлый раз.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/videos/2025-09-27/1.mp4&quot;&gt;Смотреть видео&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Переход с Three.js на Pixi.js</title><link>https://misharemmele.ru/posts/2025-08-17/perehod-s-three-js-na-pixi-js/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2025-08-17/perehod-s-three-js-na-pixi-js/</guid><description>После череды полезных задач захотелось заняться тем чего душа давно просит, а именно – снова переписать рендерер. На этот раз с ThreeJS на PixiJS.</description><pubDate>Sun, 17 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;После череды полезных задач захотелось заняться тем чего душа давно просит, а именно – снова переписать рендерер. На этот раз с &lt;a href=&quot;https://threejs.org/&quot;&gt;ThreeJS&lt;/a&gt; на &lt;a href=&quot;https://pixijs.com/&quot;&gt;PixiJS&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Я уже давно для себя решил, что движок делается исключительно под 2D иначе мне и на пенсии времени не хватит чтобы привести его в более менее солидное состояние. ThreeJS удобный, есть документация, да и исходники достаточно понятные, но работать с 2D через абстракции разработанные для отрисовки трехмерной графики оказалось избыточным. Кто бы мог подумать.&lt;/p&gt;
&lt;p&gt;Решение использовать ThreeJS было продиктовано желанием пощупать библиотеку, но сейчас, когда я захотел добавить в движок больше возможностей для отрисовки, требуется решить: либо сильнее завязываться на использование ThreeJS, либо использовать более подходящий инструмент пока еще не слишком поздно.&lt;/p&gt;
&lt;p&gt;В планах добавить возможность рендерить геометрические фигуры, текст и &lt;a href=&quot;https://pixijs.com/8.x/guides/components/filters&quot;&gt;фильтры&lt;/a&gt; для пост-обработки объектов и возможности навешивать на акторы кастомные шейдеры. Плюс к этому Pixi уже из коробки содержит множество оптимизаций и при схожем количестве кода для встраивания рендерера в движок я сразу же получил сильный прирост в производительности.&lt;/p&gt;
&lt;p&gt;В этот раз я не буду закрывать библиотеку железным занавесом из фасадов чтобы при желании можно было получить прямой доступ к PixiJS контексту и использовать его возможности в полной мере. Немного опасно если потом я снова (надеюсь нет) решу переписать рендерер, но для небольших игр для гейм джемов – почему бы и нет.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/videos/2025-08-17/1.mp4&quot;&gt;Смотреть видео&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Генерация скриптов через редактор</title><link>https://misharemmele.ru/posts/2025-07-11/generaciya-skriptov-cherez-redaktor/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2025-07-11/generaciya-skriptov-cherez-redaktor/</guid><description>Переходим к последнему этапу по избавлению от бойлерплейта – автоматизируем создание новых скриптов через редактор.</description><pubDate>Fri, 11 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Переходим к последнему этапу по избавлению от бойлерплейта – автоматизируем создание новых скриптов через редактор. Все инструменты для автоматического обнаружения нового кода в редакторе и в игре есть, нужно добавить интерфейс для генерации файлов по заданному шаблону. Поскольку редактор на Electron, то у меня есть доступ к NodeJS, а значит файл создать по клику на кнопку не составляет никакой трудности.&lt;/p&gt;
&lt;p&gt;В меню добавления новых элементов я разместил кнопку, по клику на которую открывается модальное окно, где можно указать имя создаеваемого класса и путь по которому будет создан файл. Путь строится автоматически по имени класса, но при желании его можно указать вручную.&lt;/p&gt;
&lt;p&gt;Файлы создаются по шаблону. Есть дефолтные, но при желании можно указать свои через конфигурационный файл в проекте. Шаблон – это просто функция, которая принимает имя класса, а возвращает строку, поэтому накрутить там можно все что угодно.&lt;/p&gt;
&lt;p&gt;Остается проверить насколько удобнее стала разработка игр, но в целом я не придумал ничего нового и подобные инструменты есть во многих популярных движках. До следующего людума еще есть несколько месяцев, поэтому буду думать чем займусь дальше.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/videos/2025-07-11/1.mp4&quot;&gt;Смотреть видео&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Автоподключение скриптов в редакторе</title><link>https://misharemmele.ru/posts/2025-06-27/avtopodklyuchenie-skriptov-v-redaktore/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2025-06-27/avtopodklyuchenie-skriptov-v-redaktore/</guid><description>Продолжаю работу по автоматизации добавления новых скриптов при разработке игры. В прошлый раз я научил редактор сканировать проект и автоматически подключать новые системы и компоненты. Далее нужно сделать так, чтобы новый код был доступен не только в редакторе, но и в игре без дополнительных импортов.</description><pubDate>Fri, 27 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Продолжаю работу по автоматизации добавления новых скриптов при разработке игры. В прошлый раз я научил редактор сканировать проект и автоматически подключать новые системы и компоненты. Далее нужно сделать так, чтобы новый код был доступен не только в редакторе, но и в игре без дополнительных импортов.&lt;/p&gt;
&lt;p&gt;В редакторе я контролирую процесс сборки, поэтому могу добавлять туда что угодно. Движок же – это просто библиотека, которую можно подключить в любой проект и я пока не хочу завязываться на какой-то конкретный сборщик. Поэтому реализовать такую же автоматизацию как в редакторе для него пока не вышло.&lt;/p&gt;
&lt;p&gt;К счастью, подобный механизм можно реализовать на уровне исходного кода игры, а не движка. К примеру, если игра собирается через Webpack, то можно воспользоваться функцией &lt;a href=&quot;https://webpack.js.org/guides/dependency-management/#requirecontext&quot;&gt;require.context&lt;/a&gt;, которая позволяет по регулярке импортировать все нужные модули и затем передать все что удалось найти в конструктор класса Engine. В Vite для тех же целей есть &lt;a href=&quot;https://vite.dev/guide/features.html#glob-import&quot;&gt;import.meta.glob&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function importAll(context) {
  const modules = context.keys().map(context);

  // ожидаем что система/компонент доступны через дефолтный экспорт в скрипте
  return modules.map((module) =&amp;gt; module.default);
}

const components = importAll(require.context(&apos;./&apos;, true, /.component.ts$/));
const systems = importAll(require.context(&apos;./&apos;, true, /.system.ts$/));

const engine = new Engine({
  components,
  systems
});

engine.play();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Помимо этого, пользуясь &lt;a href=&quot;https://t.me/misha_pishet_dvizhok/110?comment=281&quot;&gt;советом&lt;/a&gt; из комментариев к предыдущему &lt;a href=&quot;https://t.me/misha_pishet_dvizhok/110&quot;&gt;посту&lt;/a&gt;, я доработал редактор и заменил использование нескольких точек входа и поиск по glob паттерну на виртуальный модуль с require.context внутри. Для этого я использовал &lt;a href=&quot;https://www.npmjs.com/package/webpack-virtual-modules&quot;&gt;плагин&lt;/a&gt;, который позволяет создать виртуальный модуль из динамически сгенерированной строки с кодом.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// в упрощенном виде выглядит примерно так:
const virtualModules = new VirtualModulesPlugin({
  &apos;./virtual-entry.ts&apos;: () =&amp;gt; `
  function importAll(r) {
    r.keys().forEach(r);
  }

  importAll(
    require.context(&apos;./&apos;, true, /\.system\.ts$/),
  );
  importAll(
    require.context(&apos;./&apos;, true, /\.component\.ts$/),
  );
  `
});

webpack({
  entry: {
    extension: &apos;./virtual-entry.ts&apos;
  },

  output: {
    libraryTarget: &apos;umd&apos;,
    library: &apos;[name]&apos;
  },

  plugins: [virtualModules]
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Код проекта все еще собирается отдельным скриптом и подключается в редактор как UMD модуль (после загрузки и исполнения скрипта его экспорт можно достать из window). Так сделано, потому что при запуске редактора, Webpack собирает только код игры, а клиентский код редактора бандлится заранее.&lt;/p&gt;
&lt;p&gt;Webpack Dev Server при любых изменениях в коде проекта производит пересборку виртуального модуля и через апи электрона посылает событие в редактор. В ответ на событие, я загружаю скрипт сборки по новой и делаю ререндер панели инспектора. Благодаря этому, все обновления кода систем и компонентов сразу же отображаются в редакторе без обновления страницы и потери состояния интерфейса.&lt;/p&gt;
&lt;p&gt;В следующий раз я расскажу как все это вместе можно использовать, чтобы наконец-то добиться &quot;магии&quot; когда по клику и заполнению пары полей через редактор, в проекте появится шаблонный скрипт с новой системой или компонентом, который сразу же можно использовать без лишних движений.&lt;/p&gt;
</content:encoded></item><item><title>Убираю бойлерплейт в движке</title><link>https://misharemmele.ru/posts/2025-06-08/ubirayu-bojlerplejt-v-dvizhke/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2025-06-08/ubirayu-bojlerplejt-v-dvizhke/</guid><description>Очередная крупная задача, которая долго висела в бэклоге – сократить количество бойлерплейта для создания новых компонентов, систем и других сущностей, которые добавляются и в коде и в редакторе.</description><pubDate>Sun, 08 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Очередная крупная задача, которая долго висела в бэклоге – сократить количество бойлерплейта для создания новых компонентов, систем и других сущностей, которые добавляются и в коде и в редакторе.&lt;/p&gt;
&lt;p&gt;К примеру, для того чтобы добавить очки здоровья для игрового персонажа, нужно создать компонент Health с числовым полем для храненя текущего уровня здоровья:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Health {
  points: number;

  constructor(config) {
    this.points = config.point;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Все компоненты добавляются в акторы через редактор, поэтому нужно чтобы редактор как-то узнал про новый компонент. Для этого нужно в отдельном скрипте описать схему компонента для генерации виджета с необходимыми полями ввода:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const health = {
  title: &apos;Health,
  fields: [
    {
      name: &apos;points&apos;,
      title: &apos;Points&apos;,
      type: &apos;number&apos;,
    },
  ],
  getInitialState: () =&amp;gt; ({
    points: 100,
  }),
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Далее, чтобы игра узнала о новом компоненте, его нужно пробросить в конструктор класса Engine, который представляет собой инстанс игры, а схему нужно экспортировать через отдельную точку входа, которая используется для сбора информации о проекте при старте редактора.&lt;/p&gt;
&lt;p&gt;Наконец, после описанных действий, компонент можно добавить в актор через интерфейс редактора, а в коде систем и скриптов обращаться к нему для реализации боевой системы или любой другой логики.&lt;/p&gt;
&lt;p&gt;Все это ужасно утомляет, особенно во время гейм джемов когда код нужно писать быстро.&lt;/p&gt;
&lt;p&gt;Вдохновляясь существующими движками и редакторами, я начал фантазировать, что было бы здорово для добавления нового компонента, прямо в редакторе нажать на кнопку &quot;Create New&quot;, ввести имя и получить готовый шаблон скрипта в коде проекта, который будет автоматически подключен и в игре и в редакторе, чтобы немедленно начать его использовать.&lt;/p&gt;
&lt;p&gt;Такого поведения вполне можно добиться, но нужно решить несколько задач:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Объединить описание класса и его схему. Напрямую проблему не решает, но сильно ускорит внесение дальнейших изменений&lt;/li&gt;
&lt;li&gt;Научить редактор и движок самостоятельно сканировать проект в поисках систем и компонентов&lt;/li&gt;
&lt;li&gt;По нажатию на кнопку в редакторе, генерировать скрипт из шаблона и складывать в нужную папку&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Для решения первой проблемы я использовал TypeScript декораторы. Декоратор класса позволяет зарегистрировать класс в хранилище к которому обращается редактор для рисования виджетов, а декораторы полей помогают упростить описание схемы виджета, так как имя и тип данных (если он примитивный) можно взять из самого поля и не дублировать эту информацию. В самом простом случае это выглядит следующим образом:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@DefineComponent({
  name: &apos;Health&apos;,
})
class Health {
  @DefineField({ initialValue: 100 })
  points: number;

  constructor(config) {
    this.points = config.points;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Переходим к автоматическому подключению. Для сборки информации о проекте под капотом редактора я использую webpack dev сервер, но теперь вместо статической точки входа, я сканирую определенные каталоги проекта по glob паттерну и выгребаю все что есть. Webpack позволяет указать несколько точек входа и собрать их в один бандл, который затем я подключаю в редакторе.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const widgetEntries = fastGlob.globSync(
  [
    `${config.systemsDir}/**/*.ts`,
    `${config.componentsDir}/**/*.ts`,
    `${config.behaviorsDir}/**/*.ts`
  ],
  { absolute: true }
);

// далее передаем widgetEntries в entry поле webpack конфига
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Благодаря декораторам даже импортировать ничего из этого бандла не нужно, достаточно того, что при подключении выполнится код объявления классов, а с этим и декорирующих функций, которые автоматически зарегистрируют классы.&lt;/p&gt;
&lt;p&gt;На текущий момент я нахожусь на втором шаге релизации задачи. Схему и классы объединил, редактор автоматически искать описание виджетов научил, но теперь нужно поработать над тем как все это использовать в коде игры. Вместе с декораторами при сборки игры в бандл полезло много всего лишнего и игра даже перестала запускаться. Так что работы еще много, но уже есть определенные успехи, которыми можно поделиться.&lt;/p&gt;
</content:encoded></item><item><title>Рефакторинг жизненного цикла сцен</title><link>https://misharemmele.ru/posts/2025-05-13/refaktoring-zhiznennogo-cikla-scen/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2025-05-13/refaktoring-zhiznennogo-cikla-scen/</guid><description>Последний месяц потратил на переписывание жизненного цикла сцен и систем в движке. Давно хотел это сделать, убрать лишние сущности и сделать интерфейс более понятным.</description><pubDate>Tue, 13 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Последний месяц потратил на переписывание жизненного цикла сцен и систем в движке. Давно хотел это сделать, убрать лишние сущности и сделать интерфейс более понятным.&lt;/p&gt;
&lt;h2&gt;Какие были проблемы&lt;/h2&gt;
&lt;p&gt;Системы были привязаны к сценам и уничтожались при их смене. Это не позволяло пошарить состояние между разными экранами игры и за неимением других возможностей я складывал данные в &lt;code&gt;window&lt;/code&gt; или &lt;code&gt;local storage&lt;/code&gt;. К примеру, в новогоднем мини-квесте я так хранил инвентарь с предметами.&lt;/p&gt;
&lt;p&gt;Помимо сцен были еще уровни. Так как системы с логикой игры хранились в сценах, я не хотел хранить там же и акторы. Иначе, при создании многоуровневой игры, мне бы пришлось дублировать системы в каждую сцену и при добавлении новой системы, не забывать все обновлять.&lt;/p&gt;
&lt;p&gt;Короче говоря, интерфейс был громоздким, непонятным, а возможностей недостаточно.&lt;/p&gt;
&lt;h2&gt;Что сделал&lt;/h2&gt;
&lt;p&gt;Чтобы решить все проблемы разом я:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Вытащил системы на общий уровень&lt;/li&gt;
&lt;li&gt;Списки акторов вернул обратно в сцены&lt;/li&gt;
&lt;li&gt;Уровни удалил&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Системы разделил на два типа: &lt;code&gt;WorldSystem&lt;/code&gt; и &lt;code&gt;SceneSystem&lt;/code&gt;. Первые являются синглтонами, которые сохраняют состояние при переходах между сценами, а вторые инстанцируются под каждую сцену отдельно.&lt;/p&gt;
&lt;h2&gt;Как следить за изменениями&lt;/h2&gt;
&lt;p&gt;Для отслеживания переходов между сценами я добавил методы:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  onSceneLoad(scene: Scene): Promise&amp;lt;void&amp;gt; // загрузка ресурсов: текстуры, звуки и тд.
  onSceneEnter(scene: Scene): void // активация сцены
  onSceneExit(scene: Scene): void // выход со сцены, без уничтожения
  onSceneDestroy(scene: Scene): void // сцена полностью уничтожается
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Для &lt;code&gt;World&lt;/code&gt; систем есть еще пара методов:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  onWorldLoad(world: World): Promise&amp;lt;void&amp;gt; // загрузка глобальных ресурсов. К примеру, бандл с интерфейсом игры.
  onWorldReady(world: World): void // глобальные ресурсы загружены
  onWorldDestroy(world: World): void // полное завершение игры

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;World&lt;/code&gt; – это новый элемент, который является родительским узлом для сцен. Он не меняется на протяжении игры. Удобно для глобальных обработчиков событий, поскольку все игровые события вызванные на акторах и сценах всплывают наверх.&lt;/p&gt;
&lt;h2&gt;Как управлять переходами&lt;/h2&gt;
&lt;p&gt;Переходы между сценами, их загрузка и удаление управляются через отправку событий:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type LoadSceneEvent = WorldEvent&amp;lt;{
  id: string;
  autoEnter?: boolean;
  autoDestroy?: boolean;
}&amp;gt;;
type EnterSceneEvent = WorldEvent&amp;lt;{
  id: string;
  autoDestroy?: boolean;
}&amp;gt;;
type ExitSceneEvent = WorldEvent&amp;lt;{
  autoDestroy?: boolean;
}&amp;gt;;
type DestroySceneEvent = WorldEvent&amp;lt;{ id: string }&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Флаги &lt;code&gt;autoEnter&lt;/code&gt; и &lt;code&gt;autoDestroy&lt;/code&gt; для автоматического перехода или уничтожения сцен. По умолчанию, оба в &lt;code&gt;true&lt;/code&gt;. Если выключить &lt;code&gt;autoEnter&lt;/code&gt;, то сцена загрузится, но перехода не произойдет. Полезно если потребуется сцены предзагружать заранее.&lt;/p&gt;
&lt;h2&gt;Зачем я это делал&lt;/h2&gt;
&lt;p&gt;Проектирование API библиотеки для меня – это одна из самых сложных задач. При написании бизнес логики, главное чтобы работало так как хочет заказчик, а на удобство использования кода можно хоть и не всегда, но закрыть глаза. Ну не будет никто эту функцию использовать кроме тебя, зато работает. При разработке библиотеки, ее API – это и есть продукт, а заказчики – это те, кто будут им пользоваться. Или не будут если оно будет сложным и неудобным.&lt;/p&gt;
</content:encoded></item><item><title>Результаты Ludum Dare 57</title><link>https://misharemmele.ru/posts/2025-04-30/rezultaty-ludum-dare-57/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2025-04-30/rezultaty-ludum-dare-57/</guid><description>Опубликовали результаты прошедшего людума, 268 место в общем зачете из 1181 игры. В прошлом году были 700 и 400 места, поэтому прогресс есть.</description><pubDate>Wed, 30 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Опубликовали результаты прошедшего людума, 268 место в общем зачете из 1181 игры. В прошлом году были 700 и 400 места, поэтому прогресс есть. В категории Fun получилось попасть в топ-100, что для меня является самой важной категорией. Закономерностью это пока не назовешь, но будем и дальше работать над улучшением результата, пусть и не в этих циферках, а хотя бы в своих собственных глазах.&lt;/p&gt;
&lt;p&gt;Для того чтобы в твою игру поиграли после публикации, нужно оценивать чужие игры. Всего я поиграл в 42 игры и на каждую старался писать фидбек.&lt;/p&gt;
&lt;p&gt;При оценке чужих игр понимаешь насколько важно чтобы управление было интуитивным, а игра раскрывалась в первые минуты. Иначе ты сильно рискуешь потерять потенциального игрока, который задушится уже на главном меню и с отзывом &quot;Very nice art! Great jam entry!&quot; закроет вкладку и пойдет дальше.&lt;/p&gt;
&lt;p&gt;Среди игр, которые мне довелось посмотреть, было немало классных проектов. Отдельно я бы отметил &lt;a href=&quot;https://ldjam.com/events/ludum-dare/57/the-void&quot;&gt;The Void&lt;/a&gt; и &lt;a href=&quot;https://ldjam.com/events/ludum-dare/57/scavenger-deep&quot;&gt;Scavenger Deep&lt;/a&gt; за супер крутой пиксель арт и общую атмосферность.&lt;/p&gt;
&lt;p&gt;Еще одна игра под названием &lt;a href=&quot;https://ldjam.com/events/ludum-dare/57/podvodsk&quot;&gt;Podvodsk&lt;/a&gt; привлекла мое внимание знакомым графическим стилем. Как оказалось, эти же ребята 5 лет назад сделали на людуме &lt;a href=&quot;https://ldjam.com/events/ludum-dare/45/loopathero&quot;&gt;LooPatHero&lt;/a&gt;, которая в последствие была доработана и переименована в &lt;a href=&quot;https://store.steampowered.com/app/1282730/Loop_Hero/&quot;&gt;LoopHero&lt;/a&gt;. Ее достаточно высоко оценили многие игровые журналы и она была номинирована на звание лучшей инди игры на &lt;a href=&quot;https://thegameawards.com/&quot;&gt;VGA&lt;/a&gt; в 2021 году.&lt;/p&gt;
&lt;p&gt;&amp;lt;img
src=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/images/2025-04-30/1.avif&quot;
alt=&quot;Результаты голосования&quot;&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
</content:encoded><enclosure url="https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/images/2025-04-30/1.avif" length="0" type="image/avif"/></item><item><title>Сходил на подкаст Xufostation</title><link>https://misharemmele.ru/posts/2025-04-28/shodil-na-podkast-xufostation/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2025-04-28/shodil-na-podkast-xufostation/</guid><description>Сходил на подкаст к @xufostation. Поговорили про то, чем я занимаюсь, с чего все начиналось, про геймджеймы и про геймдев в целом.</description><pubDate>Mon, 28 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Сходил на подкаст к @xufostation. Поговорили про то, чем я занимаюсь, с чего все начиналось, про геймджеймы и про геймдев в целом.&lt;/p&gt;
&lt;p&gt;Послушать можно здесь: https://t.me/xufostation/992.&lt;/p&gt;
</content:encoded></item><item><title>Подводим итоги Ludum Dare 57</title><link>https://misharemmele.ru/posts/2025-04-13/podvodim-itogi-ludum-dare-57/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2025-04-13/podvodim-itogi-ludum-dare-57/</guid><description>Ну что ж, немного передохнул — пора подводить итоги прошедшего гейм-джема. В целом, стало заметно удобнее по сравнению с прошлым Людумом. Помогли и доработки в редакторе по части горячих клавиш и мультивыделения, и новый шаблон проекта с уже заготовленными системами передвижения и боёвки.</description><pubDate>Sun, 13 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Ну что ж, немного передохнул — пора подводить итоги прошедшего гейм-джема.&lt;/p&gt;
&lt;p&gt;В целом, стало заметно удобнее по сравнению с прошлым Людумом. Помогли и доработки в редакторе по части горячих клавиш и мультивыделения, и новый шаблон проекта с уже заготовленными системами передвижения и боёвки.&lt;/p&gt;
&lt;p&gt;Из больших плюсов — аудиосопровождение. Благодаря ему игра воспринимается совершенно иначе, куда атмосфернее. При этом реализация занимает не так много времени: если не писать музыку и звуки самостоятельно, а взять готовые, то всё можно собрать за полтора - два часа. У меня так и вышло.&lt;/p&gt;
&lt;p&gt;Но, конечно, есть куда расти. На подсчёт очков, меню выбора уровня и сохранение прогресса я потратил часа три. Хотя вся эта логика достаточно универсальная и к геймплею напрямую не относится — её вполне можно было держать заготовленной заранее.&lt;/p&gt;
&lt;p&gt;Ну и, как и в прошлый раз, движок всё ещё требует много boilerplate кода на каждую систему или компонент — это тормозит.&lt;/p&gt;
&lt;p&gt;По части рендеринга — не хватает гибкости. Например, нельзя писать свои шейдеры. Сейчас Three.js в движке закрыт фасадом, интерфейс у него полностью декларативный. Нельзя отрисовать ничего, кроме статичных картинок. А у меня как раз была идея добавить задникам эффект легкой вибрации, чтобы подчеркнуть, что действие происходит под водой.&lt;/p&gt;
&lt;p&gt;Совсем не жалею, что начал регулярно участвовать в Людумах. Чтобы развивать инструменты в правильном направлении — ими нужно пользоваться. Да и просто приятно иметь в коллекции ещё одну завершённую игру, пусть и небольшую.&lt;/p&gt;
&lt;p&gt;PS: На скриншоте — как игра выглядит в редакторе. Красные прямоугольники — это те самые края-убийцы, а фиолетовая полоса слева убивает всех недобитых противников для очистки ресурсов.&lt;/p&gt;
&lt;p&gt;&amp;lt;img
src=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/images/2025-04-13/1.avif&quot;
alt=&quot;Скриншот игры в редакторе&quot;&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
</content:encoded><enclosure url="https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/images/2025-04-13/1.avif" length="0" type="image/avif"/></item><item><title>Финал Ludum Dare 57</title><link>https://misharemmele.ru/posts/2025-04-08/final-ludum-dare-57/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2025-04-08/final-ludum-dare-57/</guid><description>Закончился третий день. Игру доделал и загрузил на сайт людума. До дедлайна оставалось два часа, уложился прям как раз. Нарисовал графику, настроил звуки, которые скачал с freesound и собрал два уровня. Сделал не все что планировал, как обычно хотелок было очень много.</description><pubDate>Tue, 08 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Закончился третий день. Игру доделал и &lt;a href=&quot;https://ldjam.com/events/ludum-dare/57/piranha-frenzy&quot;&gt;загрузил&lt;/a&gt; на сайт людума. До дедлайна оставалось два часа, уложился прям как раз.&lt;/p&gt;
&lt;p&gt;Нарисовал графику, настроил звуки, которые скачал с freesound.org и собрал два уровня. Сделал не все что планировал, как обычно хотелок было очень много. Каждый день приходило очередное осознание, что успею сделать, а что нет, поэтому резал как мог.&lt;/p&gt;
&lt;p&gt;В целом результатом доволен.&lt;/p&gt;
&lt;p&gt;Поиграть можно тут: https://ludum-dare-57.netlify.app/
Исходники тут: https://github.com/michailRemmele/ludum-dare-57&lt;/p&gt;
&lt;p&gt;Подробнее о впечатлениях и результатах напишу позже.&lt;/p&gt;
&lt;p&gt;PS: На телефоне игра работает, но текущий вариант управления не очень удобный и я не смог побороть выделение текста по долгому нажатию на айфонах. Свойство &quot;user-select: none&quot; не помогло, возможно как обычно приколы Safari.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/videos/2025-04-08/1.mp4&quot;&gt;Смотреть видео&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Второй день Ludum Dare 57</title><link>https://misharemmele.ru/posts/2025-04-07/vtoroj-den-ludum-dare-57/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2025-04-07/vtoroj-den-ludum-dare-57/</guid><description>Добавил больше разновидностей противников, уровни и подсчет очков.</description><pubDate>Mon, 07 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Второй день.&lt;/p&gt;
&lt;p&gt;Добавил стреляющих противников. Есть стационарные, которые спавнятся при приближении и стреляют очередями. Есть плавающие, которые после того как попадут в поле зрения, двигаются со скоростью скролла камеры, поэтому просто так от них избавиться не получится. Дополнительные пираньи теперь выдаются не просто так, а в награду за получение уровня. Уровень можно набить поедая других рыб. Также, добавил подсчет очков с сохранением прогресса. Информация складывается в local storage и при ее наличии, в меню вместо кнопки старта будет кнопка выбора уровня.&lt;/p&gt;
&lt;p&gt;Порисовать успел всего часок. Добавил спрайт пираньи с анимациями атаки, получения урона и смерти. Гневное рыло рыбы перед атакой срисовал со своей собаки. У меня корги если что.&lt;/p&gt;
&lt;p&gt;Вечерние часы проходят не очень продуктивно, к концу дня голова начинает плыть и я могу долго залипать в одно и то же место. С утра до ночи код писать тяжко.&lt;/p&gt;
&lt;p&gt;Завтра последний день. Буду преимущественно рисовать арт и собирать уровни. Ну и дополнительные механики попробую добавить если успею.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/videos/2025-04-07/1.mp4&quot;&gt;Смотреть видео&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Первый день Ludum Dare 57</title><link>https://misharemmele.ru/posts/2025-04-05/pervyj-den-ludum-dare-57/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2025-04-05/pervyj-den-ludum-dare-57/</guid><description>Закончился первый день людума. Тема на этот раз – Depths. Я хотел сделать сайд-скроллер, поэтому, недолго думая, решил, что игра будет про рыб – чтобы под тему подходило.</description><pubDate>Sat, 05 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Закончился первый день людума. Тема на этот раз – Depths.&lt;/p&gt;
&lt;p&gt;Я хотел сделать сайд-скроллер, поэтому, недолго думая, решил, что игра будет про рыб – чтобы под тему подходило. Сейчас на рыб и море это не особо похоже, только если сильно напрячь воображение. Но фон вот синий, например.&lt;/p&gt;
&lt;p&gt;Игрок управляет косяком пираний, которыми нужно жрать других рыб. Вместо привычных для сайд-скроллера улучшений огневой мощи можно пополнять отряд новыми пираньями. В зависимости от их количества меняется построение.&lt;/p&gt;
&lt;p&gt;За сегодняшний день сделал логику скролла экрана с ограничениями по краям. Если выйти за пределы экрана, то сначала нанесется предупреждающий урон, а затем все заплутавшие рыбы будут уничтожены. Пираньи при контакте с другими рыбами начинают кусаться с некоторым кулдауном. Также добавил спавнеры рыб, в которых можно указать, кого, сколько и с какой частотой нужно создать, и по какой траектории направить. Есть рыбы, которые просто плывут и терпят, а есть рыба-меч, которая наносит урон если подъехать к ней не с той стороны.&lt;/p&gt;
&lt;p&gt;Завтра планирую заниматься добавлением большего количества разновидностей врагов с разными атаками. Возможно, попробую добавить улучшения, для точечного усиления пираний. Ну и, если останется время, начну рисовать графику.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/videos/2025-04-05/1.mp4&quot;&gt;Смотреть видео&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Подготовка к Ludum Dare 57</title><link>https://misharemmele.ru/posts/2025-04-04/podgotovka-k-ludum-dare-57/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2025-04-04/podgotovka-k-ludum-dare-57/</guid><description>Завтра пройдет очередной Ludum Dare. Это всемирный челлендж, когда за несколько дней нужно написать игру на определеную тему. Я учел проблемы прошлого людума и поменял стартовый шаблон проекта.</description><pubDate>Fri, 04 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Завтра пройдет очередной &lt;a href=&quot;https://ldjam.com/&quot;&gt;Ludum Dare&lt;/a&gt;. Это всемирный челлендж, когда за несколько дней нужно написать игру на определеную тему. Тема объявляется в начале эвента.&lt;/p&gt;
&lt;p&gt;Есть разные форматы участия: можно соло, можно в команде. В базовом варианте есть три дня на написание игры, при этом ограничений на количество участников и первоначальную кодовую базу нет. Я в этот раз пойду в соло.&lt;/p&gt;
&lt;p&gt;Учел проблемы &lt;a href=&quot;https://t.me/misha_pishet_dvizhok/91&quot;&gt;прошлого людума&lt;/a&gt; и поменял стартовый шаблон проекта. От &lt;a href=&quot;https://t.me/misha_pishet_dvizhok/87&quot;&gt;заготовки в виде змейки&lt;/a&gt; не было никакого толку, потому что игровая логика слишком специфичная и пришлось тратить время на ее выпиливание.&lt;/p&gt;
&lt;p&gt;В новый шаблон я запихал все системы, которые ранее писал так, чтобы их можно было переиспользовать: перемещение персонажа, боевая система, управление камерой, параллакс и тд. При таких жестких ограничениях по времени нет смысла писать каждый раз базовые механики повторно.&lt;/p&gt;
&lt;p&gt;В дополнение к этому я добавил инструмент для работы с гугл аналитикой. Хочу разметить игру кастомными событиями чтобы понимать как вообще в игру играют: пройдут ли тот или иной уровень, смогут ли использовать какой-то предмет и так далее&lt;/p&gt;
&lt;p&gt;Поглядим что получится в этот раз, буду делиться успехами по ходу работы.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/videos/2025-04-04/1.mp4&quot;&gt;Смотреть видео&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Система звука</title><link>https://misharemmele.ru/posts/2025-03-21/sistema-zvuka/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2025-03-21/sistema-zvuka/</guid><description>Пора возвращаться к работе над звуковой системой. Для работы со звуком в браузерах существует Web Audio API. Апи, как оказалось, уже из коробки умеет многое. Похоже, даже изобретать ничего не придется – достаточно написать удобный фасад, чтобы интегрировать звук в движок и редактор.</description><pubDate>Fri, 21 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Ну чтож, передохнул – пора возвращаться к работе над звуковой системой. Для работы со звуком в браузерах существует &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API&quot;&gt;Web Audio API&lt;/a&gt;. Апи, как оказалось, уже из коробки умеет многое: есть пространственный звук, фильтры и опция выноса звука в отдельный воркер. Похоже, даже изобретать ничего не придется – достаточно написать удобный фасад, чтобы интегрировать звук в движок и редактор.&lt;/p&gt;
&lt;p&gt;Начал с самого простого: воспроизведения звуков, привязанных к акторам в качестве компонентов. Звук можно запускать через отправку событий или автоматически при загрузке уровня. Пока без позиционирования и эффектов – звуки проигрываются как есть.&lt;/p&gt;
&lt;p&gt;Работа с Web Audio API сводится к составлению графа, где узлы – это источники звука, эффекты (например, фильтрация или изменения громкости) и выходной узел куда подается итоговый микс. Все довольно удобно и логично. Документация на MDN подробная, есть примеры на GitHub. Огорчает разве что отсутствие отладочных инструментов на уровне браузеров. В Chrome они доступны только в виде расширений, а в Firefox вкладка в дев тулзах для отладки когда-то была, но, похоже ее уже убрали. Из-за этого я испытываю некоторую тревогу: справляется ли сборщик мусора с отработанными звуками или нет.&lt;/p&gt;
&lt;p&gt;Собрал небольшую демку на базе последней игры, которой я занимался. Поскольку в работе с аудио я полный профан, было довольно тяжело подобрать подходящие звуки, особенно музыку. В итоге трек сгенерировал через &lt;a href=&quot;https://suno.com/&quot;&gt;Suno AI&lt;/a&gt;, а остальные звуки взял с &lt;a href=&quot;https://freesound.org/&quot;&gt;freesound.org&lt;/a&gt;. Поэтому впервые на канале видос к посту со звуком. Предыдущие видео, где я случайно забыл отключить запись микрофона и дышал в него пока делал запись экрана – не в счет.&lt;/p&gt;
&lt;p&gt;Пока я прокрастинировал работу над движком и устанавливал все игры, незаметно подкралась дата апрельского Ludum Dare. Планирую участвовать и, возможно, даже успею довести звук до рабочего состояния, чтобы использовать его на джеме.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/videos/2025-03-21/1.mp4&quot;&gt;Смотреть видео&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Про переводы книг по геймдеву</title><link>https://misharemmele.ru/posts/2025-02-05/pro-perevody-knig-po-gejmdevu/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2025-02-05/pro-perevody-knig-po-gejmdevu/</guid><description>Пока пробираюсь через главу о работе со звуком в книге Game Engine Architecture, хочу поделиться своим недовольством по поводу качества перевода литературы из области геймдева и IT в целом.</description><pubDate>Wed, 05 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Пока пробираюсь через главу о работе со звуком в книге &lt;em&gt;Game Engine Architecture&lt;/em&gt;, хочу поделиться своим недовольством по поводу качества перевода литературы из области геймдева и IT в целом.&lt;/p&gt;
&lt;p&gt;Около года назад я начал читать книги на английском. После занятий с репетитором язык стал лучше, да и надоело постоянно ждать перевода. Сначала было тяжело, но как только разрешаешь себе не понимать некоторых слов и не лезть в словарь каждую секунду, жить становится намного легче.&lt;/p&gt;
&lt;p&gt;Недавно я зашел в книжный поглазеть на обложки и удивился, как много книг появилось по игровой тематике. В большинстве случаев издателем является &lt;em&gt;Бомбора&lt;/em&gt; – та самая, что занималась переизданием &lt;em&gt;Кровь, пот и пиксели&lt;/em&gt; Шрайера после шквала критики первоначального перевода. Второй вариант я читал и мне он понравился, так что, решив, что издательство заботится о качестве, я купил у них &lt;em&gt;Пиксели! Курс по пиксельной графике и анимации для новичков&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;С первых страниц что-то было не так, чем дальше я шел, тем сильнее сомневался в качестве книги. В итоге, как это бывает, когда мне что-то не нравится, я пошел в интернет читать негативные отзывы.&lt;/p&gt;
&lt;p&gt;Конкретно на эту книгу я плохих рецензий не нашел, но зато наткнулся на &lt;a href=&quot;https://habr.com/ru/articles/757204/&quot;&gt;статью на Хабре,&lt;/a&gt; где автор разбирает ошибки перевода другой книги от &lt;em&gt;Бомборы&lt;/em&gt;, а также приводит ряд примеров других книг с аналогичными проблемами.&lt;/p&gt;
&lt;p&gt;Чтобы не быть голословным, вот типичный абзац, который оставил меня в полном недоумении:&lt;/p&gt;
&lt;p&gt;Оригинал:
&quot;Add some simple shading details in the clouds and grass. You can add more details if you prefer but backgrounds are often looped seamlessly so adding more detail can often catch the viewers eye and take away from the entire experience by feeling repetetive.&quot;&lt;/p&gt;
&lt;p&gt;Перевод:
&quot;Нанесите тени на облака и траву. Художники часто обводят фоны плавно, поэтому дополнительные детали могут привлекать внимание зрителя и перебивать общее впечатление, поскольку элементы пейзажа будут казаться повторяющимися.&quot;&lt;/p&gt;
&lt;p&gt;О какой обводке идет речь – мне так и не стало понятно.&lt;/p&gt;
&lt;p&gt;В очередной раз убедился: если есть возможность, книги лучше читать в оригинале. А книги от этого издателя я теперь точно буду обходить стороной.&lt;/p&gt;
</content:encoded></item><item><title>Копировать, вырезать и вставить</title><link>https://misharemmele.ru/posts/2025-02-02/kopirovat-vyrezat-i-vstavit/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2025-02-02/kopirovat-vyrezat-i-vstavit/</guid><description>Январь позади, а я уже закончил первую большую задачу – добавил операции: копировать, вырезать и вставить. Теперь можно скопировать или перенести выбранные элементы в произвольное место.</description><pubDate>Sun, 02 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Январь позади, а я уже закончил первую большую задачу – добавил операции: копировать, вырезать и вставить. Теперь можно скопировать или перенести выбранные элементы в произвольное место. Ранее операции совершались только над одним элементом за раз, а возможности переноса не было совсем, только создание дубликата на том же месте.&lt;/p&gt;
&lt;p&gt;Пришлось перелопатить кучу кода. Дала о себе знать проблема, что разные сущности в конфиге игры представлены по-разному, а также у некоторых элементов есть побочные эффекты при изменении. Например, при удалении шаблона нужно также удалить все наследуемые от него акторы. Акторы можно вкладывать друг в друга, но сцены и уровни – нет. Поэтому базовый каркас для работы с буфером обмена и горячими клавишами общий, но обработчики пришлось писать отдельно для каждого списка.&lt;/p&gt;
&lt;p&gt;И это далеко не все проблемы с которыми я столкнулся. При вставке в место, где уже есть элемент с таким именем, нужно генерировать новое имя для копии. Для этого пришлось перебирать все элементы в точке назначения, чтобы исключить возможность совпадения. А при множественном переносе нужно учесть, что по нажатию на ctrl+Z все должно вернуться на свои места.&lt;/p&gt;
&lt;p&gt;Очередное улучшение сделало редактор значительно удобнее. Всего на год я запланировал как минимум 4 задачи для движка и редактора, которые я посчитал самыми важными. Следующая на очереди – звук.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/videos/2025-02-02/1.mp4&quot;&gt;Смотреть видео&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Мультивыбор элементов в проводнике</title><link>https://misharemmele.ru/posts/2025-01-10/multivybor-elementov-v-provodnike/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2025-01-10/multivybor-elementov-v-provodnike/</guid><description>Сейчас при создании уровня нельзя выбрать более одного актора. Если нужно передвинуть группу объектов на сцене или удалить лишние, то делать это придется долго, редактируя один элемент за другим. Нужно добавить возможность выбирать несколько сразу.</description><pubDate>Fri, 10 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;В новом году я решил пересмотреть приоритеты и делать не то что нравится, а то что наиболее важно для комфортной работы с движком и редактором. К физике вернусь позже, когда решу критичные задачи.&lt;/p&gt;
&lt;p&gt;Начал с задачи попроще – в редакторе. Сейчас при создании уровня нельзя выбрать более одного актора. Если нужно передвинуть группу объектов на сцене или удалить лишние, то делать это придется долго, редактируя один элемент за другим. Нужно добавить возможность выбирать несколько сразу.&lt;/p&gt;
&lt;p&gt;Компонент дерева из библиотеки Ant Design поддерживает мультивыделение, но пришлось отрефакторить код вокруг него, поскольку раньше редактор был заточен под работу только с одним выбранным элементом. А вот для окна просмотра уровня из коробки ничего нет, пришлось писать самостоятельно. Добавил выделение рамкой и по клику мыши с зажатой клавишей Shift. Как оказалось в нативных событиях мыши есть такие поля как shiftKey, altKey и тд. Удобно.&lt;/p&gt;
&lt;p&gt;Когда я показал доработки своей жене, она сначала не поняла что изменилось. Функции выделения настолько привычны в WYSIWYG-редакторах, что их даже не замечаешь. Это ожидается по умолчанию.&lt;/p&gt;
&lt;p&gt;На текущий момент, я добавил только возможность выделения и перемещения выборки по уровню. Дальше буду работать над удалением, копированием и перемещением элементов списка с уровня на уровень и внутрь друг друга через Drag-and-Drop.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/videos/2025-01-10/1.mp4&quot;&gt;Смотреть видео&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Как я делал игру на новогодний конкурс</title><link>https://misharemmele.ru/posts/2024-12-31/kak-ya-delal-igru-na-novogodnij-konkurs/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2024-12-31/kak-ya-delal-igru-na-novogodnij-konkurs/</guid><description>История о том, как за месяц я собрал квест-игру для новогоднего стримерского конкурса: от идеи и механик до релиза и неожиданного показа на стриме.</description><pubDate>Tue, 31 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Идея&lt;/h2&gt;
&lt;p&gt;Я люблю смотреть игровые стримы. Обычно я смотрю их на фоне когда работаю если задача не требует глубокой концентрации или когда занимаюсь своими пет проектами, чтобы сделать процесс более непринужденным и внушить себе, что я уже отдыхаю, а не работаю. Смотрю буквально пару человек, чаще всего стримера с ником WELOVEGAMES, а зовут его Денис.&lt;/p&gt;
&lt;p&gt;В конце ноября на одном из стримов Дениса я услышал, что он планирует устроить несколько новогодних конкурсов. Один из них - конкурс рисунков по новогодней тематике с отсылками на канал стримера. Я сразу подумал, что было бы здорово сделать вместо рисунка небольшую игру. Пусть она и пойдет мимо конкурса, зато примажусь к общему мероприятию, да и в игру поиграют, может быть даже прямо на стриме.&lt;/p&gt;
&lt;p&gt;Я решил сделать квест в котором стримеру нужно подготовиться к новогодней трансляции: нарядить елку, повесить гирлянду и возможно запастить едой. В качестве сеттинга я выбрал пост-апокалиптическое убежище из игр серии Fallout, поскольку Денис является давним поклонником изометрических игр этой серии.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/images/2024-12-31/2.avif&quot; alt=&quot;Fallout 1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Fallout 1&lt;/p&gt;
&lt;h2&gt;Планируем фронт работ&lt;/h2&gt;
&lt;p&gt;На разработку у меня был месяц, нужно было реализовать основные механики, задизайнить игровой мир и наполнить его задачами. Ну и конечно же нужно много рисовать: персонажа, предметы, помещения, декорации и интерфейс.&lt;/p&gt;
&lt;p&gt;На очередной прогулке с собакой я составил следующий список задач:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;перемещение персонажа&lt;/li&gt;
&lt;li&gt;возможность собирать и использовать предметы, инвентарь&lt;/li&gt;
&lt;li&gt;сохранение прогресса при перезагрузке страницы или переходах между уровнями&lt;/li&gt;
&lt;li&gt;цепочки квестов, которые нужно выполнить в рамках игры&lt;/li&gt;
&lt;li&gt;рисовать так много сколько успею&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Когда план оказался перед глазами, я сильно запереживал, что времени у меня не хватит. Могло получиться так, что игра к дедлайну будет на половину недоделанной и такое будет стыдно показывать, но желание испытать себя было сильнее, поэтому я принялся за работу. Использовал каждый вечер после работы и все свободное время на выходных.&lt;/p&gt;
&lt;h2&gt;Первые шаги&lt;/h2&gt;
&lt;p&gt;Я постарался наворовать как можно больше кода с моих предыдущих игр, но ничего кроме общей заготовки проекта и системы перемещения взять не удалось. Почти всю логику пришлось писать с нуля.&lt;/p&gt;
&lt;p&gt;Взаимодействие с объектами сцены я реализовал через коллизии. Отслеживая столкновения курсора и интерактивных объектов, я проверяю наличие компонента Interactable и смотрю на поле с действием, которое этот объект предполагает. Зная контекст, по клику мыши я отправляю соответствующее событие: переход в другую комнату, поднятие предмета и так далее.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/images/2024-12-31/3.avif&quot; alt=&quot;Демо уровень&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Персонажа временно взял из другой своей игры, остальное - заглушки в виде цветных прямоугольников и кружков&lt;/p&gt;
&lt;p&gt;Чтобы подсказывать игроку, какое действие сейчас можно выполнить на клик, я планировал нарисовать разные варианты курсоров: руку чтобы подобрать предмет, стрелку или ботинок для перехода на другой экран, и так далее. В качестве временной заглушки я нарисовал одну пиксельную стрелку и покрасил ее в разные цвета. Забегая вперед, курсоры я нарисовать не успел, поэтому заглушка так и осталась в финальной версии.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/images/2024-12-31/4.avif&quot; alt=&quot;Курсор&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Курсор&lt;/p&gt;
&lt;h2&gt;Добавляем инвентарь&lt;/h2&gt;
&lt;p&gt;Для инвентаря я решил не делать отдельных модальных окон или выпадающих меню. Комнаты я планировал рисовать небольшие, поэтому на экране должно быть достаточно места чтобы поместилась панель инвентаря с журналом событий. Получится некая смесь интерфейса из Fallout и Братья Пилоты: по следам полосатого слона.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/images/2024-12-31/5.avif&quot; alt=&quot;Братья Пилоты&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Братья Пилоты&lt;/p&gt;
&lt;p&gt;Для разных размеров экрана я предусмотрел разное количество ячеек для инвентаря, а для случаев когда предметов больше чем ячеек, есть стрелки &quot;вперед&quot;-&quot;назад&quot;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/images/2024-12-31/6.avif&quot; alt=&quot;Инвентарь&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Добавил инвентарь и журнал событий, чтобы было где отобразить название предметов&lt;/p&gt;
&lt;p&gt;Я до сих пор не привык к тому, что большую часть времени, разрабатываемая игра выглядит плохо. Так как я сначала пишу логику и геймплей, а только потом берусь за рисование графики, игра выглядит как набор цветных прямоугольников и нужно сильно постараться чтобы видеть в этом то, что задумано.&lt;/p&gt;
&lt;h2&gt;Переходы между комнатами и сохранение прогресса&lt;/h2&gt;
&lt;p&gt;Комнаты я сделал отдельными уровнями, поэтому за раз на экране отображается только один, а переходы происходят по клику на дверь. Чтобы переходы не были такими резкими, добавил плавное затемнение экрана. Никаких красивых переходов между сценами у меня в движке нет, поэтому затемнение это обычный div с разными классами состояниями.&lt;/p&gt;
&lt;p&gt;При переходе с уровня на уровень, движок не сохраняет своего состояния и все системы полностью сбрасываются. Это проблема, которую нужно будет решать отдельно, но а сейчас за неимением других опций я сложил все в один объект в window, c последующим отложенным сохранением в Local Storage. Туда попали последние координаты персонажа, список всех предметов и их текущих состояний, чтобы при перезапуске игры можно было восстановить последнее состояние. Сохранение происходит в определенным контрольных точках когда происходит хоть сколько-нибудь значимое событие: успешное взаимодействие с предметами или переход в другую комнату.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/videos/2024-12-31/1.mp4&quot;&gt;Смотреть видео&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Переход между комнатами&lt;/p&gt;
&lt;h2&gt;Квесты&lt;/h2&gt;
&lt;p&gt;В качестве основных задач игры я выбрал три: собрать все новогодние игрушки и нарядить елку, повесить и зажечь гирлянду, и найти себе поесть.&lt;/p&gt;
&lt;p&gt;Поиск игрушек это задача на исследование всех комнат, сбор предметов и последующее их применение на елке. Для разнообразия, я добавил объекты контейнеры, только открыв которые, можно собрать содержимое. Некоторые открываются просто так, некоторые требуют соблюдения определенных условий. К примеру, деревянный ящик можно открыть только после использования на нем монтировки. Содержимое контейнеров заранее добавлено во все уровни и расставлено по позициям, а их скрытие реализовано посредством привязки к текущему состояния контейнера.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/videos/2024-12-31/2.mp4&quot;&gt;Смотреть видео&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Сбор предметов&lt;/p&gt;
&lt;p&gt;Квест гирлянды состоит из нескольких этапов. Сначала ее нужно найти, затем повесить и попробовать включить. Первая попытка оказывается провальной и в результате выбивает пробки. Далее нужно найти щиток и пройти мини-игру, выполненную в стиле старой ПК игры про Гарри Поттера. По клику на щиток открывается отдельная сцена на которой отображается контур в виде молнии, который игрок должен успеть достаточно ровно обвести за ограниченное время. Контур состоит из множества круглых коллайдеров с помощью которых отслеживается точность обводки.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/videos/2024-12-31/3.mp4&quot;&gt;Смотреть видео&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Нарисованный контур отображается на отдельном холсте канваса, решение буквально в несколько строчек&lt;/p&gt;
&lt;p&gt;Последняя задача - достать еду. Ее я также выполнил в стиле отдельной мини-игры, где игрок должен вовремя перепрыгивать препятствия. Для активации игры, нужно найти аркадный автомат производства известной в пустоши доставки пиццы. Игрок принимает на себя роль курьера, который должен, минуя все преграды, добраться до нашего убежища и привезти заказ. По выполнению квеста, игрок получает дополнительный предмет в инвентарь, который разблокирует финальное условие для завершения игры.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/videos/2024-12-31/4.mp4&quot;&gt;Смотреть видео&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Квест с аркадным автоматом&lt;/p&gt;
&lt;h2&gt;Рисуем&lt;/h2&gt;
&lt;p&gt;На рисование у меня ушло полторы недели. Под конец года у меня оставался недогулянный отпуск, поэтому я взял недельку и фуллтайм только и делал, что рисовал. Самое сложное было рисовать отдельные сцены с электрическим щитком и аркадным автоматом, поскольку в таком большом разрешении я еще не рисовал и боялся, что ничего не получится. Ну и конечно же панель инвентаря была моим персональным боссом-вертолетом, не только из-за уровня детализации, но и из-за того, что я просто не знал как ее рисовать чтобы получилось красиво.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/images/2024-12-31/1.avif&quot; alt=&quot;Финальная версия игры&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Финальная версия игры&lt;/p&gt;
&lt;h2&gt;Результат&lt;/h2&gt;
&lt;p&gt;Игру я задеплоил через Netlify и ссылку отправил 25 числа в день дедлайна в той же гугло форме через которую собирались новогодние рисунки. Ну и чтобы избежать недопонимания сразу же написал в предложку в телеге с пояснениями. И стал ждать.&lt;/p&gt;
&lt;p&gt;29 декабря Денис начал подводить итоги конкурсов и в самом конце показал мою игру в качестве бонуса. Неожиданно после открытия ссылки с игрой у стримера крашнулась трансляция на несколько минут и чат сразу же заподозрил наличие майнера, но трансляция ожила и дальше все прошло хорошо. Единственная проблема - почему-то текст в игре, который должен был быть темным, посветлел и на сцене с аркадным автоматом Денис долго не мог найти кнопку &quot;Играть&quot;. Мою игру тестировало человек 5 включая меня и ни у кого такой проблемы не было. Подозреваю, что проблема заключалась в расширении хрома, которое могло инвертировать цвет текста. Насколько я знаю, такие расширения могут ставить если очень хочется иметь темную тему на сайтах, где ее нет.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/images/2024-12-31/7.avif&quot; alt=&quot;Денис запустил игру&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Денис запустил игру&lt;/p&gt;
&lt;p&gt;В целом можно сказать, что результатом я доволен. Игра понравилась Денису да и в чате я видел много комплиментов. На момент показа игры, трансляцию смотрело около 5 тыс. человек. Это была моя первая игра таких размеров. Пусть и геймплея в ней всего на 5 минут, но по количеству графики и фич это самое крупное, что я когда-либо делал. Кто знает, может быть в следующем году я решусь повторить нечто подобное, только уже чтобы геймплея было хотя бы на час.&lt;/p&gt;
&lt;p&gt;Спасибо что уделили внимание моей статье и с наступающим вас новым годом!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/images/2024-12-31/8.avif&quot; alt=&quot;С новым годом&quot; /&gt;&lt;/p&gt;
</content:encoded><enclosure url="https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/images/2024-12-31/1.avif" length="0" type="image/avif"/></item><item><title>Коллизии повернутых прямоугольников</title><link>https://misharemmele.ru/posts/2024-11-20/kollizii-povernutyh-pryamougolnikov/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2024-11-20/kollizii-povernutyh-pryamougolnikov/</guid><description>Ранее движок умел работать только с прямоугольниками, выравненными вдоль оси координат. Добавляем возможность обнаруживать коллизии между фигурами, повернутыми в пространстве.</description><pubDate>Wed, 20 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Первый раз алгоритм для обнаружения коллизий между двумя повернутыми прямоугольниками я применил внезапно не в движке, а на работе.&lt;/p&gt;
&lt;p&gt;Когда-то давно я работал над графических редактором для построения электросхем в браузере. У меня была задача реализовать авторасстановку элементов так, чтобы они не перекрывали друг друга. Каждая деталь на схеме могла сопровождаться набором картинок, изображающих изделие с разных ракурсов и чтобы пользователю редактора не нужно было все расставлять в ручную, заказчик попросил добавить автоматизацию. Также, нужно было предусмотреть функцию авторасстановки для уже существующих электросхем, для элементов, которые были расставлены неаккуратно.&lt;/p&gt;
&lt;p&gt;Примерно в это же время я работал над физикой в движке и уже успел почитать про алгоритмы, которые используются для определения столкновений. Спустя примерно 6 лет уже сложно вспомнить чем я руководствовался при выборе решения и насколько оно было оправданным, но в том момент, мне показалось, что это удачная возможность применить свои знания на реальной рабочей задаче.&lt;/p&gt;
&lt;p&gt;Один из популярных алгоритмов построен на теореме о разделяющей оси (плоскости для 3D). Суть в том, что фигуры не пересекаются если существует хотя бы одна ось, при проекции на которую, полученные отрезки не пересекаются. В качестве осей часто предлагается использовать нормали к граням тестируемых многоугольников. Подробнее про реализацию можно почитать &lt;a href=&quot;https://dyn4j.org/2010/01/sat/&quot;&gt;здесь&lt;/a&gt; если интересно.&lt;/p&gt;
&lt;p&gt;После решения рабочей задачи я притащил решение к себе в движок. Хоть я и не планировал в ближайшее время добавлять возможность поворачивать коллайдеры, я подумал что пусть будет, пока я еще помню как устроен алгоритм. Чтобы снизить количество оверхеда я порезал алгоритм чтобы проверялись не все оси, а только две оси координат.&lt;/p&gt;
&lt;p&gt;Настало время двигаться дальше и я добавил возможность поворачивать фигуры коллайдеров. В уже написанном алгоритме достаточно было расширить цикл с двух осей до 8-ми, которые формируются нормалями о которых я говорил ранее. Для теста прямоугольник-прямоугольник делать практически ничего не пришлось.&lt;/p&gt;
&lt;p&gt;Сложнее было с проверкой прямоугольника и окружности. Старый алгоритм совершенно не подходил и пришлось его переписать. В целом, для теста окружность-прямоугольник тоже можно было использовать теорему о разделяющей оси, но я решил использовать другой способ с чуть меньшим количеством шагов. Задача проверки на пересечение сводится к двум случаям: окружность либо лежит полностью внутри многоугольника, либо она пересекает одну из его граней.&lt;/p&gt;
&lt;p&gt;Для первой ситуации нужно проверить лежит ли центр окружности в многоугольнике. Алгоритмы я находил в интернете самые разные, но выбрал тот, что мне был наиболее понятен. Для проверки из точки выпускается луч и подсчитывается сколько раз луч пересекает контур фигуры. Если четное количество раз, то точка лежит снаружи, если нечетное – внутри. Подробно на реализации останавливаться не буду, текста уже получается очень много. Кому интересно вот ссылка на &lt;a href=&quot;https://observablehq.com/@tmcw/understanding-point-in-polygon&quot;&gt;статью&lt;/a&gt; на которую я опирался.&lt;/p&gt;
&lt;p&gt;Для второй ситуации требуется проверить расстояния до трех точек для каждой грани многоугольника. Две из них это концы граней, а третья получается путем построения перпендикуляра из точки к грани. Если хоть одно из расстояний меньше радиуса окружности, то пересечение есть.&lt;/p&gt;
&lt;p&gt;Во время работы над этой фичей я постоянно ловил флешбеки со школьных уроков геометрии и физики, но только сейчас все эти синусы, косинусы, вектора и линейные уравнения начали иметь для меня какой-то смысл. Задача была интересная, но я немного утомился, поэтому далее я возьму паузу и загляну в редактор. Посмотрю, что можно сделать с проблемами, которые мешали нам на людуме.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/videos/2024-11-20/1.mp4&quot;&gt;Смотреть видео&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Оптимизируем систему обнаружения коллизий</title><link>https://misharemmele.ru/posts/2024-11-10/optimiziruem-sistemu-obnaruzheniya-kollizij/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2024-11-10/optimiziruem-sistemu-obnaruzheniya-kollizij/</guid><description>Впервые с написания физической системы провел замеры производительности и начал работу по устранению самых проблемных мест.</description><pubDate>Sun, 10 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Во время рефакторинга системы обнаружения коллизий, я сделал несколько замеров производительности, чтобы выявить наиболее медленные места. Ранее я ни разу этим не занимался, как написал систему, так она и работала все это время. Честно говоря, тогда я даже слабо понимал за счет чего выбранные алгоритмы работают эффективно, поэтому легко мог что-то проглядеть и накосячить.&lt;/p&gt;
&lt;p&gt;Оказалось, что самая медленная часть системы была не в тестировании пар и не в широкой фазе, а на этапе подготовки данных. При изменении координат объекта, нужно внести изменения во все структуры данных, которые используются под капотом системы коллизий для представления объекта. В частности, нужно обновить границы объекта в отсортированных списках, которые используются на широкой фазе.
Тут я видимо устал, когда писал эту логику несколькими годами ранее и не придумал ничего лучше, чем проходиться по всему списку и искать запись, соответствующую объекту и вносить изменения. Отсюда вылез N^2 на такой простой задаче. Для устранения проблемы я завел мапу, в которой, по идентификатору актора, дублируется запись из сортировочного списка. Ссылка на запись в мапе и в списке одна и та же, поэтому перебирать список больше не надо, достаточно обновить нужное поле с координатами.&lt;/p&gt;
&lt;p&gt;В дополнение к этому, я решил больше не использовать стандартную функцию сортировки и написал собственную сортировку вставками. Как я рассказывал ранее, в данном случае она подходит очень хорошо, поскольку отрабатывает за линейное время на почти отсортированном массиве. Завязываться при этом на сортировку, которая используется в конкретном браузере не хочется, поскольку в браузерном движке может быть реализация, которая здесь не очень подходит.&lt;/p&gt;
&lt;p&gt;Последняя деталь, которая, пожалуй, еще сильнее смахивает на микрооптимизацию, но тем не менее дала небольшой прирост — это доработка массива для хранения пересекающихся пар. Каждую итерацию движка я создавал новый массив и пушил в него пересекающиеся пары, которые прошли все тесты. Чтобы сократить время на выделении памяти, я избавился от нового массива и начал перезаписывать значения по индексу. Если длина массива в конце итерации отличается от того, что было в прошлый раз, то записываю актуальный размер в поле length чтобы избавиться от лишних объектов.&lt;/p&gt;
&lt;p&gt;Наибольший прирост в производительности, ожидаемо, дала именно первая правка, но и последующие две также внесли свой вклад. На душе стало немного спокойнее, я освежил в памяти детали устройства системы коллизий, лучше стал понимать некоторые нюансы используемых алгоритмов, и более сознательно все протестировал. Ранее я скорее полагался на то, что так надо делать, потому что так написано в учебниках.
Ну а теперь можно перейти от оптимизаций к новым фичам, к примеру надо уже научиться поворачивать коллайдеры, ведь для этого все необходимое уже есть.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/videos/2024-11-10/1.mp4&quot;&gt;Смотреть видео&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Широкая фаза обнаружения столкновений</title><link>https://misharemmele.ru/posts/2024-10-26/shirokaya-faza-obnaruzheniya-stolknovenij/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2024-10-26/shirokaya-faza-obnaruzheniya-stolknovenij/</guid><description>Обнаружение коллизий между объектами – задача ресурсоемкая. Если попарно проверять все объекты сцены друг с другом на пересечение, то это займёт квадратичное время. Для сокращения количества проверок придумали множество техник, чтобы максимально быстро отбросить как можно больше пар, которые точно не пересекаются.Такой этап называется широкой фазой обнаружения столкновений.</description><pubDate>Sat, 26 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Как я уже писал выше, обнаружение коллизий между объектами – задача ресурсоемкая. Если попарно проверять все объекты сцены друг с другом на пересечение, то это займёт квадратичное время.&lt;/p&gt;
&lt;p&gt;Для сокращения количества проверок придумали множество техник, чтобы максимально быстро отбросить как можно больше пар, которые точно не пересекаются. Такой этап называется широкой фазой обнаружения столкновений. На следующем этапе, называемом узкой фазой, происходит более тщательная проверка пар, сформированных на предыдущем шаге.&lt;/p&gt;
&lt;p&gt;Популярной техникой для широкой фазы, является алгоритм Sweep and Prune. Идея заключается в том, чтобы построить проекции объектов на одну из координатных осей, отсортировать полученные границы объектов и затем перебрать отсортированный список, фиксируя все пересечения проекций.
Таким образом, нам не придется проверять объекты каждый с каждым, поскольку встретив закрывающую границу проекции объекта, далее его можно не рассматривать.
Что касается сортировки, то алгоритм опирается на то, что между итерациями игрового цикла, объекты не сильно меняют свои позиции, а значит массив проекций будет всегда почти отсортирован. Сортировка, которая лучше работает на частично отсортированном массиве будет выдавать сложность близкую к O(n). Например, сортировка вставками.&lt;/p&gt;
&lt;p&gt;На самом деле я реализовал этот алгоритм уже довольно давно, но рассказывал про него только на &lt;a href=&quot;https://youtu.be/r2aeSbpFcbg&quot;&gt;своем выступлении на HolyJS&lt;/a&gt;&lt;a href=&quot;https://youtu.be/r2aeSbpFcbg?si=4VsDhDNChwRK5WkO&quot;&gt;,&lt;/a&gt; поэтому думаю стоит упомянуть об этом здесь отдельно перед тем как двигаться дальше. А двигаться есть куда, поскольку недавно я заметил, что из-за косяков в реализации алгоритма я избавился от N^2 не до конца. О своих ошибках я расскажу дальше.&lt;/p&gt;
&lt;p&gt;&amp;lt;img
src=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/images/2024-10-26/1.avif&quot;
alt=&quot;Схема Sweep and Prune алгоритма&quot;&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
</content:encoded><enclosure url="https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/images/2024-10-26/1.avif" length="0" type="image/avif"/></item><item><title>Возвращаюсь к физике движка</title><link>https://misharemmele.ru/posts/2024-10-13/vozvraschayus-k-fizike-dvizhka/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2024-10-13/vozvraschayus-k-fizike-dvizhka/</guid><description>Прошло больше четырех лет с момента когда я работал над физикой и обнаружением коллизий в своем движке. С того момента я к этому коду практически не притрагивался.</description><pubDate>Sun, 13 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Прошло больше четырех лет с момента когда я работал над физикой и обнаружением коллизий &lt;a href=&quot;https://t.me/misha_pishet_dvizhok/23&quot;&gt;в своем движке.&lt;/a&gt; С того момента я к этому коду практически не притрагивался. Перевел на тайпскрипт разве что.&lt;/p&gt;
&lt;p&gt;С физикой все понятно. А что такое обнаружение коллизий и зачем это нужно? Вот есть у нас два объекта, и, допустим, они столкнулись. Их столкновение – это коллизия. Информацию о столкновении можно использовать для той же физики, чтобы персонаж в платформере не провалился сквозь землю и не падал бесконечно вниз или для игровых механик таких как стрельба башен в нашей последней игре, где при столкновении снаряда с мышью, снаряд уничтожался, а мышь получала урон.
Задача непростая, поскольку объектов может быть много, геометрия объекта может представлять собой многоугольник повернутый в пространстве да и помимо факта столкновения зачастую требуется дополнительная информация о точке контакта или вектора столкновения.&lt;/p&gt;
&lt;p&gt;Когда я взялся за эту задачу в первый раз, то практически ничего не понимал и даже нагуглить нужные материалы не мог, поскольку не знал что искать и какие ключевые слова использовать. В итоге я сидел, обложившись научными статьями с Киберленинки и понимал примерно 20 процентов из прочитанного.&lt;/p&gt;
&lt;p&gt;В результате, я написал набор систем, которые умеют регистрировать столкновения между простыми фигурами такими как круг или прямоугольник выравненный вдоль координатных осей и расталкивать их. Код, эмулирующий физику, позволил прикладывать векторы силы и импульса к объектам, а также оказывать действие гравитации на них если потребуется.
Этого хватало для игр, которые я писал все это время, но хочется иметь возможность делать что-то более сложное. К примеру, чтобы движок позволял составить башню из блоков как в Дженге, при вытаскивании которых, башня начинает шататься и разваливаться.&lt;/p&gt;
&lt;p&gt;Я уже начал работать над улучшениями еще до Людума и далее планирую продолжать. Как минимум задачи следующие:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;возможность поворачивать коллайдеры&lt;/li&gt;
&lt;li&gt;поддержка угловой скорости, чтобы при столкновении объекты могли разворачиваться относительно точки приложенной силы&lt;/li&gt;
&lt;li&gt;ray casting. Это функция, позволяющая выпустить луч из заданной точки и получить список объектов-попаданий. Полезно для AI или стрельбы в шутерах&lt;/li&gt;
&lt;li&gt;упругие столкновения объектов и возможность передачи кинетической энергии&lt;/li&gt;
&lt;li&gt;пространственное разделение (spatial partitioning) чтобы оптимизировать количество попарных проверок. Может быть такие оптимизации в небольших играх и не понадобятся, но интересно разобраться, что это за зверь такой + хорошая возможность применить на практике сложные структуры данных&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;В целом, с прошлого раза ничего не изменилось – что и как делать непонятно, поэтому я сел за научные статьи. Наткнулся на &lt;a href=&quot;https://research.ncl.ac.uk/game/mastersdegree/gametechnologies/physicstutorials/&quot;&gt;туториалы по физике&lt;/a&gt; на сайте гейм дев факультета британского университета – довольно увлекательно да и там не только про физику материалы есть.&lt;/p&gt;
</content:encoded></item><item><title>Итоги Ludum Dare 56</title><link>https://misharemmele.ru/posts/2024-10-10/itogi-ludum-dare-56/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2024-10-10/itogi-ludum-dare-56/</guid><description>Подведу итоги прошедшего джема. В целом все прошло неплохо, все в команде успели поработать с движком и с редактором, от чего проблемы и недостатки инструментов всплывали с утроенной скоростью.</description><pubDate>Thu, 10 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Подведу итоги прошедшего джема. В целом все прошло неплохо, все в команде успели поработать с движком и с редактором, от чего проблемы и недостатки инструментов всплывали с утроенной скоростью.&lt;/p&gt;
&lt;p&gt;К примеру, редактор не реагирует на пулл изменений из ветки и если другой разработчик трогал сцену и что-то менял, то запущенный редактор эти изменения перетрет. Из-за этого приходилось закрывать редактор каждый раз перед стягиванием изменений из мастера.&lt;/p&gt;
&lt;p&gt;Мы в первый раз делали игру с несколькими уровнями и хоть такая функция и была предусмотрена в движке, возникли проблемы с камерой. Для рендерера нужно указывать камеру заранее через редактор в сцене. При смене уровня, сцена остается та же, но одни объекты выгружаются, а другие загружаются. Поскольку айдишники у новых объектов отличаются, то для следующего уровня нужно передать информацию о новом объекте с компонентом камеры. Сделать это можно только через код, который срабатывает слишком поздно и игра крашится так как камера не найдена.&lt;/p&gt;
&lt;p&gt;Шаблонный репозиторий для быстрого старта все еще полезная штука, но оказалось, что стартовая репа для новичка и для людума – это разные вещи. Часть первого дня я потратил на подключение систем и перенос кода, которые я уже писал для прошлых игр. Боевую систему взял для стрельбы башен, систему перемещения для мышей, да и спавн врагов волнами тоже был реализован несколько месяцев назад. Возможно для игровых джемов полезнее создавать максимально толстый репозиторий со всем, что может пригодиться. Хотя даже с тем, что было, получилось сделать игру, которая работает сразу на всех платформах при минимальных затратах на адаптацию верстки.&lt;/p&gt;
&lt;p&gt;Помимо перечисленного, были и другие проблемы вроде отсутствия горячих клавиш в редакторе или отсутствия множественного выделения в панели проводника. Список доработок я себе занес и буду стремиться учесть хотя бы наиболее важные из них до следующего людума в апреле.&lt;/p&gt;
&lt;p&gt;&amp;lt;img
src=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/images/2024-10-10/1.avif&quot;
alt=&quot;Скриншот игры в редакторе&quot;&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
</content:encoded><enclosure url="https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/images/2024-10-10/1.avif" length="0" type="image/avif"/></item><item><title>Финал Ludum Dare 56</title><link>https://misharemmele.ru/posts/2024-10-08/final-ludum-dare-56/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2024-10-08/final-ludum-dare-56/</guid><description>Закончился последний день Ludum Dare. Доделывали игру как могли. Нарисовали башни, подтюнили уровни и добавили оформление: главное меню, лого и различные иконки для интерфейса.</description><pubDate>Tue, 08 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Закончился последний день Ludum Dare. Доделывали игру как могли. Нарисовали башни, подтюнили уровни и добавили оформление: главное меню, лого и различные иконки для интерфейса.&lt;/p&gt;
&lt;p&gt;Хотелось добавить в уровни больше декоративных элементов: построек вроде теплицы, грядок и тому подобного, но времени не хватило, поэтому получилось довольно минималистично.&lt;/p&gt;
&lt;p&gt;Героями в защите дачи от грызунов стали: лебедь из покрышки, мангал и старый холодильник.&lt;/p&gt;
&lt;p&gt;Заполнили страничку на сайте: https://ldjam.com/events/ludum-dare/56/long-live-dacha. Теперь надо играть и оценивать другие игры если хотим получить фидбек на свою.&lt;/p&gt;
&lt;p&gt;В игру можно поиграть по этой ссылке: https://michailremmele.github.io/ludum-dare-56/&lt;/p&gt;
&lt;p&gt;Из известных багов есть один неприятный момент: грызуны иногда забывают поворачивать и бегут за края экрана. Правила людума резрешают пушить хотфиксы после дедлайна, поэтому исправлю это завтра. В качестве временного решения – газон убивает всех кто его пытается покинуть.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/videos/2024-10-08/1.mp4&quot;&gt;Смотреть видео&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Второй день Ludum Dare 56</title><link>https://misharemmele.ru/posts/2024-10-06/vtoroj-den-ludum-dare-56/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2024-10-06/vtoroj-den-ludum-dare-56/</guid><description>Добавили возможность строительства и апгрейда башен, разместили точки для их строительства. Попутно дорабатываем логику ИИ чтобы они правильно ходили по трекам.</description><pubDate>Sun, 06 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Второй день. Добавили возможность строительства и апгрейда башен, разместили точки для их строительства. Попутно дорабатываем логику ИИ чтобы они правильно ходили по трекам.&lt;/p&gt;
&lt;p&gt;Набросали три уровня и сверстали интерфейс для переключения между ними. Инфа о пройденных уровнях сохраняется в local storage, чтобы в случае перезагрузки страницы можно было продолжить игру не потеряв прогресс.&lt;/p&gt;
&lt;p&gt;Арт тоже потихоньку начали прикручивать. До башен добраться не успели. Официально командный людум идет три дня, поэтому завтра еще остается время на доработки.&lt;/p&gt;
&lt;p&gt;Времени остается все меньше, поэтому костыли становятся все страшнее.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/videos/2024-10-06/1.mp4&quot;&gt;Смотреть видео&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Первый день Ludum Dare 56</title><link>https://misharemmele.ru/posts/2024-10-05/pervyj-den-ludum-dare-56/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2024-10-05/pervyj-den-ludum-dare-56/</guid><description>Прошел первый день Ludum Dare, где пишут игры за несколько дней. Тема 56 людума – &quot;Tiny creatures&quot;. Мы решили делать Tower Defence про дачу, которую надо оборонять от мелких вредителей.</description><pubDate>Sat, 05 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Прошел первый день Ludum Dare, где пишут игры за несколько дней. Тема 56 людума – &quot;Tiny creatures&quot;.&lt;/p&gt;
&lt;p&gt;Мы решили делать Tower Defence про дачу, которую надо оборонять от мелких вредителей (грызуны, мб птицы). Идея банальная, но давно хотелось такое написать, плюс все были не против.&lt;/p&gt;
&lt;p&gt;За сегодня накидали несколько видов башен: одна стреляет одиночными снарядами, другая по области, а третья как и вторая, но еще и замедляет.&lt;/p&gt;
&lt;p&gt;Набросали черновик уровня с одним треком по которому бегут противники, добавили базовый набор вроде меню победы и поражения. Все на цветных прямоугольниках пока арт в работе.&lt;/p&gt;
&lt;p&gt;Завтра будем делать возможность строительства и апгрейда башен и усиленно работать над графикой.&lt;/p&gt;
&lt;p&gt;С появлением еще одного разработчика в команде, приходится страдать при мержах json-а в котором описана сцена. Интересно как с этим справляются разработчики на энтерпрайз движках...&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/videos/2024-10-05/1.mp4&quot;&gt;Смотреть видео&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Подготовка к Ludum Dare 56</title><link>https://misharemmele.ru/posts/2024-09-21/podgotovka-k-ludum-dare-56/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2024-09-21/podgotovka-k-ludum-dare-56/</guid><description>Осталось меньше двух недель до следующего Ludum Dare и я потихоньку готовлюсь. Поскольку джемы проходят в большей степени на энтузиазме и уровень участников очень разный, то ограничений на подготовку практически нет.</description><pubDate>Sat, 21 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Осталось меньше двух недель до следующего &lt;a href=&quot;https://ldjam.com/&quot;&gt;Ludum Dare&lt;/a&gt; и я потихоньку готовлюсь. Поскольку джемы проходят в большей степени на энтузиазме и уровень участников очень разный, то ограничений на подготовку практически нет. Разрешается использовать любые инструменты да и в целом врываться с любой кодовой базой.&lt;/p&gt;
&lt;p&gt;В прошлый раз я ограничился простым Hello World проектом, где &quot;игра&quot; выводила на экран только пару квадратов, а также добавил очень простой интерфейс на реакте без стилей. По итогу приходилось тратить драгоценное время на стилизацию меню и подключение недостающих игровых систем, а на мобильную версию игры времени вообще не хватило.&lt;/p&gt;
&lt;p&gt;На этот раз я заморочился со &lt;a href=&quot;https://github.com/michailRemmele/remiz-dummy&quot;&gt;стартовой репой&lt;/a&gt; чуть сильнее и решил, что будет проще и быстрее начинать с уже готовой, но простой игры с настроенным управлением как для десктопа так и для мобильных устройств. Выбор пал на змейку.&lt;/p&gt;
&lt;p&gt;Также, я добавил манифест, чтобы игру можно было сохранять на домашний экран телефона и запускать без обвязки браузера.&lt;/p&gt;
&lt;p&gt;С высокой вероятностью в этот раз к нам с женой подключится еще один разработчик – мой бывший коллега, поэтому надеюсь, что такой шаблон поможет и ему быстрее втянуться в разработку игры.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/videos/2024-09-21/1.mp4&quot;&gt;Смотреть видео&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Улучшаем работу с шаблонами</title><link>https://misharemmele.ru/posts/2024-09-08/uluchshaem-rabotu-s-shablonami/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2024-09-08/uluchshaem-rabotu-s-shablonami/</guid><description>Расскажу немного про работу с шаблонами в редакторе. При создании актора из шаблона, копируется вся его иерархия, включая вложенные шаблоны. После создания актора из шаблона его можно кастомизировать, что-то переопределить или добавить дополнительные объекты. Проблема возникает когда в шаблон нужно добавить новые объекты.</description><pubDate>Sun, 08 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Расскажу немного про работу с шаблонами в редакторе.&lt;/p&gt;
&lt;p&gt;При создании актора (напоминаю, что актором в моем движке называется любой объект на сцене) из шаблона, копируется вся его иерархия, включая вложенные шаблоны. Зачем это нужно? После создания актора из шаблона его можно кастомизировать, что-то переопределить или добавить дополнительные объекты. Чтобы различать кастомные акторы от наследуемых, каждый актор помечается признаком, указывающим от какого вложенного шаблона он наследуется.&lt;/p&gt;
&lt;p&gt;Проблема возникает когда в шаблон нужно добавить новые объекты. Если на сцену ранее уже добавлялись акторы, созданные из этого шаблона, то при изменении структуры шаблона, нужно отыскать все наследуемые акторы и обновить их.&lt;/p&gt;
&lt;p&gt;В свое время я не стал этого делать, решив что работа с шаблонами очень уж сложная и лучше не развивать эту историю, а позднее все переписать.&lt;/p&gt;
&lt;p&gt;Очень зря, потому что ничего лучше я так и не придумал, а во время людума после редактирования шаблонов персонажей мне приходилось вручную удалять и добавлять заново все наследуемые акторы тратя кучу времени.&lt;/p&gt;
&lt;p&gt;Проблему я исправил и теперь при добавлении вложенных шаблонов, редактор модифицирует всех наследников автоматически. Происходит это путем перебора дерева уровней в поиске акторов с подходящим идентификатором родительского шаблона.&lt;/p&gt;
&lt;p&gt;Не все проблемы удалось решить за один заход, но это уже другая история. В любом случае ручной работы снова стало немного меньше.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/videos/2024-09-08/1.mp4&quot;&gt;Смотреть видео&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Незаметные функции редактора</title><link>https://misharemmele.ru/posts/2024-08-19/nezametnye-funkcii-redaktora/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2024-08-19/nezametnye-funkcii-redaktora/</guid><description>В редакторах кода так много функций, которые даже не замечаешь А вот если привычных инструментов нет – это сразу бросается в глаза.</description><pubDate>Mon, 19 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;В редакторах кода так много функций, которые даже не замечаешь. Например, при переключении между табами с открытыми исходниками, в проводнике происходит подскролл к выбранному файлу, а если файл был в свернутой директории, то весь путь до него раскрывается.&lt;/p&gt;
&lt;p&gt;Когда пишешь редактор сам, такие мелочи быстро замечаешь, потому что у тебя этого нет и реализовывать все нужно самостоятельно.&lt;/p&gt;
&lt;p&gt;Добавил скролл, авто раскрытие родительских нод и переключение вкладки на ту, что соответствует выбранному элементу. Мелочь, но времени на ручные переключения будет тратиться чуть меньше.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/videos/2024-08-19/1.mp4&quot;&gt;Смотреть видео&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Восстанавливаем состояние редактора при перезапуске</title><link>https://misharemmele.ru/posts/2024-08-09/vosstanavlivaem-sostoyanie-redaktora-pri-perezapuske/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2024-08-09/vosstanavlivaem-sostoyanie-redaktora-pri-perezapuske/</guid><description>Следующий людум уже не за горами, поэтому самое время вспомнить какие проблемы возникали во время разработки игры на апрельском эвенте. Одна из таких проблем – при перезапуске редактора сбрасываются все настройки вроде размера сетки, выбранного уровня или открытых табов.</description><pubDate>Fri, 09 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Лето выдалось не очень продуктивным. После гейм джема я вяло вносил правки в игру, а потом поехал в отпуск, после которого тоже надо было отдохнуть.&lt;/p&gt;
&lt;p&gt;Ludum Dare проходит два раза в год и следующий раз уже не за горами – 4 октября. Самое время вспомнить какие проблемы возникали во время разработки игры на апрельском эвенте.&lt;/p&gt;
&lt;p&gt;Одна из таких проблем – при перезапуске редактора сбрасываются все настройки вроде размера сетки, выбранного уровня или открытых табов. Перезапускать его при этом приходится часто. Чтобы созданные для игры компоненты и системы подгружались в редакторе, нужно обновить страницу, поскольку под капотом редактора webpack пересобирает код, который берет из проекта с игрой. Возможно тут можно прикрутить hot module replacement чтобы перезагрузка не требовалась, но даже если редактор был закрыт, хотелось бы продолжить с того места на котором остановился.&lt;/p&gt;
&lt;p&gt;Один из вариантов – использовать local storage, но порт на котором запускается редактор выбирается случайным образом для избежания коллизий с другими приложениями, поэтому доступ к сохраненным данным можно легко потерять.&lt;/p&gt;
&lt;p&gt;Другой вариант – хранить состояние в файле. Способ простой и надежный. На нем я и остановился. Написал класс прослойку PersistentStorage, который отдает get, set методы, под капотом хранит все в обычном объекте и периодически пишет изменения в файл через node апишку проброшенную через electron. Далее во всех местах редактора, где было желание прихранить состояние, я добавил обращение к PersistentStorage.&lt;/p&gt;
&lt;p&gt;Теперь при перезапуске редактора восстанавливается выбранный уровень, положение камеры на сцене, настройки грида и некоторые другие параметры вроде открытых табов и раскрытых элементов в древовидных списках.&lt;/p&gt;
&lt;p&gt;Возвращаться к работе после долгого перерыва может быть тяжело, особенно если тебе за нее не платят, но я постарался взять быструю и понятную задачу и заставлял себя хотя бы по минут 15-20 в день смотреть в проект и писать пару строчек кода. Так и вернулся настрой.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/videos/2024-08-09/1.mp4&quot;&gt;Смотреть видео&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Как не бросить пет-проект</title><link>https://misharemmele.ru/posts/2024-06-06/kak-ne-brosit-pet-proekt/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2024-06-06/kak-ne-brosit-pet-proekt/</guid><description>Казалось бы, у пет-проекта должна быть конечная цель. В какой-то момент можно было бы сказать, что я сделал все что хотел, научился тому что хотел, поэтому можно пойти дальше. Вместо этого я только добавляю себе работы.</description><pubDate>Thu, 06 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Казалось бы, у пет-проекта должна быть конечная цель. В какой-то момент можно было бы сказать, что я сделал все что хотел, научился тому что хотел, поэтому можно пойти дальше &lt;s&gt;и переписать движок на Rust.&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;Вместо этого я только добавляю себе работы.
Например:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;В движке нужно доделывать физику&lt;/li&gt;
&lt;li&gt;Все еще нету звука&lt;/li&gt;
&lt;li&gt;От рендерера на Three.js хочется отказаться и попробовать снова написать свой рендеринг, но уже на WebGPU&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;А еще есть редактор и там свои проблемы:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;В редакторе не хватает инструментов&lt;/li&gt;
&lt;li&gt;Нет валидации в формах инспектора&lt;/li&gt;
&lt;li&gt;Состояние не сохраняется и при перезапуске нужно натыкивать все по новой&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Не говоря уже о том, что хочется писать игры и к каждой уже существующей хочется что-нибудь добавить.&lt;/p&gt;
&lt;p&gt;Я как будто насоздавал рабочих мест, но сидеть на них буду в одиночку.&lt;/p&gt;
&lt;p&gt;Помогает ведение досок с задачами по каждому проекту. В задачах я веду заметки по ходу работы и подвожу итоги того что сделал. Это позволяет в случае чего восстановить последовательность действий, быстро вернуться в контекст проекта после перерыва или понять зачем я написал такой код пять лет назад.&lt;/p&gt;
&lt;p&gt;Было неприятно, когда в конце прошлого года Atlassian заблочил мою учетку с Trello и похерил весь лог работы. Как оказалось они уведомляли меня по почте что аккаунт скоро заблокируют и я потеряю все данные, но я это письмо успешно пропустил, поскольку после сотен писем с заголовком &quot;Taco from Trello&quot; перестал обращать на них внимание. К счастью, по просьбе в поддержку мне дали один день, чтобы все выгрузить и я экспортировал свои доски в Notion. В целом норм.&lt;/p&gt;
&lt;p&gt;Короче говоря, конечной цели у меня никакой нет. Возможно я до старости лет буду заниматься движком или пока совсем не надоест. За шесть лет пока не надоело. Пожалуй, в этом я пошел в родителей, которые после приобретения дачи открыли для себя неисчерпаемый источник работы и уже больше 15-ти лет что-то там ремонтируют.&lt;/p&gt;
&lt;p&gt;&amp;lt;img
src=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/images/2024-06-06/1.avif&quot;
alt=&quot;Скриншот доски Notion с задачами&quot;&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
</content:encoded><enclosure url="https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/images/2024-06-06/1.avif" length="0" type="image/avif"/></item><item><title>Добавил параллакс</title><link>https://misharemmele.ru/posts/2024-06-03/dobavil-parallaks/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2024-06-03/dobavil-parallaks/</guid><description>Недавно добавил параллакс. Что это вообще такое и как его можно реализовать? Парралакс в играх – это когда фоновые объекты двигаются с разной скоростью относительно наблюдателя в зависимости от их удаленности.</description><pubDate>Mon, 03 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Недавно добавил параллакс.&lt;/p&gt;
&lt;p&gt;Что это вообще такое и как его можно реализовать?&lt;/p&gt;
&lt;p&gt;Парралакс в играх – это когда фоновые объекты двигаются с разной скоростью относительно наблюдателя в зависимости от их удаленности. Это дает ощущение объема и картинка выглядит живее.&lt;/p&gt;
&lt;p&gt;Реализация может быть очень простой и моя система уместилась в 60 строк кода, а основная часть и вовсе состоит из пары строк вычислений (код немного порезан чтобы было проще прочитать):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function update() {
  const { x: cameraX, y: cameraY } = camera.Transform;

  actorCollection.forEach((actor) =&amp;gt; {
    const t = actor.Transform;
    const p = actor.Parallax;

    t.x = p.startX + (cameraX - p.startX) * p.distance;
    t.y = p.startY + (cameraY - p.startY) * p.distance;
  });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Для каждого объекта фона, к которому мы хотим применить параллакс эффект, добавляется компонент Parallax со свойством distance. Текущее положение объекта с параллаксом вычисляется относительно его начальных координат, координат камеры и коэффициента distance, который принимает значения от -1 до 1. Если к примеру distance положительный, то объект будет перемещаться вслед за персонажем, поэтому будет казаться, что объект находится далеко от точки наблюдения и чтобы объект пропал из поля зрения нужно потратить больше времени чем когда параллакса нет.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/videos/2024-06-03/1.mp4&quot;&gt;Смотреть видео&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>О фиксированной частоте обновления физики</title><link>https://misharemmele.ru/posts/2024-05-20/o-fiksirovannoj-chastote-obnovleniya-fiziki/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2024-05-20/o-fiksirovannoj-chastote-obnovleniya-fiziki/</guid><description>Кто-то мог заметить неприятный баг в предыдущей игре, когда персонаж внезапно проваливается сквозь платформу и бесконечно падает в бездну. Проблема кроется в дискретности обновления игры.</description><pubDate>Mon, 20 May 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Кто-то мог заметить неприятный баг в предыдущей игре, когда персонаж внезапно проваливается сквозь платформу и бесконечно падает в бездну. Особенно часто это возникало при переключении вкладок или сворачивании браузера.&lt;/p&gt;
&lt;p&gt;Проблема кроется в дискретности обновления игры. Каждый игровой такт, физическая система прикладывает силы к объектам сцены и перемещает их в зависимости от набранной скорости и времени, которое прошло с предыдущего такта игрового цикла. Далее, при возникновении коллизий, физика расталкивает твердые тела чтобы одно не проваливалось сквозь другое. Все эти этапы происходят до отрисовки, поэтому на экране игрок видит как персонаж устойчиво стоит на платформе.&lt;/p&gt;
&lt;p&gt;Но что если пауза между кадрами будет слишком большой? К примеру, если устройство пользователя не справится с нагрузкой и игра начнет тормозить. В таком случае, за одну итерацию объект может переместиться настолько далеко, что система обнаружения коллизий не увидит никакого столкновения. Один объект будто бы перескочит через другой. Именно это и происходит в нашей игре.&lt;/p&gt;
&lt;p&gt;Решение может заключаться в жесткой фиксации количества итераций физического пайплайна и другой игровой логики, связанной с перемещением объектов по сцене. То есть, вне зависимости от частоты кадров и возможных просадок в производительности, всегда нужно запускать физику одинаковое количество раз с фиксированной дельтой по времени.&lt;/p&gt;
&lt;p&gt;Реализовать это можно через добавление вложенного цикла внутрь основного цикла игры и счетчика, накапливающего время выполнения. Цикл крутится уменьшая счетчик на фиксированное количество миллисекунд пока значение не упадет до меньшего или равного нулю. Таким образом, если игра работает медленнее чем нужно, то физика отработает несколько раз, а если игра работает слишком быстро, то она не отработает вообще.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/videos/2024-05-20/1.mp4&quot;&gt;Смотреть видео&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Доработал ИИ призраков</title><link>https://misharemmele.ru/posts/2024-05-14/dorabotal-ii-prizrakov/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2024-05-14/dorabotal-ii-prizrakov/</guid><description>Во время людума я хотел чтобы призраки умели самостоятельно добираться до врагов даже если те находятся не в прямой доступности, а где-то на платформах. Быстро это сделать не получилось, поэтому я добавил кнопку призыва приведений к игроку.</description><pubDate>Tue, 14 May 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Во время людума я хотел чтобы призраки умели самостоятельно добираться до врагов даже если те находятся не в прямой доступности, а где-то на платформах. Быстро это сделать не получилось, поэтому я добавил кнопку призыва приведений к игроку.&lt;/p&gt;
&lt;p&gt;Сейчас времени у меня достаточно, поэтому я решил снова подумать, что с этим можно сделать.&lt;/p&gt;
&lt;p&gt;Ранее мне доводилось слышать про A* алгоритм и построение графа перемещения, но я пока плохо представляю как все это делать поскольку в тему не погружался. Когда-нибудь я изучу этот вопрос, но пока что нужен способ проще.&lt;/p&gt;
&lt;p&gt;Первым решением была способность телепортироваться к выбранной цели. Благодаря телепортации, приведение сможет без проблем дотянуться до врагов, где бы они не находились да и за игроком следовать будет проще.&lt;/p&gt;
&lt;p&gt;Как я уже упомянул, на людуме сделать это не вышло. Причиной было непонимание как определять когда можно телепортироваться, а когда нет. Можно смотреть на расстояние по оси Y до цели и если оно выше какого-то минимального порога, то делать скачек. Но что если игрок находится в прыжке? Как определить висит ли он в воздухе или стоит на платформе?&lt;/p&gt;
&lt;p&gt;В компоненте RigidBody, описывающем физические параметры объекта, хранятся вектора приложенных сил и вектор текущей скорости. Поскольку прыжок реализован через импульс, а бег через обычное смещение координат в зависимости от времени, то я решил смотреть на значение вектора скорости. Если вектор ненулевой, значит возможно цель висит в воздухе, поэтому лучше подождать.&lt;/p&gt;
&lt;p&gt;Для эффектности я нарисовал анимированные клубы дыма, которые появляются в точках откуда и куда призрак переместился.&lt;/p&gt;
&lt;p&gt;Ну и напоследок, мне также не нравилось, что всех врагов нужно провоцировать своим лицом и терпеть пока не добегут союзники, поэтому призракам с копьями я дал возможность телепортации к врагу даже если он стоит на том же уровне. Полностью проблему не решило, но стало чуть получше.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/videos/2024-05-14/1.mp4&quot;&gt;Смотреть видео&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;На самом деле была еще одна проблема: как понять с какой стороны к цели можно переместиться? Вдруг противник стоит на краю платформы и выбрав неверную сторону призрак сразу же упадет.
Можно конечно прям в координаты цели телепортировать, но это как-то некрасиво.&lt;/p&gt;
&lt;p&gt;Для решения этой проблемы я привязал к каждому юниту по набору из двух коллайдеров по бокам и скрипту, который слушает их коллизии и проверяет, нет ли там столкновения с твердым статичным объектом. Если есть, то значит с этой стороны подступиться можно, а если нет, то нужно проверить другую. Если земли нет с обеих сторон, то тогда уже придется перемещаться по координатам самой цели, но это скорее исключение, потому что таких коротких платформ я ставить не планировал.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/videos/2024-05-14/2.mp4&quot;&gt;Смотреть видео&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Результаты голосования на людуме</title><link>https://misharemmele.ru/posts/2024-05-06/rezultaty-golosovaniya-na-lyudume/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2024-05-06/rezultaty-golosovaniya-na-lyudume/</guid><description>На днях закончился подсчет голосов на людуме и опубликовали результаты. Всего в категории Jam (команда в несколько человек, три дня на разработку и отсутствие серьезных ограничений) было отправлено 1679 игр.</description><pubDate>Mon, 06 May 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;На днях закончился подсчет голосов на людуме и опубликовали результаты.&lt;/p&gt;
&lt;p&gt;Всего в категории Jam (команда в несколько человек, три дня на разработку и отсутствие серьезных ограничений) было отправлено 1679 игр.&lt;/p&gt;
&lt;p&gt;Мы заняли 435-е в общем зачете и 208-е за графику. Довольно далеко от верхушки, но вместо трех дней мы работали над игрой только два (понедельник рабочий) да и опыта у нас особого нет.&lt;/p&gt;
&lt;p&gt;Это наша первая серьезная попытка засабмитить игру на сайте джема и получить оценку. Таня участвовала в первый раз, а для меня это вторая игра, которую удалось довести до рабочего состояния к концу мероприятия.&lt;/p&gt;
&lt;p&gt;Так что, пойдет. Будем продолжать и посмотрим как пойдет прогресс.&lt;/p&gt;
&lt;p&gt;&amp;lt;img
src=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/images/2024-05-06/1.avif&quot;
alt=&quot;Результаты голосования на людуме&quot;&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
</content:encoded><enclosure url="https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/images/2024-05-06/1.avif" length="0" type="image/avif"/></item><item><title>Все еще доделываю игру</title><link>https://misharemmele.ru/posts/2024-04-24/vse-esche-dodelyvayu-igru/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2024-04-24/vse-esche-dodelyvayu-igru/</guid><description>После людума передо мной встал выбор, вернуться к доработке движка и погрязнуть в переписывании физики на пару месяцев или еще немного позаниматься игрой и развить механики. Я выбрал второй вариант. В конце концов я пишу движок чтобы делать игры.</description><pubDate>Wed, 24 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;После людума передо мной встал выбор, вернуться к доработке движка и погрязнуть в переписывании физики на пару месяцев или еще немного позаниматься игрой и развить механики. Я выбрал второй вариант. В конце концов я пишу движок чтобы делать игры.&lt;/p&gt;
&lt;p&gt;Одна только проблема не давала мне покоя. Если персонаж прыгнет находясь под платформой, то он ударится об нее головой. Обычно, платформеры позволяют игроку запрыгивать на платформу снизу, то есть платформа вроде как и твердое тело на котором можно стоять, но в то же время пропускает сквозь себя объекты в определенном направлении.&lt;/p&gt;
&lt;p&gt;Я пока плохо представляю как это сделать правильно и хочу поскорее продолжить писать игру, поэтому сделал специальную костыльную версию движка. В ней я навтыкал дополнительных условий в физическую систему, где я проверяю нужно ли как-то разрешать коллизию и раздвигать объекты или нет.&lt;/p&gt;
&lt;p&gt;Теперь если объект с компонентом RigidBody обладает свойством isPlatform и возникает коллизия с другим объектом, вектор движения которого направлен вверх, то такая коллизия пропускается и будет игнорироваться до тех пор, пока объекты не разойдутся в стороны и возникнет событие CollisionLeave.&lt;/p&gt;
&lt;p&gt;Напомню, что все обновления можно посмотреть на &lt;a href=&quot;https://michailremmele.github.io/alumnus-of-darkness/&quot;&gt;страничке игре&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/videos/2024-04-24/1.mp4&quot;&gt;Смотреть видео&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Оцениваем чужие игры и доделываем свою</title><link>https://misharemmele.ru/posts/2024-04-20/ocenivaem-chuzhie-igry-i-dodelyvaem-svoyu/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2024-04-20/ocenivaem-chuzhie-igry-i-dodelyvaem-svoyu/</guid><description>Прошла неделя с момента как мы закончили игру. Как оказалось дедлайн по публикации это только половина дела. Дальше идет оценка игр и определение топа по различным категориям (графика, звук, инновационнсть, фан и тд). Процесс занимает чуть больше двух недель.</description><pubDate>Sat, 20 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Прошла неделя с момента как мы закончили игру. Как оказалось дедлайн по публикации это только половина дела. Дальше идет оценка игр и определение топа по различным категориям (графика, звук, инновационнсть, фан и тд). Процесс занимает чуть больше двух недель.&lt;/p&gt;
&lt;p&gt;Если ты хочешь чтобы твою игру заметили, поиграли в нее и оставили фидбек, то тебе придется приложить не меньше усилий чем при разработке. Начинается самый настоящий маркетинг на минималках.&lt;/p&gt;
&lt;p&gt;Для того, чтобы игра попадалась на первых страницах сайта ldjam.com тебе нужно играть и оценивать чужие игры. Так ты будешь получать карму и твоя игра будет получать более высокий приоритет. До тех пор, пока кто-нибудь не напишет &quot;Nice game!&quot; и твоя игра снова не улетит в помойку.&lt;/p&gt;
&lt;p&gt;На текущий момент мы с Таней оценили чуть больше 20-ти игр, по возможности старались писать развернуто о своих впечатлениях. Наша же игра набрала 20 оценок – это критическая отметка после которой по итогам голосования твоя игра попадет в общий рейтинг. Ну и комментариев под игрой нам тоже немного накидали.&lt;/p&gt;
&lt;p&gt;Очень классное ощущение читать фидбек на свою игру, особенно если он детальный. Заряжает энергией на то чтобы работать дальше. В среднем большинство людей страдает от лучников, но оно и понятно.&lt;/p&gt;
&lt;p&gt;А еще мы накатали большую статью о том как мы делали игру и с какими сложностями в процессе столкнулись. Опубликовали на сайте людума чтобы собрать классов и привлечь еще немного внимания к нашей игре.
Прочитать можно тут (на английском): &lt;a href=&quot;https://ldjam.com/events/ludum-dare/55/the-alumnus-of-darkness/making-the-alumnus-of-darkness-on-my-game-engine-was-it-worth-it&quot;&gt;ссылка&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Ну и игру мы пока не забрасывали. Таня перерисовала некоторые элементы, а я поправил хитбоксы у персонажей чтобы игрок ловил меньше стрел в лицо и добавил мобильную версию с тач управлением.
Поскольку обновлять игру после дедлайна несолидно, я завел новый репозиторий, поэтому актуальная ссылка с игрой вот: &lt;a href=&quot;https://michailremmele.github.io/alumnus-of-darkness/&quot;&gt;ссылка&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;У странички есть манифест, поэтому игру можно сохранить как пва на домашний экран и играть на телефоне в фуллскрине.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/videos/2024-04-20/1.mp4&quot;&gt;Смотреть видео&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Закончили писать игру</title><link>https://misharemmele.ru/posts/2024-04-14/zakonchili-pisat-igru/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2024-04-14/zakonchili-pisat-igru/</guid><description>Закончили. За два дня мы провели за ноутами по меньшей мере часов 20 с перерывами на покушать и погулять пса. Я писал код и собирал уровень в редакторе, а Таня нарисовала практически весь арт, кроме персонажей. Их рисовал я.</description><pubDate>Sun, 14 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Закончили. За два дня мы провели за ноутами по меньшей мере часов 20 с перерывами на покушать и погулять пса.&lt;/p&gt;
&lt;p&gt;Я писал код и собирал уровень в редакторе, а Таня нарисовала практически весь арт, кроме персонажей. Их рисовал я.&lt;/p&gt;
&lt;p&gt;Старался писать быстро как мог, где можно было наговнокодить – говнокодил, где скопипастить – копипастил.&lt;/p&gt;
&lt;p&gt;Не успел прикрутить управление с мобильных телефонов, добавлю чуть позже. Поэтому пока только десктоп.&lt;/p&gt;
&lt;p&gt;Очень устали, но получилось симпатично, к тому же это первый людум, где я использовал редактор.&lt;/p&gt;
&lt;p&gt;Кажется игра получилась сложноватой. Мне пришлось немного потренироваться прежде чем я научился справляться с лучниками.&lt;/p&gt;
&lt;p&gt;Поиграть можно тут: https://michailremmele.github.io/ludum-dare-55/&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/videos/2024-04-14/1.mp4&quot;&gt;Смотреть видео&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Пишем с женой игру на людум</title><link>https://misharemmele.ru/posts/2024-04-13/pishem-s-zhenoj-igru-na-lyudum/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2024-04-13/pishem-s-zhenoj-igru-na-lyudum/</guid><description>Собрались с женой писать игру на людум. Тема в этот раз: Summoning. Брейнштормили идеи пока гуляли с собакой, надумали игру про молодого некроманта.</description><pubDate>Sat, 13 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Собрались с женой писать игру на людум. Тема в этот раз: Summoning.&lt;/p&gt;
&lt;p&gt;Брейнштормили идеи пока гуляли с собакой, надумали игру про молодого некроманта. Сам он драться не умеет, зато за него это делают восставшие мертвецы.&lt;/p&gt;
&lt;p&gt;Планируем сделать платформер с непрямой боевой системой когда за тебя дерутся поднятые NPC.&lt;/p&gt;
&lt;p&gt;При убиении очередного противника опционально можно отпустить текущего подопечного и призвать нового (пехотинца заменить на лучника к примеру).&lt;/p&gt;
&lt;p&gt;На все про все у нас есть только два дня. Посмотрим, что получится.&lt;/p&gt;
</content:encoded><enclosure url="https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/images/2024-04-13/1.avif" length="0" type="image/avif"/></item><item><title>Подготовка к людуму</title><link>https://misharemmele.ru/posts/2024-04-06/podgotovka-k-lyudumu/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2024-04-06/podgotovka-k-lyudumu/</guid><description>Немного подготовился к предстоящему людуму: создал репозиторий, настроил окружение и собрал hello world игры, где один квадрат падает на другой. Сразу воткнул React чтобы сделать экран меню и нарисовать интерфейс.</description><pubDate>Sat, 06 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Немного подготовился к предстоящему людуму: создал &lt;a href=&quot;https://github.com/michailRemmele/ludum-dare-55&quot;&gt;репозиторий&lt;/a&gt;, настроил окружение и собрал &lt;a href=&quot;https://michailremmele.github.io/ludum-dare-55/&quot;&gt;hello world&lt;/a&gt; игры, где один квадрат падает на другой. Сразу воткнул React чтобы сделать экран меню и нарисовать интерфейс.&lt;/p&gt;
&lt;p&gt;А еще поставил новый пиксель арт редактор на планшет — Pixquare. Aseprite все никак не появится на iPad зато начали появляться подражатели. Это уже второй, из тех, что я нашел.&lt;/p&gt;
&lt;p&gt;Потестил Pixquare. На пару с женой нарисовали нашего пса. Вроде даже получилось похоже на собаку если сильно взгляд не отдалять.&lt;/p&gt;
</content:encoded><enclosure url="https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/images/2024-04-06/1.avif" length="0" type="image/avif"/></item><item><title>Кнопка перезагрузки редактора</title><link>https://misharemmele.ru/posts/2024-03-31/knopka-perezagruzki-redaktora/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2024-03-31/knopka-perezagruzki-redaktora/</guid><description>При работе с редактором бывают ситуации, когда он реагирует на изменение в коде проекта и рисует уведомление с просьбой его перезагрузить. Как оказалось на практике это происходит слишком часто и алерт надоедает.</description><pubDate>Sun, 31 Mar 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;После большой задачи с событиями хочется немного откиснуть и сделать что-то несложное. Тем более через две недели Ludum Dare на котором я буду писать игру.&lt;/p&gt;
&lt;p&gt;При работе с редактором бывают ситуации, когда он реагирует на изменение в коде проекта и рисует уведомление с просьбой его перезагрузить.
Как оказалось на практике это происходит слишком часто и алерт надоедает.
Поэтому я заменил его на небольшую кнопку перезагрузки в футере, которая появляется когда это требуется.&lt;/p&gt;
&lt;p&gt;Ну и раз я выделил в окне редактора место под футер, то можно еще и текущие координаты курсора в него затолкать. Может быть полезно при точном позиционировании объектов или чтобы сориентироваться где ты находишься. На перерисовку координат воткнул небольшой троттлинг чтобы хоть как-то регулировать частоту. Функцию троттлинга я писал во время подготовки к собеседованиям, поэтому чтобы зря не пропадало, затащил к себе в проект.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/videos/2024-03-31/1.mp4&quot;&gt;Смотреть видео&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>Очередь событий</title><link>https://misharemmele.ru/posts/2024-03-11/ochered-sobytij/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2024-03-11/ochered-sobytij/</guid><description>Какую бы книгу или статью по проектированию игровых движков я не брал, всегда есть упоминание того, что события можно обрабатывать синхронно или же откладывать выполнение формируя некую очередь. Каждый подход имеет право на существование, у каждого есть свои плюсы и минусы.</description><pubDate>Mon, 11 Mar 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Со всплытием разобрались, теперь посмотрим какие еще улучшения можно добавить. Какую бы книгу или статью по проектированию игровых движков я не брал, всегда есть упоминание того, что события можно обрабатывать синхронно или же откладывать выполнение формируя некую очередь. Каждый подход имеет право на существование, у каждого есть свои плюсы и минусы.&lt;/p&gt;
&lt;p&gt;В отличии от синхронной рассылки событий, очередь более предсказуема. Системы спокойно заканчивают свое выполнение и только потом накопившиеся события рассылаются всем заинтересованным. Но это так же может быть и проблемой, когда к примеру, система слушает события об удалении объектов и запоздалая реакция приведет к нежелательным сайд-эффектам вроде коллизии с тем чего уже нет.&lt;/p&gt;
&lt;p&gt;Для начала реализовал очередь без усложнений. Никаких лимитов или приоритезации. Несмотря на то, что каждый объект сам является источником событий, очередь одна на всех. В JavaScript синглтон реализуется просто – из модуля экспортируется не сам класс очереди, а уже созданный экземпляр, который используется там где нужно.&lt;/p&gt;
&lt;p&gt;Полностью перейти на использование очереди у меня не получилось. Как я говорил выше, в некоторых случаях когда дело касается добавления или удаления объектов из сцены, отложенная реакция приводила к ошибкам и попыткам обратиться к уже уничтоженным данным. Поэтому наряду с методом dispatchEvent() для отправки события в очередь, я оставил dispatchEventImmediately() для немедленного оповещения. Второй используется только для уведомления об изменениях на сцене и вообще служит только для исключительных кейсов, когда срочность очень важно и без нее никак.&lt;/p&gt;
</content:encoded><enclosure url="https://s3.ru1.storage.beget.cloud/4d7b85999aed-legendary-azamat/blog/images/2024-03-11/1.avif" length="0" type="image/avif"/></item><item><title>Всплытие событий</title><link>https://misharemmele.ru/posts/2024-02-25/vsplytie-sobytij/</link><guid isPermaLink="true">https://misharemmele.ru/posts/2024-02-25/vsplytie-sobytij/</guid><description>Для чего вообще может понадобиться всплытие событий в движке? Представим, что есть система, которая обрабатывает различные эффекты вроде &quot;Нанести персонажу X урона&quot;, &quot;Вылечить игрока на Y хп&quot; или &quot;Отравить врага на Z секунд&quot;. Как узнать о том, что к какому-то объекту применили эффект?</description><pubDate>Sun, 25 Feb 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Для чего вообще может понадобиться всплытие событий в движке?&lt;/p&gt;
&lt;p&gt;Представим, что есть система, которая обрабатывает различные эффекты вроде &quot;Нанести персонажу X урона&quot;, &quot;Вылечить игрока на Y хп&quot; или &quot;Отравить врага на Z секунд&quot;. Событие эффекта может иметь единый тип, но разный набор параметров и тогда системе достаточно следить только за этим событием и подбирать правильную стратегию реагирования. Но как узнать о том, что к какому-то объекту применили эффект? Без всплытия придется перебрать вообще все объекты сцены и на каждый подписаться. При этом нужно не забывать, что объекты могут исчезать и появляться, поэтому нужно следить и за этим тоже. На выходе имеем больше boilerplate кода и кучу одинаковых обработчиков на каждом объекте. В то же время, если бы события умели всплывать наверх, то одной подписки на корневой объект (например сцену) было бы достаточно чтобы следить за всеми сразу.&lt;/p&gt;
&lt;p&gt;Само по себе всплытие реализовать достаточно просто. Если у объекта есть доступ к родителю и списку его подписчиков, то поднимаясь наверх можно по очереди уведомить о событии всех заинтересованных.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const event = {
  type,
  target: this,
  currentTarget: this
};

let target = this;

while (target !== null) {
  event.currentTarget = target;

  const listeners = target.getListeners(event.type);
  listeners?.forEach((listener) =&amp;gt; listener(event));

  target = target.parent;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Связь между объектами у меня уже есть, но для того чтобы событие поднималось аж до сцены, должна существовать родительская связь и со сценой тоже.&lt;/p&gt;
&lt;p&gt;Все бы ничего, но апи сцены по добавлению объектов совсем не подходило под текущие требования. К примеру, все объекты лежат в сцене плоским списком в независимости от их связей между собой. Когда-то это было просто и удобно, но сейчас все хочется переделать и привести к одному виду.&lt;/p&gt;
&lt;p&gt;Чтобы у сцены и объекта был единый интерфейс по работе с родителями и детьми я добавил немножечко наследования. В основе лежит EventTarget класс c возможностями подписки и генерации событий, затем идет Entity – базовый класс, поддерживающий методы вроде appendChild(), removeChild(), getEntityById() и так далее. Наконец, от него уже наследуются Scene и Actor (переименовал GameObject в Actor потому что надоело писать много букв да и приставка Game ни туда ни сюда).&lt;/p&gt;
&lt;p&gt;Код движка стал немного аккуратнее и у меня даже получилось щегольнуть знаниями, полученными после нарешивая задач на литкоде. Для поиска объекта в дереве по идентификатору я использовал &lt;a href=&quot;https://en.wikipedia.org/wiki/Breadth-first_search&quot;&gt;BFS&lt;/a&gt; метод с очередью на односвязном списке.&lt;/p&gt;
</content:encoded></item></channel></rss>