<?xml version="1.0" encoding="utf-8" ?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <atom:link href="https://mopsicus.ru/rss.xml" rel="self" type="application/rss+xml"/>
    <title>Блог Mopsicus – про разработку, игры и личный опыт</title>
    <description>Блог Mopsicus – про разработку, игры и личный опыт: заметки, проекты, Shardy, Unity, C#, Node.js, opensource</description>
    <language>ru</language>
    <link>https://mopsicus.ru</link>
    <generator>Trafaret v1.2.0</generator>
    <managingEditor>mail@mopsicus.ru (Igor Lopatin)</managingEditor>
    <webMaster>mail@mopsicus.ru (Igor Lopatin)</webMaster>
    <category>website</category>
    <item>
  <title>Починил RSS</title>
  <description>&lt;p&gt;Починил все RSS ленты на сайте. Оказалось, что там были неправильные пути для всех картинок &lt;span class="emoji"&gt;🥲&lt;/span&gt; Теперь можно смело подписываться, кто пользуется RSS читалками. Ссылка в подвале, справа.&lt;/p&gt;</description>
  <guid>https://mopsicus.ru/blog/website-rss-fix.html</guid>
  <link>https://mopsicus.ru/blog/website-rss-fix.html</link>
  <pubDate>Mon, 02 Mar 2026 12:35:00 +0300</pubDate>
  <comments>https://mopsicus.ru/blog/website-rss-fix.html#comments</comments>
  <author>mail@mopsicus.ru (Igor Lopatin)</author>
  <category>website</category>
  <category>blog</category>
  <category>rss</category>
</item>
<item>
  <title>Восстановление аккаунта разработчика Google</title>
  <description>&lt;p&gt;Удалось восстановить и подтвердить свой древний аккаунт разработчика в Google Play. Несколько дней переписки и апелляций, составление «правильных» писем в саппорт с помощью ChatGPT и — аккаунт разлочен и подтверждён &lt;span class="emoji"&gt;😅&lt;/span&gt;&lt;/p&gt;&lt;p&gt;В итоге задолбал их и дошёл под ручного подтверждения, потому что бот настойчиво отказывался принимать доки. Главное не сдаваться &lt;span class="emoji"&gt;😬&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Если есть вопросы, что и куда писал — задавайте в комментах, на &lt;a href="https://mopsicus.rumailto:mail@mopsicus.ru"&gt;почту&lt;/a&gt; или в &lt;a href="https://mopsicus.ruhttps://t.me/mopsicus"&gt;телегу&lt;/a&gt;, может что полезное подскажу.&lt;/p&gt;</description>
  <guid>https://mopsicus.ru/blog/restore-google-account.html</guid>
  <link>https://mopsicus.ru/blog/restore-google-account.html</link>
  <pubDate>Fri, 27 Feb 2026 14:10:00 +0300</pubDate>
  <comments>https://mopsicus.ru/blog/restore-google-account.html#comments</comments>
  <author>mail@mopsicus.ru (Igor Lopatin)</author>
  <category>google</category>
  <category>chatgpt</category>
  <category>advice</category>
</item>
<item>
  <title>Лучшие практики Sprite Atlas в Unity</title>
  <description>&lt;iframe width="560" height="315" src="https://www.youtube.com/embed/hXlpnwD-TgY?si=qUEep0Dx2wGG-kDE" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen&gt;&lt;/iframe&gt;</description>
  <guid>https://mopsicus.ru/blog/unity-sprite-atlas-best-practices.html</guid>
  <link>https://mopsicus.ru/blog/unity-sprite-atlas-best-practices.html</link>
  <pubDate>Wed, 25 Feb 2026 13:00:00 +0300</pubDate>
  <comments>https://mopsicus.ru/blog/unity-sprite-atlas-best-practices.html#comments</comments>
  <author>mail@mopsicus.ru (Igor Lopatin)</author>
  <category>unity</category>
  <category>ui</category>
  <category>2d</category>
  <category>optimization</category>
  <category>video</category>
</item>
<item>
  <title>UniText — рендеринг текста для Unity</title>
  <description>&lt;p&gt;Вчера увидел в Unity паблике про свежий текстовый движок для Unity, корректно отображающий все системы письма. В основе тот же движок формирования текста который используется в Chrome, Firefox, Adobe InDesign и Android.&lt;/p&gt;&lt;p&gt;Пишут что быстрее, легче, сильнее чем TMP &lt;span class="emoji"&gt;😅&lt;/span&gt; Но самое главное что я для себя отметил: полная &lt;strong&gt;поддержка эмоджи с использованием системных шрифтов&lt;/strong&gt;! То есть, не нужно тащить в билд атласы с эмоджи. Да и вообще, сами атласы шрифтов для UniText вроде как тоже легче.&lt;/p&gt;&lt;p&gt;&lt;a href="https://mopsicus.ruhttps://github.com/LightSideMeowshop/unitext"&gt;UniText&lt;/a&gt; — полная поддержка Unicode 17.0, системные emoji, 150+ языков, zero-allocation архитектура, шейпинг HarfBuzz.&lt;/p&gt;&lt;div class="fotorama"&gt; &lt;img class="emerge float-center" src="https://mopsicus.ru/images/assets/unitext-1.87df8676.jpg" alt="UniText платформы"&gt; &lt;img class="emerge float-center" src="https://mopsicus.ru/images/assets/unitext-2.8d06a451.jpg" alt="UniText возможности"&gt; &lt;img class="emerge float-center" src="https://mopsicus.ru/images/assets/unitext-3.51cc80bc.jpg" alt="UniText бенчмарк"&gt; &lt;img class="emerge float-center" src="https://mopsicus.ru/images/assets/unitext-4.da46466a.jpg" alt="UniText сравнение"&gt; &lt;/div&gt;&lt;p&gt;Есть &lt;a href="https://mopsicus.ruhttps://unity.lightside.media/unitext/docs/"&gt;отличная дока&lt;/a&gt;, открытый код, в общем — выглядит всё очень интересно, пока.&lt;/p&gt;&lt;h3&gt;Потенциальные трудности миграции с TMP&lt;/h3&gt;&lt;ul&gt; &lt;li&gt;много сторонних ассетов основано на TMP&lt;/li&gt; &lt;li&gt;какие-то редакторские скрипты надо переписывать&lt;/li&gt; &lt;li&gt;атласы шрифтов переделывать, эффекты, материалы&lt;/li&gt; &lt;li&gt;UI рефакторить&lt;/li&gt; &lt;/ul&gt;&lt;p&gt;Если UI не сложный, наверное проще переехать. Было бы неплохо если сделают какой-нибудь конвертер TMP → UniText, для более легкой миграции.&lt;/p&gt;&lt;p&gt;Ещё на первый взгляд стало больше конфиг файлов всяких, но думаю это плата за модульную архитектуру, или дело привычки 🤷&lt;/p&gt;&lt;p&gt;Сделал быстрый тест — сбилдил для андроид — работает жеж &lt;span class="emoji"&gt;🔥&lt;/span&gt; Буду ковырять дальше, возможно есть подводные камни.&lt;/p&gt;</description>
  <guid>https://mopsicus.ru/blog/unitext-font-engine.html</guid>
  <link>https://mopsicus.ru/blog/unitext-font-engine.html</link>
  <pubDate>Thu, 19 Feb 2026 11:45:00 +0300</pubDate>
  <comments>https://mopsicus.ru/blog/unitext-font-engine.html#comments</comments>
  <author>mail@mopsicus.ru (Igor Lopatin)</author>
  <category>csharp</category>
  <category>github</category>
  <category>unity</category>
  <category>package</category>
  <category>tmp</category>
  <category>emoji</category>
  <category>fonts</category>
</item>
<item>
  <title>Znak — 2D движок для игр</title>
  <description>&lt;p&gt;Кто интересуется Unity и ECS наверняка слышали про товарища Leopotam и его разработки. С ним мы периодически общаемся ещё со времён самого первого ECS &lt;span class="emoji"&gt;😅&lt;/span&gt; Так вот, он разрабатывает не только новые версии своей ECS, но и свой 2D движок &lt;a href="https://mopsicus.ruhttps://leopotam.ru/53/"&gt;Znak&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Это простой 2D движок, сначала был написан на Golang, потом портирован на Nimlang, компилируется за секунды в какие-то килобайты, которые даже считать смешно. Есть уже игра в разработке — &lt;a href="https://mopsicus.ruhttps://vk.com/club233195130"&gt;Танчики&lt;/a&gt;.&lt;/p&gt;&lt;ul&gt; &lt;li&gt;Рендер на OpenGL 3.3/WebGL2/GLFW 3.4&lt;/li&gt; &lt;li&gt;Бандлы&lt;/li&gt; &lt;li&gt;SDF-шрифты&lt;/li&gt; &lt;li&gt;Пользовательский ввод&lt;/li&gt; &lt;li&gt;Шейдеры, материалы, текстуры, атласы&lt;/li&gt; &lt;li&gt;HTTP, WebSockets&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;a href="https://mopsicus.ruhttps://gitverse.ru/nimlang/znak"&gt;Исходники открыты&lt;/a&gt;, велкоме, как говорится.&lt;/p&gt;</description>
  <guid>https://mopsicus.ru/blog/znak-2d-game-engine-leopotam.html</guid>
  <link>https://mopsicus.ru/blog/znak-2d-game-engine-leopotam.html</link>
  <pubDate>Fri, 06 Feb 2026 10:45:00 +0300</pubDate>
  <comments>https://mopsicus.ru/blog/znak-2d-game-engine-leopotam.html#comments</comments>
  <author>mail@mopsicus.ru (Igor Lopatin)</author>
  <category>nimlang</category>
  <category>gitverse</category>
  <category>games</category>
  <category>engine</category>
</item>
<item>
  <title>Использование C# 14 в Unity</title>
  <description>&lt;p&gt;It’s Possible To Use C# 14 In Unity! — так называется свежий видос на одном ютуб канале. Несмотря на то что Unity официально не поддерживает C# 14 «из коробки», есть способы включить новые фичи 14 версии. Ссылки для патчинга прилагаются в описании. Так-так-так…&lt;/p&gt;&lt;p&gt;В самом видео говорится, что многое из списка скорее всего не будет работать, но это уже совсем другая история &lt;span class="emoji"&gt;😶‍🌫️&lt;/span&gt;&lt;/p&gt;&lt;div class="media-wrap"&gt; &lt;iframe width="560" height="315" src="https://www.youtube.com/embed/9BO4gkp90Do?si=bwJnt1_nilC4plEQ" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen&gt;&lt;/iframe&gt; &lt;/div&gt;&lt;p&gt;В новых версиях в основном «сахар», но тем не менее, это может делать код более чистым, добавляет новые паттерны и кое-где производительность.&lt;/p&gt;&lt;p&gt;Extension Members — можно расширять типы не только методами, но и свойствами, индексаторами и операторами.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;public static class EnumerableExtensions {
    extension&amp;lt;T&amp;gt;(IEnumerable&amp;lt;T&amp;gt; source) {
        public bool IsEmpty =&amp;gt; !source.Any();
        public T this[int index] =&amp;gt; source.Skip(index).First();
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Null-conditional присваивание (?.=) — позволяет записывать значения только если объект не null, без if-проверок.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;player?.Health = 100;
player?.Score += 10;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Использование &lt;code&gt;nameof&lt;/code&gt; с необобщёнными типами.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Debug.Log(nameof(List&amp;lt;&amp;gt;)); // &amp;quot;List&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Использование &lt;code&gt;Span&lt;/code&gt; и &lt;code&gt;ReadOnlySpan&lt;/code&gt;.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Span&amp;lt;byte&amp;gt; data = someArray;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Лямбды с модификаторами — можно использовать модификаторы вроде &lt;code&gt;ref&lt;/code&gt;, out прямо в лямбдах без указания типа.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;TryParse&amp;lt;int&amp;gt; parse = (text, out result) =&amp;gt; int.TryParse(text, out result);
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Ну и &lt;code&gt;field&lt;/code&gt; в свойствах, наверно самое полезное, как по мне — можно обращаться к скрытому полю свойства прямо внутри тела аксессоров&lt;/p&gt;&lt;pre&gt;&lt;code&gt;// раньше
public int Health { get; set; }

private int _health;

public int Health {
    get =&amp;gt; _health;
    set =&amp;gt; _health = Mathf.Clamp(value, 0, 100);
}

//по новому
public int Health {
    get;
    set =&amp;gt; field = Mathf.Clamp(value, 0, 100);
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Но помните, Unity официально не поддерживает самые свежие версии C#, так что — на свой страх и риск &lt;span class="emoji"&gt;😅&lt;/span&gt;&lt;/p&gt;</description>
  <guid>https://mopsicus.ru/blog/unity-csharp-14.html</guid>
  <link>https://mopsicus.ru/blog/unity-csharp-14.html</link>
  <pubDate>Fri, 30 Jan 2026 15:38:00 +0300</pubDate>
  <comments>https://mopsicus.ru/blog/unity-csharp-14.html#comments</comments>
  <author>mail@mopsicus.ru (Igor Lopatin)</author>
  <category>csharp</category>
  <category>github</category>
  <category>unity</category>
  <category>video</category>
</item>
<item>
  <title>Год ИИ</title>
  <description>&lt;p&gt;Год активного использования нейросетей, агентов, ИИ, etc!&lt;br&gt;Будут различные прототипы того, что долго откладывал в стол &lt;span class="emoji"&gt;😅&lt;/span&gt;&lt;/p&gt;</description>
  <guid>https://mopsicus.ru/blog/year-ai-2026.html</guid>
  <link>https://mopsicus.ru/blog/year-ai-2026.html</link>
  <pubDate>Sat, 24 Jan 2026 19:28:00 +0300</pubDate>
  <comments>https://mopsicus.ru/blog/year-ai-2026.html#comments</comments>
  <author>mail@mopsicus.ru (Igor Lopatin)</author>
  <category>ai</category>
  <category>vibecoding</category>
  <category>copilot</category>
  <category>thoughts</category>
</item>
<item>
  <title>Как получить размеры TMP</title>
  <description>&lt;p&gt;Во всех наших играх есть список друзей и чаты между ними. Уже много лет для его реализации я использую UIS, свой же скрипт для бесконечных списков. И когда надо посчитать высоту элемента чата (облачко, speech balloon), это делалось с помощью старой функции подсчета слов, переносов строк и т.п. И в общем-то, работает это до сих пор нормально.&lt;/p&gt;&lt;p&gt;Но сейчас при создании новой игры, я нашёл более правильное решение этой задачи. Оказывается &lt;span class="emoji"&gt;😅&lt;/span&gt; у TMP есть специальный метод который позволяет получить размеры компонента до! установки туда текста:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;var size = _message.GetPreferredValues(text);
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;И размеры будут с учётом якорей, отступов и прочего. А если нужно получить, например, размеры с фиксированной шириной и изменяемой высотой, то можно задать это дополнительными параметрами:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;var size = _message.GetPreferredValues(text, MAX_ITEM_WIDTH, Mathf.Infinity);
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Так что в новой игре чатики будут красивее &lt;span class="emoji"&gt;😁&lt;/span&gt;&lt;/p&gt;&lt;div class="alert"&gt; &lt;p&gt;&lt;span id="uis-stars"&gt;&lt;/span&gt; &lt;i class="fa-regular fa-star"&gt;&lt;/i&gt; Все &lt;a href="https://mopsicus.ruhttps://github.com/mopsicus/uis"&gt;исходники UIS&lt;/a&gt; доступны на Github&lt;/p&gt; &lt;/div&gt;</description>
  <guid>https://mopsicus.ru/blog/unity-tmp-sizes.html</guid>
  <link>https://mopsicus.ru/blog/unity-tmp-sizes.html</link>
  <pubDate>Fri, 23 Jan 2026 15:40:00 +0300</pubDate>
  <comments>https://mopsicus.ru/blog/unity-tmp-sizes.html#comments</comments>
  <author>mail@mopsicus.ru (Igor Lopatin)</author>
  <category>unity</category>
  <category>tmp</category>
  <category>csharp</category>
  <category>ui</category>
</item>
<item>
  <title>MCP для Unity</title>
  <description>&lt;p&gt;Наткнулся тут на &lt;a href="https://mopsicus.ruhttps://github.com/CoplayDev/unity-mcp"&gt;MCP for Unity&lt;/a&gt;. Это «мост» между редактором Unity и внешними AI-ассистентами/IDE через MCP (Model Context Protocol). В общем, позволяет выполнять действия напрямую в Unity через команды на естественном языке, так же как мы вайбкодим в чатике 😁&lt;/p&gt;&lt;h2&gt;Зачем&lt;/h2&gt;&lt;ul&gt; &lt;li&gt;автоматизация монотонных задач, например: создать объекты, настроить сцены, добавить компоненты&lt;/li&gt; &lt;li&gt;собственные команды/инструменты, можно расширять собственными инструментами и дать их в распоряжение нейронки&lt;/li&gt; &lt;li&gt;интеграция с MCP клиентами, не только с Claude, но и другими, которые поддерживают MCP&lt;/li&gt; &lt;/ul&gt;&lt;h2&gt;Что может&lt;/h2&gt;&lt;p&gt;Поставил. Подключил к своему Copilot. Интересная штука оказалась. В дополнение к Claude теперь можно какие-то вещи делегировать туда и оно будет работать в редакторе:&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;li&gt;лазить по пунктам меню&lt;/li&gt; &lt;li&gt;запускать тесты&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://github.com/CoplayDev/unity-mcp/blob/main/README.md#key-features-"&gt;и многое другое&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt;&lt;img class="emerge float-center" src="https://mopsicus.ru/images/assets/mcp-for-unity-flow.66cf51ae.gif" alt="MCP для Unity"&gt;&lt;p&gt;Немного потестировал в своих обычных кейсах, например: скопируй префаб Х из папки Y, удали в нём компонент Z и добавь компонент J, переименуй. В общем, то что обычно руками делал. Ну работает конечно, но не быстро 😅 Ещё написано что можно &lt;a href="https://mopsicus.ruhttps://github.com/CoplayDev/unity-mcp/blob/main/docs/CUSTOM_TOOLS.md"&gt;делать свои расширения&lt;/a&gt;, вот это уже интересно. И есть возможность выполнять несколько команд в одном вызове, это должно ускорить процесс…&lt;/p&gt;&lt;p&gt;Пока продолжаю наблюдение, так сказать. Попробую использовать в каких-то более практичесих задачах, может будет какая-то польза.&lt;/p&gt;</description>
  <guid>https://mopsicus.ru/blog/unity-mcp.html</guid>
  <link>https://mopsicus.ru/blog/unity-mcp.html</link>
  <pubDate>Mon, 19 Jan 2026 11:34:00 +0300</pubDate>
  <comments>https://mopsicus.ru/blog/unity-mcp.html#comments</comments>
  <author>mail@mopsicus.ru (Igor Lopatin)</author>
  <category>unity</category>
  <category>github</category>
  <category>links</category>
  <category>ai</category>
  <category>vibecoding</category>
  <category>plugin</category>
  <category>copilot</category>
</item>
<item>
  <title>Маркетинг в эпоху нейросетей</title>
  <description>&lt;p&gt;Вчера прочитал пост, что сейчас делать MVP по большому счёту уже не нужно и даже «вредно». С приходом нейросетей больше нет проблемы быстро что-то сделать: несложный сервис, приложение или клон можно наколдовать за короткое время.&lt;/p&gt;&lt;p&gt;Основная проблема теперь — найти, кому и как это продать. Об этом стоит думать ещё до разработки и именно туда вкладывать основное время и деньги. Впрочем, так уже давно происходит — просто сейчас это стало ещё актуальнее.&lt;/p&gt;&lt;p&gt;В контексте игр это работает точно так же. Сделать несложную игру стало ещё проще, игр выпускается очень много, если не заниматься маркетингом — про неё просто никто не узнает. Вот читаю канал Кирилла Орешкина и примерно видно как работает этот процесс: есть трейлер, есть страница в Стим, нагоняется трафик, проверяются различные метрики, вишлисты и прочее. В этот момент игры ещё вообще может не быть 😁 Так и живём…&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;</description>
  <guid>https://mopsicus.ru/blog/mvp-marketing-game-ai.html</guid>
  <link>https://mopsicus.ru/blog/mvp-marketing-game-ai.html</link>
  <pubDate>Tue, 13 Jan 2026 10:15:00 +0300</pubDate>
  <comments>https://mopsicus.ru/blog/mvp-marketing-game-ai.html#comments</comments>
  <author>mail@mopsicus.ru (Igor Lopatin)</author>
  <category>marketing</category>
  <category>ai</category>
  <category>vibecoding</category>
  <category>thoughts</category>
  <category>games</category>
</item>
<item>
  <title>Античит для Unity на минималках</title>
  <description>&lt;p&gt;Варианты защиты своей игры, какие есть типы защиты, защита без сервера, защита значений, бесплатные и платные возможности, зачем вообще делать античит и защищать какие-то данные — что реально можно сделать инди-разработчику.&lt;/p&gt;&lt;p&gt;Главное что можно сразу сказать:&lt;/p&gt;&lt;ul&gt; &lt;li&gt;нельзя защитить клиент на 100%&lt;/li&gt; &lt;li&gt;любой локальный античит будет взломан&lt;/li&gt; &lt;li&gt;Unity билд довольно легко расковырять&lt;/li&gt; &lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Основная задача:&lt;/strong&gt; отсечь 80–90% «случайных» читеров, сохранить честность различных лидерборд и рейтингов, не тратить месяцы разработки.&lt;/p&gt;&lt;div class="tagline"&gt; &lt;p&gt;Античит — это не про «сделать чит невозможным». Это про сделать чит дорогим, нестабильным и бессмысленным.&lt;/p&gt; &lt;/div&gt;&lt;p&gt;Какие есть самые распространённые читы с которыми может (скорее всего) столкнуться разработчик игр:&lt;/p&gt;&lt;ul&gt; &lt;li&gt;Memory hack (скорость, здоровье, урон)&lt;/li&gt; &lt;li&gt;Подмена логики (patched APK/modded build)&lt;/li&gt; &lt;li&gt;Спидхак (Time.timeScale, SpeedHack)&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;div class="alert-info"&gt; &lt;p&gt;&lt;b&gt;Главное правило:&lt;/b&gt; клиенту нельзя доверять!&lt;/p&gt; &lt;/div&gt;&lt;p&gt;Примерно 90% игр (не ААА, а инди) ломаются с каким-нибудь &lt;abbr title="программа для взлома и модификации игр, позволяет находить и менять значения в памяти другой программы (игры)"&gt;Cheat Engine&lt;/abbr&gt;, если разработчик не озадачился минимальной защитой, как локальной так и серверной.&lt;/p&gt;&lt;p&gt;Составил список самых простых, но тем не менее рабочих способов быстро усложнить жизнь читерам и ботоводам, то что можно быстро сделать в коде. Про сторонние ассеты и библиотеки — ниже.&lt;/p&gt;&lt;h2&gt;Защита без сервера&lt;/h2&gt;&lt;h3&gt;Определение спидхака&lt;/h3&gt;&lt;pre&gt;&lt;code&gt;float realDelta = Time.unscaledDeltaTime;
float gameDelta = Time.deltaTime;

if (gameDelta / realDelta &amp;gt; 1.5f) {
    Flag();
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Ловим &lt;code&gt;TimeScale&lt;/code&gt; и системные спидхаки, просто и работает.&lt;/p&gt;&lt;h3&gt;Защита значений&lt;/h3&gt;&lt;pre&gt;&lt;code&gt;private int _gold;
private int _goldKey;

public int Gold {
    get =&amp;gt; _gold ^ _goldKey;
    set {
        _goldKey = Random.Range(int.MinValue, int.MaxValue);
        _gold = value ^ _goldKey;
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Не совсем защита, а скорее усложнение при поиске точных значений в памяти.&lt;/p&gt;&lt;h3&gt;Контроль максимума&lt;/h3&gt;&lt;pre&gt;&lt;code&gt;if (damage &amp;gt; maxDamage * 1.2f) {
    Flag();
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Отслеживание максимальной скорости, урона и теоретического максимума, там где ресурсы растут без источника и т.д.&lt;/p&gt;&lt;div class="alert-info"&gt; &lt;p&gt;&lt;b&gt;Важно:&lt;/b&gt; не банить сразу, а накапливать «флаги». Любой античит может ошибаться. Об этом ниже.&lt;/p&gt; &lt;/div&gt;&lt;h2&gt;Защита в онлайн-играх&lt;/h2&gt;&lt;p&gt;Авторитарный сервер — наверное самый распространённый вариант — является единственным источником истины, он обрабатывает все механики, движения и взаимодействия, чтобы исключить читерство.&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;получение команд неподходящих под состояние игры&lt;/li&gt; &lt;li&gt;другие подозрительные паттерны&lt;/li&gt; &lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Главное:&lt;/strong&gt; не банить сразу, не показывать сообщение «ты читер» &lt;span class="emoji"&gt;😅&lt;/span&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;нестабильный FPS&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;div class="tagline"&gt; &lt;p&gt;Идеально: читер не знает за что его поймали и что его вообще поймали.&lt;/p&gt; &lt;/div&gt;&lt;p&gt;Если банить сразу:&lt;/p&gt;&lt;ul&gt; &lt;li&gt;читер узнает точный триггер&lt;/li&gt; &lt;li&gt;он чинит за 10 минут&lt;/li&gt; &lt;li&gt;выкладывает «рабочую версию»&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;blockquote&gt; &lt;p&gt;Лучший античит — тот, который не даёт обратной связи. &lt;cite&gt;Джейсон Стейтем&lt;/cite&gt;&lt;/p&gt; &lt;/blockquote&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;кидать в отдельный matchmaking&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;strong&gt;И самое важное:&lt;/strong&gt; читер продолжает играть и не создаёт новый аккаунт!&lt;/p&gt;&lt;h2&gt;Простая система флагов&lt;/h2&gt;&lt;p&gt;Простейшая система: флаги копятся со временем, могут затухать, учитывается частота и разнообразие. В онлайн игре всё это конечно должно делаться на стороне сервера, или комбо &lt;span class="emoji"&gt;😅&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;&lt;code&gt;enum CheatFlag {
    SpeedHack,
    TimeHack,
    InvalidDamage,
    Teleport,
    RateLimit
}

Dictionary&amp;lt;CheatFlag, int&amp;gt; flags = new();

void AddFlag(CheatFlag flag) {
    flags.TryAdd(flag, 0);
    flags[flag]++;
    int total = flags.Values.Sum();
    if (total &amp;gt; 10) {
        SoftBan();
    }
}
&lt;/code&gt;&lt;/pre&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;/ul&gt;&lt;p&gt;В общем, античит — это наблюдение, накопление и изоляция.&lt;/p&gt;&lt;h2&gt;Платные и бесплатные ассеты&lt;/h2&gt;&lt;h3&gt;&lt;a href="https://mopsicus.ruhttps://assetstore.unity.com/packages/tools/utilities/anti-cheat-toolkit-202695"&gt;Anti-Cheat Toolkit&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Универсальный античит, один из самых популярных ассетов для Unity, который предлагает:&lt;/p&gt;&lt;ul&gt; &lt;li&gt;защиту переменных в памяти&lt;/li&gt; &lt;li&gt;обфускацию PlayerPrefs&lt;/li&gt; &lt;li&gt;обнаружение speedhack и Time hack&lt;/li&gt; &lt;li&gt;инжекционный хук-детектор&lt;/li&gt; &lt;li&gt;обнаружение wallhack и прочие механизмы&lt;/li&gt; &lt;li&gt;есть GUI для инспектора и примеры кода&lt;/li&gt; &lt;/ul&gt;&lt;h3&gt;&lt;a href="https://mopsicus.ruhttps://assetstore.unity.com/packages/tools/utilities/anti-cheat-free-v2025-140341"&gt;Anti Cheat Free v2025&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt; &lt;li&gt;защита типов переменных (int, float, Vector3 и др.)&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;h3&gt;&lt;a href="https://mopsicus.ruhttps://assetstore.unity.com/packages/tools/network/guard-multiplayer-anti-cheat-321434"&gt;GUARD – Multiplayer Anti Cheat&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt; &lt;li&gt;античит для сетевых игр&lt;/li&gt; &lt;li&gt;обнаружение читов в многопользовательской среде&lt;/li&gt; &lt;li&gt;подходит, если игра использует свой сетевой код (не только Unity Netcode), есть поддержка Mirror&lt;/li&gt; &lt;li&gt;обнаруживает Melon, BepInEx, DLL инъекции, патчинг инструкций, дебагеры, виртуальные машины&lt;/li&gt; &lt;/ul&gt;&lt;h3&gt;&lt;a href="https://mopsicus.ruhttps://assetstore.unity.com/packages/tools/utilities/easy-anti-cheat-tools-38477"&gt;Easy Anti Cheat Tools&lt;/a&gt;&lt;/h3&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;манипуляций с PlayerPrefs&lt;/li&gt; &lt;li&gt;расширенное использование PlayerPrefs&lt;/li&gt; &lt;li&gt;защищённые типы данных&lt;/li&gt; &lt;li&gt;простые в использовании хеширование и криптография&lt;/li&gt; &lt;/ul&gt;&lt;h3&gt;&lt;a href="https://mopsicus.ruhttps://github.com/DevsDaddy/GameShield"&gt;Unity Game Shield&lt;/a&gt;&lt;/h3&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;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;h3&gt;&lt;a href="https://mopsicus.ruhttps://github.com/ookii-tsuki/SafeValues"&gt;SafeValues&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt; &lt;li&gt;скрывает переменные в памяти&lt;/li&gt; &lt;li&gt;шифрует и расширяет PlayerPrefs&lt;/li&gt; &lt;li&gt;обнаруживает спидхаки&lt;/li&gt; &lt;/ul&gt;&lt;h2&gt;Тяжёлая артиллерия&lt;/h2&gt;&lt;h3&gt;&lt;a href="https://mopsicus.ruhttps://www.guardsquare.com"&gt;DexGuard (Guardsquare)&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt; &lt;li&gt;продвинутая обфускация DEX&lt;/li&gt; &lt;li&gt;защита от реверс-инжиниринга&lt;/li&gt; &lt;li&gt;анти-debug/анти-emulator&lt;/li&gt; &lt;li&gt;anti-tampering (подпись, целостность)&lt;/li&gt; &lt;li&gt;runtime checks&lt;/li&gt; &lt;li&gt;string/class encryption&lt;/li&gt; &lt;li&gt;защита JNI/native кода&lt;/li&gt; &lt;/ul&gt;&lt;p&gt;В контексте Unity:&lt;/p&gt;&lt;ul&gt; &lt;li&gt;работает поверх IL2CPP&lt;/li&gt; &lt;li&gt;защищает Java-слой, bootstrap, плагины&lt;/li&gt; &lt;li&gt;сильно усложняет modded APK&lt;/li&gt; &lt;li&gt;часто используется в F2P с реальной экономикой&lt;/li&gt; &lt;/ul&gt;&lt;h3&gt;&lt;a href="https://mopsicus.ruhttps://licelus.com/products/dexprotector"&gt;DexProtector (Licel)&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt; &lt;li&gt;anti-tampering&lt;/li&gt; &lt;li&gt;anti-debug / anti-hook&lt;/li&gt; &lt;li&gt;anti-emulator&lt;/li&gt; &lt;li&gt;string &amp;amp; class encryption&lt;/li&gt; &lt;li&gt;контроль целостности&lt;/li&gt; &lt;li&gt;защита native библиотек&lt;/li&gt; &lt;/ul&gt;&lt;p&gt;В контексте Unity:&lt;/p&gt;&lt;ul&gt; &lt;li&gt;часто используется как альтернатива DexGuard&lt;/li&gt; &lt;li&gt;подходит для игр среднего масштаба&lt;/li&gt; &lt;li&gt;хорошо режет repack + LuckyPatcher&lt;/li&gt; &lt;/ul&gt;&lt;h3&gt;&lt;a href="https://mopsicus.ruhttps://digital.ai/products/application-security/"&gt;Arxan GuardIT (Digital.ai)&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt; &lt;li&gt;одна из самых жёстких систем защиты&lt;/li&gt; &lt;li&gt;используется в крупных играх и корпорациях&lt;/li&gt; &lt;li&gt;runtime-mutation&lt;/li&gt; &lt;li&gt;self-defending code&lt;/li&gt; &lt;li&gt;анти-hook, анти-dump, анти-debug&lt;/li&gt; &lt;li&gt;серверные проверки целостности&lt;/li&gt; &lt;/ul&gt;&lt;p&gt;В контексте Unity:&lt;/p&gt;&lt;ul&gt; &lt;li&gt;применяется в крупных мобильных и PC-играх&lt;/li&gt; &lt;li&gt;часто в связке с серверным античитом&lt;/li&gt; &lt;/ul&gt;&lt;h3&gt;&lt;a href="https://mopsicus.ruhttps://www.appdome.com"&gt;Appdome&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt; &lt;li&gt;защита без изменения исходного кода&lt;/li&gt; &lt;li&gt;cloud-pipeline&lt;/li&gt; &lt;li&gt;anti-tampering&lt;/li&gt; &lt;li&gt;anti-debug&lt;/li&gt; &lt;li&gt;обнаружение root/jailbreak&lt;/li&gt; &lt;/ul&gt;&lt;p&gt;В контексте Unity:&lt;/p&gt;&lt;ul&gt; &lt;li&gt;подходит, если нет доступа к исходникам Android-части&lt;/li&gt; &lt;li&gt;часто берут издатели&lt;/li&gt; &lt;/ul&gt;&lt;h3&gt;Virbox/Themida/VMProtect&lt;/h3&gt;&lt;ul&gt; &lt;li&gt;применяются для PC-версий&lt;/li&gt; &lt;li&gt;защищают exe/native IL2CPP&lt;/li&gt; &lt;li&gt;часто конфликтуют с античитами&lt;/li&gt; &lt;li&gt;могут вызывать срабатывания у антивирусов&lt;/li&gt; &lt;/ul&gt;&lt;hr&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;Банить сразу — значит терять игроков быстрее, чем читеров &lt;span class="emoji"&gt;🥲&lt;/span&gt; Надеюсь ваши игры будут такие популярные и успешные, что вам хватит на любую защиту &lt;span class="emoji"&gt;🤑&lt;/span&gt; Потому что в таком случае она вам 100% понадобится.&lt;/p&gt;</description>
  <guid>https://mopsicus.ru/notes/minimum-game-anticheat.html</guid>
  <link>https://mopsicus.ru/notes/minimum-game-anticheat.html</link>
  <pubDate>Sat, 31 Jan 2026 16:35:00 +0300</pubDate>
  <comments>https://mopsicus.ru/notes/minimum-game-anticheat.html#comments</comments>
  <author>mail@mopsicus.ru (Igor Lopatin)</author>
  <category>unity</category>
  <category>csharp</category>
  <category>unity</category>
  <category>games</category>
  <category>list</category>
  <category>links</category>
  <category>indie</category>
  <category>github</category>
  <category>links</category>
  <category>android</category>
</item>
<item>
  <title>Настройка VS Code для Unity</title>
  <description>&lt;p&gt;Скоро уже будет лет 10 как я увяз в Unity и C#… Начинал наверно как и многие с MonoDevelop &lt;span class="emoji"&gt;🥴&lt;/span&gt; Потом появился Sublime Text, которым я пользовался пару лет, классный и быстрый редактор, у меня даже была такая же заметка про его настройку. Но он был поменян на VS Code, который тогда начал активно развиваться и поддерживаться. Собственно, до сих пор им и продолжаю пользоваться.&lt;/p&gt;&lt;p&gt;Далее, почему я считаю VS Code хорошим выбором для разработки на Unity и C#, также список плагинов и настроек которые использую и рекомендую.&lt;/p&gt;&lt;h2&gt;Почему VS Code, плюсы&lt;/h2&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;хорошо дружит с Git&lt;/li&gt; &lt;/ul&gt;&lt;p&gt;Он действительно быстрый. Если сразу начали с него и не с чем сравнить особо, то можно попробовать отрыть средний проект в Studio или Rider и убедиться, что это действительно так. Ну и огромное количество расширений на каждый чих. И полезных и не очень.&lt;/p&gt;&lt;h3&gt;Минусы&lt;/h3&gt;&lt;ul&gt; &lt;li&gt;более слабая интеграция с Unity&lt;/li&gt; &lt;li&gt;слабый рефакторинг по сравнению с Rider&lt;/li&gt; &lt;li&gt;слабее отладка&lt;/li&gt; &lt;li&gt;нет встроенной навигации по Unity API&lt;/li&gt; &lt;/ul&gt;&lt;p&gt;Тут минусы только в контексте работы с Unity. Некоторые из них выходят как ни странно из «плюсов», например, слабая интеграция с Unity и хуже рефакторинг — это «плата» за быстроту, бесплатность и возможность в одном редакторе работать и с C#, и c TypeScript, Node.js и с прочими языками и фреймворками. Редактор не заточен под один язык или платформу, как Xcode например.&lt;/p&gt;&lt;h2&gt;Расширения&lt;/h2&gt;&lt;ul&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csdevkit"&gt;C# Dev Kit&lt;/a&gt; — ну это база, как говорится, официальное расширение от Microsoft, лучше OmniSharp: нормальный intellisense, поддержка C# 8–12, встроенный Roslyn анализатор, быстрая индексация&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://marketplace.visualstudio.com/items?itemName=Tobiah.unity-tools"&gt;Unity Tools&lt;/a&gt; — подсветка методов монобехов, ссылки на Unity доки, лучше навигация по Unity API&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://marketplace.visualstudio.com/items?itemName=usernamehw.errorlens"&gt;Error Lens&lt;/a&gt; — отображение ошибок и варнингов прямо в строках (спёрто у Rider &lt;span class="emoji"&gt;😬&lt;/span&gt;)&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://marketplace.visualstudio.com/items?itemName=eamodio.gitlens"&gt;GitLens&lt;/a&gt; — навигация по истории, blame, ревью и прочие git радости&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig"&gt;EditorConfig for VS Code&lt;/a&gt; — настройка форматирования через стандартизированный формат&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://marketplace.visualstudio.com/items?itemName=Gruntfuggly.todo-tree"&gt;Todo Tree&lt;/a&gt; — быстрый поиск и отображение TODO-шек&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://marketplace.visualstudio.com/items?itemName=VisualStudioToolsForUnity.vstuc"&gt;Unity&lt;/a&gt; — официальное расширение Unity для VS Code: API, debugger, подсказки, навигация&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://marketplace.visualstudio.com/items?itemName=monokai.theme-monokai-pro-vscode"&gt;Monokai Pro&lt;/a&gt; — тема оформления&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://marketplace.visualstudio.com/items?itemName=eppz.eppz-code"&gt;eppz!&lt;/a&gt; — ещё тема оформления&lt;/li&gt; &lt;/ul&gt;&lt;p&gt;Ниже мой файл конфигурации &lt;code&gt;.editorconfig&lt;/code&gt; который вместе с расширением &lt;em&gt;EditorConfig for VS Code&lt;/em&gt; показывает ошибки, варнинги в именовании полей, переменных и методов, а также форматирует код в соответствии с указанными &lt;a href="https://mopsicus.ruhttps://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/code-style-rule-options"&gt;правилами&lt;/a&gt;.&lt;/p&gt;&lt;div class="alert-info"&gt; &lt;p&gt;Конечно мой конфиг не эталон, он формировался со временем и все эти правила подбирались под нашу команду, так чтобы всех устраивало. В нём есть почти все директивы с которыми можно поиграться и настроить под себя.&lt;/p&gt; &lt;/div&gt;&lt;pre&gt;&lt;code&gt;# Dotnet code style settings:
[*.cs]
indent_style = space
indent_size = 4
insert_final_newline = true
charset = utf-8

# Sort using and Import directives with System.* appearing first
csharp_using_directive_placement = outside_namespace:silent
dotnet_sort_system_directives_first = true
dotnet_separate_import_directive_groups = false

# Avoid &amp;quot;this.&amp;quot; and &amp;quot;Me.&amp;quot; if not necessary
dotnet_style_qualification_for_field = false:refactoring
dotnet_style_qualification_for_property = false:refactoring
dotnet_style_qualification_for_method = false:refactoring
dotnet_style_qualification_for_event = false:refactoring

# Use language keywords instead of framework type names for type references
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
dotnet_style_predefined_type_for_member_access = true:suggestion

# Suggest more modern language features when available
dotnet_style_object_initializer = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_explicit_tuple_names = true:suggestion

# Whitespace options
dotnet_style_allow_multiple_blank_lines_experimental = false

# IDE0055: Fix formatting
dotnet_diagnostic.IDE0055.severity = warning

# Newline settings
csharp_new_line_before_open_brace = false
csharp_new_line_before_else = false
csharp_new_line_before_catch = false
csharp_new_line_before_finally = false
csharp_new_line_before_members_in_object_initializers = false
csharp_new_line_before_members_in_anonymous_types = false
csharp_new_line_between_query_expression_clauses = false
csharp_new_line_between_members = true

# Indentation preferences
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents = true
csharp_indent_case_contents_when_block = true
csharp_indent_switch_labels = true
csharp_indent_labels = flush_left

# Whitespace options
csharp_style_allow_embedded_statements_on_same_line_experimental = false
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = false
csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = false
csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = false

# # Prefer &amp;quot;var&amp;quot; everywhere
csharp_style_var_for_built_in_types = true:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
csharp_style_var_elsewhere = true:suggestion

# Prefer method-like constructs to have a block body
csharp_style_expression_bodied_methods = false:none
csharp_style_expression_bodied_constructors = false:none
csharp_style_expression_bodied_operators = false:none

csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion

# Prefer property-like constructs to have an expression-body
csharp_style_expression_bodied_properties = true:none
csharp_style_expression_bodied_indexers = true:none
csharp_style_expression_bodied_accessors = true:none

# Suggest more modern language features when available
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion
csharp_style_throw_expression = true:suggestion
csharp_style_conditional_delegate_call = true:suggestion
csharp_style_prefer_extended_property_pattern = true:suggestion

# Space preferences
csharp_space_after_cast = false
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_after_comma = true
csharp_space_after_dot = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_after_semicolon_in_for_statement = true
csharp_space_around_binary_operators = before_and_after
csharp_space_around_declaration_statements = do_not_ignore
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_before_comma = false
csharp_space_before_dot = false
csharp_space_before_open_square_brackets = false
csharp_space_before_semicolon_in_for_statement = false
csharp_space_between_empty_square_brackets = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_declaration_name_and_open_parenthesis = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false

# Blank lines
csharp_blank_lines_around_region = 0
csharp_blank_lines_inside_region = 0
csharp_keep_blank_lines_in_declarations = 1
csharp_remove_blank_lines_near_braces_in_declarations = true
csharp_blank_lines_between_using_groups = 1
csharp_blank_lines_after_using_list = 1
csharp_blank_lines_around_namespace = 1
csharp_blank_lines_inside_namespace = 0
csharp_blank_lines_after_file_scoped_namespace_directive = 1
csharp_blank_lines_around_type = 1
csharp_blank_lines_around_single_line_type = 1
csharp_blank_lines_inside_type = 0
csharp_blank_lines_around_field = 1
csharp_blank_lines_around_single_line_field = 1
csharp_blank_lines_around_property = 1
csharp_blank_lines_around_single_line_property = 1
csharp_blank_lines_around_auto_property = 1
csharp_blank_lines_around_single_line_auto_property = 1
csharp_blank_lines_around_accessor = 1
csharp_blank_lines_around_single_line_accessor = 1
csharp_blank_lines_around_invocable = 1
csharp_blank_lines_around_single_line_invocable = 1
csharp_keep_blank_lines_in_code = 0
csharp_remove_blank_lines_near_braces_in_code = false
csharp_blank_lines_around_local_method = 1
csharp_blank_lines_around_single_line_local_method = 0
csharp_blank_lines_before_control_transfer_statements = 0
csharp_blank_lines_after_control_transfer_statements = 0
csharp_blank_lines_before_block_statements = 0
csharp_blank_lines_after_block_statements = 0
csharp_blank_lines_around_block_case_section = 0
csharp_blank_lines_around_multiline_case_section = 0
csharp_blank_lines_before_case = 0
csharp_blank_lines_after_case = 0

# Access modifiers
dotnet_style_require_accessibility_modifiers = omit_if_default:warning

# Blocks are allowed
csharp_prefer_braces = true
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = true

dotnet_diagnostic.IDE0001.severity = silent
dotnet_diagnostic.IDE0002.severity = warning

# Using directive is unnecessary.
dotnet_diagnostic.IDE0005.severity = warning

# invalid switch.
dotnet_diagnostic.IDE0010.severity = silent

# add braces.
dotnet_diagnostic.IDE0011.severity = warning

# IDE0040: Add accessibility modifiers
dotnet_diagnostic.IDE0040.severity = suggestion

# IDE0060: Remove unused parameter
dotnet_diagnostic.IDE0060.severity = warning

# IDE0052: Remove unread private member
dotnet_diagnostic.IDE0052.severity = warning

# IDE0059: Unnecessary assignment to a value
dotnet_diagnostic.IDE0059.severity = warning

# CA1012: Abstract types should not have public constructors
dotnet_diagnostic.CA1012.severity = warning

# CA1822: Make member static
dotnet_diagnostic.CA1822.severity = warning

dotnet_diagnostic.IDE0051.severity = warning

dotnet_diagnostic.IDE0160.severity = warning
dotnet_diagnostic.IDE0161.severity = warning

# Prefer &amp;quot;var&amp;quot; everywhere
dotnet_diagnostic.IDE0007.severity = warning
csharp_style_var_for_built_in_types = true:warning
csharp_style_var_when_type_is_apparent = true:warning
csharp_style_var_elsewhere = true:warning

# csharp_style_allow_embedded_statements_on_same_line_experimental
dotnet_diagnostic.IDE2001.severity = silent

# csharp_style_allow_blank_lines_between_consecutive_braces_experimental
dotnet_diagnostic.IDE2002.severity = warning

# csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental
dotnet_diagnostic.IDE2004.severity = warning

# csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental
dotnet_diagnostic.IDE2005.severity = warning

# csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental
dotnet_diagnostic.IDE2006.severity = warning

# CS4014 Because this call is not awaited, execution of the current method continues before the call is completed
dotnet_diagnostic.CS4014.severity = silent

# csharp_style_implicit_object_creation_when_type_is_apparent
dotnet_diagnostic.IDE0090.severity = silent

# IDE0035: Remove unreachable code
dotnet_diagnostic.IDE0035.severity = warning

# IDE0130: Namespace does not match folder structure
dotnet_diagnostic.IDE0130.severity = silent

# IDE0071: Simplify interpolation
dotnet_diagnostic.IDE0071.severity = suggestion
dotnet_style_prefer_simplified_interpolation = true

# IDE0041: Use is null check
# Unity objects don't support null checking so best to use ReferenceEquals(value, null)
dotnet_diagnostic.IDE0041.severity = error
dotnet_style_prefer_is_null_check_over_reference_equality_method = false

# .NET Unnecessary code rules
[*.{cs,csx,cake,vb,vbx}]
dotnet_code_quality_unused_parameters = all:warning
dotnet_remove_unnecessary_suppression_exclusions = none:warning

# Constants are UPPERCASE
dotnet_naming_rule.constants_should_be_upper_case.severity = error
dotnet_naming_rule.constants_should_be_upper_case.symbols = constants
dotnet_naming_rule.constants_should_be_upper_case.style = constant_style
dotnet_naming_symbols.constants.applicable_kinds = field, local
dotnet_naming_symbols.constants.required_modifiers = const
dotnet_naming_style.constant_style.capitalization = all_upper

# Private should starts underscore
dotnet_naming_rule.private_members_with_underscore.symbols = private_fields
dotnet_naming_rule.private_members_with_underscore.style = prefix_underscore
dotnet_naming_rule.private_members_with_underscore.severity = error
dotnet_naming_symbols.private_fields.applicable_kinds = field
dotnet_naming_symbols.private_fields.applicable_accessibilities = private
dotnet_naming_style.prefix_underscore.capitalization = camel_case
dotnet_naming_style.prefix_underscore.required_prefix = _

# Public fields should be PascalCase
dotnet_naming_rule.public_fields_should_be_pascalcase.symbols = public_fields
dotnet_naming_rule.public_fields_should_be_pascalcase.style = pascalcase_style
dotnet_naming_rule.public_fields_should_be_pascalcase.severity = error
dotnet_naming_symbols.public_fields.applicable_kinds = field
dotnet_naming_symbols.public_fields.applicable_accessibilities = public
dotnet_naming_symbols.public_fields.required_modifiers = 
dotnet_naming_style.pascalcase_style.capitalization = pascal_case

# Interfaces should begin with I
dotnet_naming_rule.interface_should_begin_with_i.symbols = interface
dotnet_naming_rule.interface_should_begin_with_i.style = begins_with_i
dotnet_naming_rule.interface_should_begin_with_i.severity = error
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal
dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.capitalization = pascal_case

# Locals and parameters are camelCase
dotnet_naming_rule.locals_should_be_camel_case.symbols = locals_and_parameters
dotnet_naming_rule.locals_should_be_camel_case.style = camel_case_style
dotnet_naming_rule.locals_should_be_camel_case.severity = warning
dotnet_naming_symbols.locals_and_parameters.applicable_kinds = parameter, local
dotnet_naming_symbols.locals_and_parameters.applicable_accessibilities = *
dotnet_naming_style.camel_case_style.capitalization = camel_case

# Types should be PascalCase
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.types_should_be_pascal_case.severity = error
dotnet_naming_symbols.types.applicable_kinds = namespace, class, struct, interface, enum, property, method, event, delegate, local_function
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal
dotnet_naming_style.pascal_case.capitalization = pascal_case
&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Шрифт FiraCode&lt;/h2&gt;&lt;p&gt;Использую данный &lt;a href="https://mopsicus.ruhttps://github.com/tonsky/FiraCode"&gt;шрифт&lt;/a&gt; уже давно. Это хороший бесплатный моноширинный шрифт с лигатурами (которые почему-то не всем не нравятся &lt;span class="emoji"&gt;🤷‍♂️&lt;/span&gt;). Для отображения лигатур надо включить их в настройках VS Code:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;{
  &amp;quot;editor.fontFamily&amp;quot;: &amp;quot;Fira Code&amp;quot;, // или FiraCode-Retina
  &amp;quot;editor.fontLigatures&amp;quot;: true
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Inlay Hints&lt;/h2&gt;&lt;p&gt;Inlay Hints — это подсказки прямо в коде, которые редактор показывает внутри строки, но которые не являются частью кода. Некоторые не включают их из-за «визуального шума», но я к ним давно привык и всегда использую.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;{
  &amp;quot;editor.inlayHints.fontFamily&amp;quot;: &amp;quot;FiraCode&amp;quot;,
  &amp;quot;csharp.inlayHints.enableInlayHintsForImplicitObjectCreation&amp;quot;: true,
  &amp;quot;csharp.inlayHints.enableInlayHintsForImplicitVariableTypes&amp;quot;: true,
  &amp;quot;csharp.inlayHints.enableInlayHintsForLambdaParameterTypes&amp;quot;: true,
  &amp;quot;csharp.inlayHints.enableInlayHintsForTypes&amp;quot;: true,
  &amp;quot;dotnet.inlayHints.enableInlayHintsForIndexerParameters&amp;quot;: true,
  &amp;quot;dotnet.inlayHints.enableInlayHintsForLiteralParameters&amp;quot;: true,
  &amp;quot;dotnet.inlayHints.enableInlayHintsForObjectCreationParameters&amp;quot;: true,
  &amp;quot;dotnet.inlayHints.enableInlayHintsForOtherParameters&amp;quot;: true,
  &amp;quot;dotnet.inlayHints.enableInlayHintsForParameters&amp;quot;: true,
  &amp;quot;dotnet.inlayHints.suppressInlayHintsForParametersThatDifferOnlyBySuffix&amp;quot;: true,
  &amp;quot;dotnet.inlayHints.suppressInlayHintsForParametersThatMatchArgumentName&amp;quot;: true,
  &amp;quot;dotnet.inlayHints.suppressInlayHintsForParametersThatMatchMethodIntent&amp;quot;: true
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Плагин для Unity&lt;/h2&gt;&lt;p&gt;Для завершения интеграции с Unity редактором необходимо установить &lt;a href="https://mopsicus.ruhttps://docs.unity3d.com/Packages/com.unity.ide.visualstudio@2.0/manual/index.html"&gt;пакет Visual Studio Editor&lt;/a&gt;. Обычно он сразу включён после установки редактора, вместе с плагином для Rider.&lt;/p&gt;&lt;p&gt;Вот такая сборка. Не забываем обновлять расширения. Ждём CoreCLR &lt;span class="emoji"&gt;😅&lt;/span&gt;&lt;/p&gt;</description>
  <guid>https://mopsicus.ru/notes/unity-develop-vscode-setup.html</guid>
  <link>https://mopsicus.ru/notes/unity-develop-vscode-setup.html</link>
  <pubDate>Mon, 22 Dec 2025 20:05:00 +0300</pubDate>
  <comments>https://mopsicus.ru/notes/unity-develop-vscode-setup.html#comments</comments>
  <author>mail@mopsicus.ru (Igor Lopatin)</author>
  <category>unity</category>
  <category>unity</category>
  <category>ide</category>
  <category>vscode</category>
  <category>csharp</category>
  <category>plugin</category>
  <category>list</category>
</item>
<item>
  <title>Генерация 3D моделей</title>
  <description>&lt;p&gt;Собирал для студентов список сервисов для генерации 3D моделей по описанию и из 2D в 3D, решил что можно и поделиться, наверняка будет полезно. Мои фавориты пока это Meshy и Hitem3D.&lt;/p&gt;&lt;p&gt;Периодически буду возвращаться к этому списку, потому что такие вещи сейчас часто появляются, буду чекать ссылки, удалять мёртвые, добавлять новые. Последнее обновление: &lt;code&gt;05.11.2025&lt;/code&gt;.&lt;/p&gt;&lt;ul&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://www.meshy.ai"&gt;https://www.meshy.ai&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://hitem3d.ai"&gt;https://hitem3d.ai&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://trellis3d.net"&gt;https://trellis3d.net&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://www.tripo3d.ai"&gt;https://www.tripo3d.ai&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://www.3daistudio.com"&gt;https://www.3daistudio.com&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://fast3d.io"&gt;https://fast3d.io&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://3dpresso.ai"&gt;https://3dpresso.ai&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://www.alpha3d.io"&gt;https://www.alpha3d.io&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://3daily.ai"&gt;https://3daily.ai&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://hyper3d.ai"&gt;https://hyper3d.ai&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://spline.design"&gt;https://spline.design&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://www.csm.ai"&gt;https://www.csm.ai&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://www.sloyd.ai"&gt;https://www.sloyd.ai&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://poly.cam/tools/3d-model-generator"&gt;https://poly.cam/tools/3d-model-generator&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://gen.hexa3d.io/free-online-ai-3d-image-to-model-generator"&gt;https://gen.hexa3d.io&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://dream-machine.lumalabs.ai"&gt;https://dream-machine.lumalabs.ai&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://3d.hunyuan.tencent.com"&gt;https://3d.hunyuan.tencent.com&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://github.com/Tencent-Hunyuan/Hunyuan3D-2"&gt;https://github.com/Tencent-Hunyuan/Hunyuan3D-2&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt;</description>
  <guid>https://mopsicus.ru/notes/3d-generation-ai-list.html</guid>
  <link>https://mopsicus.ru/notes/3d-generation-ai-list.html</link>
  <pubDate>Wed, 05 Nov 2025 10:15:00 +0300</pubDate>
  <comments>https://mopsicus.ru/notes/3d-generation-ai-list.html#comments</comments>
  <author>mail@mopsicus.ru (Igor Lopatin)</author>
  <category>links</category>
  <category>list</category>
  <category>links</category>
  <category>games</category>
  <category>3d</category>
  <category>models</category>
  <category>ai</category>
</item>
<item>
  <title>Менеджер событий для Unity</title>
  <description>&lt;p&gt;Эта реализация менеджера событий, и теперь эта заметка, появились на практике нескольких проектов. Это достаточно простая, лекговесная библиотека чтобы уменьшить связность между системами, объектами и сценами в своём проекте.&lt;/p&gt;&lt;div class="tagline"&gt; &lt;p&gt;Менеджер событий или шина данных — паттерн проектирования pub-sub (публикация-подписка), который служит посредником между компонентами системы, позволяя им взаимодействовать, не зная друг друга напрямую.&lt;/p&gt; &lt;/div&gt;&lt;p&gt;Таких библиотек и скриптов очень много в открытом доступе. И много их потому что каждый разработчик решает в первую очередь свою проблему: скорость, удобство, универсальность, простота. Вот некоторые популярные:&lt;/p&gt;&lt;ul&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://github.com/Cysharp/MessagePipe"&gt;MessagePipe&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://github.com/NimaAra/Easy.MessageHub"&gt;Easy.MessageHub&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://github.com/dotnet/reactive"&gt;Rx.Subject&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://github.com/PrismLibrary/Prism/tree/master/src/Prism.Events"&gt;Prism.Events&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://github.com/Mathijs-Bakker/Extenject/blob/master/Documentation/Signals.md"&gt;Extenject.Signals&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt;&lt;p&gt;Это мощные универсальные библиотеки, которые закрывают все задачи по реализации паттерна &lt;em&gt;публикация-подписка&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;Данная же библиотека очень простая, автономная и выполняющая только свою задачу, без зависимости от какой-то «основной» большой части.&lt;/p&gt;&lt;p&gt;Сразу простой пример:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;using Shardy.Signals;

/// &amp;lt;summary&amp;gt;
/// Demo interface
/// &amp;lt;/summary&amp;gt;
public interface IDemo : ISubscriber {
    void OnSignalAction(int data);
}

/// &amp;lt;summary&amp;gt;
/// Demo class with sender/receiver
/// &amp;lt;/summary&amp;gt;
public class Demo : MonoBehaviour, IDemo {

    /// &amp;lt;summary&amp;gt;
    /// Log tag
    /// &amp;lt;/summary&amp;gt;
    public const string TAG = &amp;quot;DEMO&amp;quot;;

    /// &amp;lt;summary&amp;gt;
    /// Destroy method name
    /// &amp;lt;/summary&amp;gt;
    const string SEND_METHOD = &amp;quot;Send&amp;quot;;

    /// &amp;lt;summary&amp;gt;
    /// Init and subscribe to signals
    /// &amp;lt;/summary&amp;gt;
    void Awake() {
        Signals.Subscribe(this);
        InvokeRepeating(SEND_METHOD, 1f, 1f);
    }

    /// &amp;lt;summary&amp;gt;
    /// Unsubscribe from signals
    /// &amp;lt;/summary&amp;gt;
    void OnDestroy() {
        Signals.Unsubscribe(this);
    }

    /// &amp;lt;summary&amp;gt;
    /// Sends demo signals
    /// Send int data -&amp;gt; current subscriber count
    /// &amp;lt;/summary&amp;gt;
    void Send() {
        Signals.Send&amp;lt;IDemo&amp;gt;(a =&amp;gt; a.OnSignalAction(Signals.GetSubscriberCount&amp;lt;IDemo&amp;gt;()));
    }

    /// &amp;lt;summary&amp;gt;
    /// Called when a signal is received
    /// &amp;lt;/summary&amp;gt;
    public void OnSignalAction(int data) {
        Debug.Log(///
{{&gt; rss-list key='layout' value='post' time='rss' limit=10}}
{{&gt; rss-list key='layout' value='note' time='rss' limit=10}}
///quot;[{TAG}] action: {data}&amp;quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;В общем-то, в примере выше есть почти все методы которые понадобятся для работы с библиотекой: интерфейс, подписка, отписка, пример отправки и получения. В данном случае отправитель и получатель — это один и тот же класс. В &lt;a href="https://mopsicus.ruhttps://github.com/mopsicus/shardy-signals/blob/main/Samples~/Demo/Scripts/Demo.cs"&gt;демо&lt;/a&gt; есть пример с несколькими подписчиками и разрушаемыми объектами.&lt;/p&gt;&lt;h2&gt;Когда актуально использовать менеджер событий&lt;/h2&gt;&lt;ul&gt; &lt;li&gt;очевидно, когда нужно развязать системы&lt;/li&gt; &lt;li&gt;одно событие важно для нескольких подсистем&lt;/li&gt; &lt;li&gt;независимость UI и геймплея&lt;/li&gt; &lt;li&gt;много временных динамических объектов&lt;/li&gt; &lt;li&gt;надо сделать быстрый прототип&lt;/li&gt; &lt;/ul&gt;&lt;p&gt;Это хорошо работает с пунктом №2, т.е. когда одно действие должно вызвать реакцию в разных подсистемах. Как пример, событие &lt;code&gt;PlayerKilled&lt;/code&gt;. Что может происходить в этом случае:&lt;/p&gt;&lt;ul&gt; &lt;li&gt;надо начислить очки&lt;/li&gt; &lt;li&gt;обновить UI&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;pre&gt;&lt;code&gt;/// &amp;lt;summary&amp;gt;
/// Player interface
/// &amp;lt;/summary&amp;gt;
public interface IPlayer : ISubscriber {
    void OnPlayerKilled(int level, int score);
    ...
}

/// &amp;lt;summary&amp;gt;
/// Player class on character object
/// &amp;lt;/summary&amp;gt;
public class Player {

    /// &amp;lt;summary&amp;gt;
    /// Current player level
    /// &amp;lt;/summary&amp;gt;
    int _level = 3;

    /// &amp;lt;summary&amp;gt;
    /// Current player score
    /// &amp;lt;/summary&amp;gt;
    int _score = 120;

    /// &amp;lt;summary&amp;gt;
    /// Called when take damage
    /// &amp;lt;/summary&amp;gt;
    public void OnDamage(int health) {
      if (health &amp;lt;= 0) {
        Signals.Send&amp;lt;IPlayer&amp;gt;(action =&amp;gt; action.OnPlayerKilled(_level, _score));
      }
    }
}

/// &amp;lt;summary&amp;gt;
/// UI
/// &amp;lt;/summary&amp;gt;
public class UI: IPlayer {

    /// &amp;lt;summary&amp;gt;
    /// Subscribe to signals
    /// &amp;lt;/summary&amp;gt;
    void Awake() {
        Signals.Subscribe(this);
    }

    /// &amp;lt;summary&amp;gt;
    /// Called when a signal is received
    /// &amp;lt;/summary&amp;gt;
    public void OnPlayerKilled(int level, int score) {
        TMP.SetText(&amp;quot;Player killed on {0} level with {1} score&amp;quot;, level, score);
    }
}

/// &amp;lt;summary&amp;gt;
/// Sounds
/// &amp;lt;/summary&amp;gt;
public class SoundManager: IPlayer {

    /// &amp;lt;summary&amp;gt;
    /// Subscribe to signals
    /// &amp;lt;/summary&amp;gt;
    void Awake() {
        Signals.Subscribe(this);
    }

    /// &amp;lt;summary&amp;gt;
    /// Called when a signal is received
    /// &amp;lt;/summary&amp;gt;
    public void OnPlayerKilled(int level, int score) {
        Audio.Play(///
{{&gt; rss-list key='layout' value='post' time='rss' limit=10}}
{{&gt; rss-list key='layout' value='note' time='rss' limit=10}}
///quot;killed_sound_{level}&amp;quot;);
    }
}

/// &amp;lt;summary&amp;gt;
/// Stats
/// &amp;lt;/summary&amp;gt;
public class Stats: IPlayer {

    /// &amp;lt;summary&amp;gt;
    /// Subscribe to signals
    /// &amp;lt;/summary&amp;gt;
    void Awake() {
        Signals.Subscribe(this);
    }

    /// &amp;lt;summary&amp;gt;
    /// Called when a signal is received
    /// &amp;lt;/summary&amp;gt;
    public void OnPlayerKilled(int level, int score) {
        DB.SendStat(&amp;quot;killed&amp;quot;, level, score);
    }
}
&lt;/code&gt;&lt;/pre&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;/ul&gt;&lt;h2&gt;Особенности реализации&lt;/h2&gt;&lt;p&gt;Данная реализация поддерживает &lt;code&gt;WeakReference&lt;/code&gt;, это означает что если получатель при отправке события уже удалён, то ошибки не будет.&lt;/p&gt;&lt;p&gt;После каждой отправки проверяется есть ли «мертвые» получатели. И если такие имеются, то они автоматически удаляются из списка на отправку.&lt;/p&gt;&lt;div class="alert-info"&gt; &lt;p&gt;&lt;code&gt;WeakReference&lt;/code&gt; — это специальный контейнер, который хранит ссылку на объект, но не мешает сборщику мусора его уничтожить. Часто применяется в кеширования, в менеджерах событий &lt;span class="emoji"&gt;😅&lt;/span&gt; ну и чтобы избежать утечек памяти.&lt;/p&gt; &lt;/div&gt;&lt;p&gt;Если бы этот пакет был &lt;strong&gt;не&lt;/strong&gt; для использования в Unity, то этой проверки можно было бы избежать. А так есть нюанс, как в старом анекдоте…&lt;/p&gt;&lt;p&gt;В Unity уничтожение объекта не значит что &lt;abbr title="GC (garbage collector) — сборщик мусора, механизм управления памятью который автоматически отслеживает неиспользуемые объекты и освобождает память"&gt;GC&lt;/abbr&gt; его сразу убрал. &lt;code&gt;UnityEngine.Object&lt;/code&gt; имеют кастомное поведение:&lt;/p&gt;&lt;ul&gt; &lt;li&gt;&lt;code&gt;obj == null&lt;/code&gt; возвращает true, хотя в памяти объект ещё есть&lt;/li&gt; &lt;li&gt;&lt;code&gt;ReferenceEquals(obj, null)&lt;/code&gt; вернёт false&lt;/li&gt; &lt;/ul&gt;&lt;p&gt;Поэтому при работе с &lt;code&gt;WeakReference&lt;/code&gt; в Unity проверка должна быть двойная:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;/// &amp;lt;summary&amp;gt;
/// Check if object is alive, check custom null behavior
/// &amp;lt;/summary&amp;gt;
bool IsAlive(object target) {
    if (target == null) {
        return false;
    }
    if (target is UnityEngine.Object obj &amp;amp;&amp;amp; obj == null) {
        return false;
    }
    return true;
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Когда не стоит использовать&lt;/h2&gt;&lt;ul&gt; &lt;li&gt;когда связь «один к одному»&lt;/li&gt; &lt;li&gt;очень частые события &lt;code&gt;Update&lt;/code&gt;, &lt;code&gt;FixedUpdate&lt;/code&gt; и другие перегрузят систему, тут лучше прямые вызовы&lt;/li&gt; &lt;li&gt;есть критическая зависимость: лучше передать ссылку напрямую, а не надеятся, что «кто-то подпишется»&lt;/li&gt; &lt;/ul&gt;&lt;p&gt;Иногда, попробовав такой метод коммуникации между объектами и системами, возникает соблазн слать события на всё подряд. Но это фатальная ошибка &lt;span class="emoji"&gt;😬&lt;/span&gt; Если игра (проект) более-менее крупная, а не прототип, то нужен строгий контроль или использование других паттернов, например ECS.&lt;/p&gt;&lt;div class="alert-info"&gt; &lt;p&gt;В уменьшении связности и использовании менеджера событий есть и свои минусы: сложнее тестировать, сложнее вычислить порядок действий как получилось текущее состояние, в общем, использовать его рекомендуется не повсеместно.&lt;/p&gt; &lt;/div&gt;&lt;p&gt;Документации нет, всё поместилось в README файле. Добавил этот модуль как часть &lt;a href="https://mopsicus.ru../projects/shardy.html"&gt;Shardy&lt;/a&gt;, но использовать его конечно можно и отдельно.&lt;/p&gt;&lt;div class="alert"&gt; &lt;p&gt;&lt;span id="shardy-signals-stars"&gt;&lt;/span&gt; &lt;i class="fa-regular fa-star"&gt;&lt;/i&gt; Все &lt;a href="https://mopsicus.ruhttps://github.com/mopsicus/shardy-signals"&gt;исходники&lt;/a&gt; доступны на Github&lt;/p&gt; &lt;/div&gt;</description>
  <guid>https://mopsicus.ru/notes/unity-eventbus.html</guid>
  <link>https://mopsicus.ru/notes/unity-eventbus.html</link>
  <pubDate>Fri, 05 Sep 2025 13:34:00 +0300</pubDate>
  <comments>https://mopsicus.ru/notes/unity-eventbus.html#comments</comments>
  <author>mail@mopsicus.ru (Igor Lopatin)</author>
  <category>unity</category>
  <category>unity</category>
  <category>csharp</category>
  <category>github</category>
  <category>package</category>
</item>
<item>
  <title>FSM для C#</title>
  <description>&lt;p&gt;В общем-то, на просторах Гитхаба можно найти 100 и 1 вариант FSM, но у моей реализация есть свои «особенности» &lt;span class="emoji"&gt;😅&lt;/span&gt; Сначала, краткое описание для тех кто не в курсе:&lt;/p&gt;&lt;div class="tagline"&gt; &lt;p&gt;Машина состояний (конечный автомат) — это математическая модель, которая представляет систему, способную находиться только в одном из конечного числа состояний в каждый момент времени. Она переходит из одного состояния в другое в ответ на входные данные или события, при этом каждое состояние описывает определенное поведение или функцию системы.&lt;/p&gt; &lt;/div&gt;&lt;p&gt;И сразу тестовый пример из &lt;a href="https://mopsicus.ruhttps://github.com/mopsicus/shardy-fsm/blob/main/Samples~/Demo/Scripts/Demo.cs"&gt;демки&lt;/a&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;using Shardy.FSM;

/// &amp;lt;summary&amp;gt;
/// Test FSM
/// &amp;lt;/summary&amp;gt;
void Test() {
    _fsm = FSM&amp;lt;State, Action&amp;gt;.Builder(State.Standing)
    .State(State.Standing)
    .OnEnter((data) =&amp;gt; {
        Debug.Log(&amp;quot;on enter standing&amp;quot;);
    })
    .To(State.Sitting).On(Action.Down)
    .To(State.Jumping).On(Action.Space).If(s =&amp;gt; _isConditionActive)
    .State(State.Sitting)
    .To(State.Lying).On(Action.Down)
    .To(State.Standing).On(Action.Up)
    .State(State.Lying)
    .To(State.Sitting).On(Action.Up)
    .State(State.Jumping)
    .Note(&amp;quot;some help message here&amp;quot;)
    .OnExit((data) =&amp;gt; {
        Debug.Log(&amp;quot;on exit jumping&amp;quot;);
    })
    .To(State.Standing).On(Action.Down)
    .Build();

    _fsm.Trigger(Action.Down);
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Для перехода на стейт надо активировать триггер(ы):&lt;/p&gt;&lt;pre&gt;&lt;code&gt;fsm.Trigger(Action.Down);
fsm.Trigger(Action.Down);
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;В таком случае получится такой результат:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;initial is standing
on exit standing
on enter sitting
on exit sitting
on enter lying 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Ещё подсмотрел, как сгенерить описание для UML диаграммы и &lt;a href="https://mopsicus.ruhttps://www.planttext.com/"&gt;отрендерить её на сайте&lt;/a&gt; или &lt;a href="https://mopsicus.ruhttp://www.plantuml.com/plantuml/uml/"&gt;на этом&lt;/a&gt;, по-моему у них один движок:&lt;/p&gt;&lt;img class="emerge float-center" src="https://mopsicus.ru/images/assets/fsm-uml.11b273bb.jpg" alt="FSM UML диаграмма"&gt;&lt;div class="alert-info"&gt; &lt;p&gt;Чтобы сделать диаграмму более «интуитивной», вместо слова &lt;em&gt;state&lt;/em&gt; используется ключевое слово &lt;em&gt;agent&lt;/em&gt;, этом случае можно рисовать разные линии.&lt;/p&gt; &lt;/div&gt;&lt;pre&gt;&lt;code&gt;@startuml
skin rose
title TestFSM
left to right direction
agent Standing
agent Sitting
agent Lying
agent Jumping
note left of Jumping
some help message here
end note
Start --&amp;gt; Standing
Standing --&amp;gt; Sitting : Down
Standing ~~&amp;gt; Jumping : Space
Sitting --&amp;gt; Lying : Down
Sitting --&amp;gt; Standing : Up
Lying --&amp;gt; Sitting : Up
Jumping --&amp;gt; Standing : Down
@enduml
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Ещё к каждому переходу можно добавлять условия, (на картинке выше их нет) об этом подробнее в &lt;a href="https://mopsicus.ruhttps://github.com/mopsicus/shardy-fsm/blob/main/Documentation~/index.md"&gt;документации&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Если у перехода между состояниями есть условие(я), то линия будет рисоваться пунктирной, а если нет триггера – то с крестиком на конце. Актуально когда состояний и переходов много, можно сгенерить диаграмму и посмотреть нет ли косяков.&lt;/p&gt;&lt;div class="alert-info"&gt; &lt;p&gt;Вертикальная форма записи это необязательное условие &lt;span class="emoji"&gt;😅&lt;/span&gt;&lt;/p&gt; &lt;/div&gt;&lt;p&gt;И тут возникает вопрос: &lt;em&gt;зачем в разработке машина состояний&lt;/em&gt;?&lt;/p&gt;&lt;p&gt;Если совсем просто — это способ сказать объекту:&lt;/p&gt;&lt;blockquote&gt; &lt;p&gt;Сейчас ты в таком-то состоянии, и можешь делать только это. А потом — переключись в другое состояние и делай другое.&lt;/p&gt; &lt;/blockquote&gt;&lt;h2&gt;Примеры&lt;/h2&gt;&lt;h3&gt;Персонаж игрока&lt;/h3&gt;&lt;p&gt;Состояния: стоит, идет, прыгает, атакует.&lt;/p&gt;&lt;p&gt;Когда игрок нажимает «прыжок», машина состояний переключается с «идёт» на «прыгает», и только тогда проигрывается анимация прыжка и отключается движение по земле.&lt;/p&gt;&lt;h3&gt;Противник&lt;/h3&gt;&lt;p&gt;Состояния: патрулирует → заметил игрока → следует → атакует.&lt;/p&gt;&lt;p&gt;Без машины состояний враг мог бы «пытаться делать всё сразу», а это приводит к багам и странному поведению.&lt;/p&gt;&lt;h3&gt;Карточные игры&lt;/h3&gt;&lt;p&gt;Состояния: ожидает, ходит, бьётся, берёт.&lt;/p&gt;&lt;p&gt;На каждом состоянии отображается разные элементы управления, UI, запускается таймер и прочее.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;В общем, использование машины состояний практически всегда упрощает код, делает поведение более предсказуемым, в процесс разработки код проще отлаживать и проще расширять.&lt;/p&gt;&lt;div class="alert"&gt; &lt;p&gt;&lt;span id="shardy-fsm-stars"&gt;&lt;/span&gt; &lt;i class="fa-regular fa-star"&gt;&lt;/i&gt; Все &lt;a href="https://mopsicus.ruhttps://github.com/mopsicus/shardy-fsm"&gt;исходники&lt;/a&gt; доступны на Github&lt;/p&gt; &lt;/div&gt;</description>
  <guid>https://mopsicus.ru/notes/fsm-csharp.html</guid>
  <link>https://mopsicus.ru/notes/fsm-csharp.html</link>
  <pubDate>Thu, 05 Dec 2024 12:18:00 +0300</pubDate>
  <comments>https://mopsicus.ru/notes/fsm-csharp.html#comments</comments>
  <author>mail@mopsicus.ru (Igor Lopatin)</author>
  <category>csharp</category>
  <category>github</category>
  <category>unity</category>
  <category>csharp</category>
  <category>shardy</category>
  <category>fsm</category>
  <category>package</category>
</item>
<item>
  <title>Бесплатные звуки и музыка для игр</title>
  <description>&lt;p&gt;Небольшой список ссылок где можно взять различные звуки и музыку для своих игр. Бесплатно. Хотя есть некоторые моменты с лицензиями.&lt;/p&gt;&lt;p&gt;Периодически возвращаюсь к этому списку, прохожусь по ссылкам, удаляю мёртвые, добавляю новые. Последнее обновление: &lt;code&gt;06.11.2025&lt;/code&gt;.&lt;/p&gt;&lt;ul&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://freesound.org"&gt;https://freesound.org&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://sonniss.com"&gt;https://sonniss.com&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttp://indiegamemusic.com"&gt;http://indiegamemusic.com&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://incompetech.com/music/"&gt;https://incompetech.com/music/&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttp://99sounds.org"&gt;http://99sounds.org&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://www.purple-planet.com"&gt;https://www.purple-planet.com&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://www.joshwoodward.com"&gt;https://www.joshwoodward.com&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://opengameart.org/art-search-advanced?keys=&amp;amp;field_art_type_tid%5B%5D=13&amp;amp;sort_by=count&amp;amp;sort_order=DESC"&gt;https://opengameart.org&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttp://raisedbeaches.com/octave/"&gt;http://raisedbeaches.com/octave/&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://www.reddit.com/r/freemusic/"&gt;https://www.reddit.com/r/freemusic/&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://freemusicarchive.org"&gt;https://freemusicarchive.org&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://directory.audio"&gt;https://directory.audio&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://www.zapsplat.com/sound-effect-category/game-music-and-loops/"&gt;https://www.zapsplat.com&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://www.bensound.com/royalty-free-music/video-game"&gt;https://www.bensound.com/royalty-free-music/video-game&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://mixkit.co/free-sound-effects/game/"&gt;https://mixkit.co/free-sound-effects/game/&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://www.epidemicsound.com/music/themes/gaming/indie-gaming/"&gt;https://www.epidemicsound.com/music/themes/gaming/indie-gaming/&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://pixabay.com/music/search/genre/video%20games/"&gt;https://pixabay.com/music/&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://www.chosic.com/free-music/games/"&gt;https://www.chosic.com/free-music/games/&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://www.freepik.com/audio/music"&gt;https://www.freepik.com/audio/music&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://ncs.io"&gt;https://ncs.io&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://tunetank.com/discover/themes/gaming/"&gt;https://tunetank.com/discover/themes/gaming/&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://uppbeat.io/music/category/game"&gt;https://uppbeat.io/music/category/game&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://artlist.io/royalty-free-music"&gt;https://artlist.io/royalty-free-music&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://kenney.nl/assets/impact-sounds"&gt;https://kenney.nl/assets/impact-sounds&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://80.lv/articles/80-level-digest-free-music-for-game-developers-3d-artists/"&gt;https://80.lv&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://www.youtube.com/watch?v=Ef0VrcrOatg&amp;amp;list=PLsVfn9YIFmNC3SL_FgAe6k4_is4stMOh2&amp;amp;index=2"&gt;https://www.youtube.com/playlist&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://www.youtube.com/@FriendlyFreeSounds"&gt;https://www.youtube.com/@FriendlyFreeSounds&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://www.youtube.com/@sefe_soundeffectsforedits"&gt;https://www.youtube.com/@sefe_soundeffectsforedits&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://www.soundeffectsplus.com"&gt;https://www.soundeffectsplus.com&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://www.josh-bakaimis-sound.com/free-stuff"&gt;https://www.josh-bakaimis-sound.com/free-stuff&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://inaudio.org"&gt;https://inaudio.org&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://mobygratis.com"&gt;https://mobygratis.com&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt;&lt;p&gt;Если заметили ссылку которая перестала работать — можно писать &lt;a href="https://mopsicus.ruhttp://t.me/mopsicus"&gt;в телеграм&lt;/a&gt;, &lt;a href="https://mopsicus.rumailto:mail@mopsicus.ru"&gt;на почту&lt;/a&gt; или ниже в комменты.&lt;/p&gt;</description>
  <guid>https://mopsicus.ru/notes/free-sounds-for-games-list.html</guid>
  <link>https://mopsicus.ru/notes/free-sounds-for-games-list.html</link>
  <pubDate>Sat, 01 Apr 2023 15:06:00 +0300</pubDate>
  <comments>https://mopsicus.ru/notes/free-sounds-for-games-list.html#comments</comments>
  <author>mail@mopsicus.ru (Igor Lopatin)</author>
  <category>links</category>
  <category>music</category>
  <category>sounds</category>
  <category>list</category>
  <category>links</category>
  <category>games</category>
</item>
<item>
  <title>Оптимизация игр на Unity</title>
  <description>&lt;p&gt;Не смотрите что статья от 2021 года, она даже старее &lt;span class="emoji"&gt;😁&lt;/span&gt; На &lt;a href="https://mopsicus.ruhttps://old.mopsicus.ru"&gt;старой версии сайта&lt;/a&gt; она была одной из первых, начал я её в 2016 году. К слову, с тех пор многие советы до сих пор актуальны, поэтому я решил её оставить и в новой версии сайта.&lt;/p&gt;&lt;p&gt;Этот пост будет периодически дополняться и обновляться как и прежде, последнее обновление: &lt;code&gt;24.10.2025&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Обновляя эту заметку я подумал, что сейчас в 2025 году, во времена расцвета ИИ помощников, эти советы пригодятся начинающим разработчикам игр. Ведь зачастую, код сгенерированный ИИ далеко не самый производительный. Обученый на примерах, статьях и тех же исходниках с гитхаба — он выдаёт рабочие (и то не всегда) примеры которые в проде будут выделять кучу мусора, занимать память, просаживать FPS и т.п.&lt;/p&gt;&lt;p&gt;Ниже немного актуальных до сих пор рекомендаций и ссылки на статьи, они подойдут и для 2D и для 3D игр:&lt;/p&gt;&lt;ol&gt; &lt;li&gt; &lt;p&gt;Кешируйте всё. Всё что используется больше 1-2 раз — лучше закешировать. Операции типа &lt;code&gt;GameObject.Find()&lt;/code&gt;, &lt;code&gt;GetComponent()&lt;/code&gt;, &lt;code&gt;FindObjectOfType()&lt;/code&gt; достаточно ресурсозатратны, а если они вызываются где-нибудь в цикле или в &lt;code&gt;Update()&lt;/code&gt;, то производительность наверняка упадёт.&lt;/p&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;В Unity есть стандартные пресеты настройки качества графики. Но так как мы говорим про оптимизацию для 2D и для мобильных устройств, то всё что выше &lt;strong&gt;Simple&lt;/strong&gt; нет смысла ставить. Конечно, если у вас есть какие-то специфические моменты, частицы, и т.п., то с параметры можно поэкспериментировать найдя оптимальный баланс.&lt;/p&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;Регулируйте частоту кадров. Понижая фреймрейт на сценах или игровых меню, где ничего не двигается, можно значительно снизить CPU, а следовательно продлить жизнь батарее устройства.&lt;/p&gt; &lt;/li&gt; &lt;/ol&gt;&lt;pre&gt;&lt;code&gt;QualitySettings.vSyncCount = 0;
Application.targetFrameRate = XX;
&lt;/code&gt;&lt;/pre&gt;&lt;div class="alert-info"&gt; &lt;p&gt;Если не отключить вертикальную синхронизацию, то изменение фреймрейта игнорируется!&lt;/p&gt; &lt;/div&gt;&lt;ol start="4"&gt; &lt;li&gt; &lt;p&gt;Все используемые картинки нужно упаковывать в атласы. Можно использовать как встроенный инструмент, так и подготавливать атласы в какой-нибудь другой программе, например &lt;a href="https://mopsicus.ruhttps://www.codeandweb.com/texturepacker"&gt;TexturePacker&lt;/a&gt;. Таким образом уменьшается количество вызовов отрисовок ваших спрайтов. Проверить как идёт отрисовка можно с помощью встроенного инструмента &lt;strong&gt;Frame Debugger&lt;/strong&gt;.&lt;/p&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;Ещё про атласы. Если упаковываете в Unity, убедитесь что атлас влезает на одну страницу! Это видно в инспекторе. Иначе каждая страница по сути новая текстура.&lt;/p&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;Старайтесь по возможности делать меньше вызовов &lt;code&gt;xxx.ToList()&lt;/code&gt; и &lt;code&gt;xxx.ToArray()&lt;/code&gt; — они создают новые коллекции при каждом вызове. Лучше построить свой код так, чтобы был прямой доступ к массивам, спискам и использовать ранее созданные коллекции повторно.&lt;/p&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;Уделите внимание работе со строками, особенно если это частая операция. Используйте &lt;code&gt;StringBuilder&lt;/code&gt; и специальный форматный метод TMP — &lt;code&gt;tmp.SetText(&amp;quot;Data: {0}&amp;quot;, data)&lt;/code&gt;, он не создаёт промежуточные строки.&lt;/p&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;Используйте пул объектов. &lt;code&gt;GameObject.Instantiate()&lt;/code&gt; — очень дорогая операция! Если есть возможность не использовать её в процессе игры — не используйте. Для большого количества однотипных объектов надо использовать пул объектов (object pool). Один раз инициализировав определенное количество, например пуль, они будут использоваться снова и снова, вместо создания и уничтожения каждый раз. &lt;a href="https://mopsicus.ruhttps://learn.unity.com/tutorial/introduction-to-object-pooling"&gt;Урок по пулу объектов&lt;/a&gt; и куча готовых реализаций гуглится по запросу &lt;code&gt;unity object pool github&lt;/code&gt;.&lt;/p&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;a href="https://mopsicus.ruunity-eventbus.html"&gt;Используйте события&lt;/a&gt;. Во многих уроках и видео до сих пор используют метод &lt;code&gt;Update()&lt;/code&gt;. Он вызывается каждый кадр, а &lt;code&gt;FixedUpdate()&lt;/code&gt; по умолчанию вызывается 50 раз в секунду! Если у вас в этих методах лежит какой-то тяжелый код, то производительность приложения упадёт. В качестве альтернативы можно использовать: 1) событийный подход 2) корутины 3) централизованный Update 4) перейти на ECS&lt;/p&gt; &lt;/li&gt; &lt;/ol&gt;&lt;pre&gt;&lt;code&gt;/// &amp;lt;summary&amp;gt;
/// Пример с событиями (event-driven)
/// &amp;lt;/summary&amp;gt;
public class Player : MonoBehaviour {

    public Action OnDamaged;

    public void Damage(int value) {
        OnDamaged?.Invoke();
    }
}

/// &amp;lt;summary&amp;gt;
/// Интерфейс для вызова обновления
/// &amp;lt;/summary&amp;gt;
public interface IUpdatable {
    void OnUpdate();
}

/// &amp;lt;summary&amp;gt;
/// Менеджер для централизованного Update
/// &amp;lt;/summary&amp;gt;
public class Updater : MonoBehaviour {
    
    List&amp;lt;IUpdatable&amp;gt; updatables = new List&amp;lt;IUpdatable&amp;gt;();

    public void Register(IUpdatable item) =&amp;gt; updatables.Add(item);

    void Update() {
        foreach (var item in updatables) {
            item.OnUpdate();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;ol start="10"&gt; &lt;li&gt; &lt;p&gt;Уменьшайте размер текстур. Если не знаете как оптимизировать картинки при сохранении в редакторе, прогоните их через какой-нибудь &lt;a href="https://mopsicus.ruhttps://compresspng.com"&gt;онлайн оптимизатор&lt;/a&gt;. В некоторых случаях можно уменьшить размер на 20-40% от оригинала.&lt;/p&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;Ещё про текстуры. Если используйте спрайты вне атласов — старайтесь делать картинки (да и сами атласы тоже) размером кратным степени 2, т.е. 1024×1024, 2048×2048. Особенно актуально для WebGL билдов.&lt;/p&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;При возможности используйте одинаковые материалы. Если на объектах стоят разные материалы, они не будут &lt;abbr title="batching — процесс объединения нескольких отрисовок объектов в одну команду для GPU"&gt;батчится&lt;/abbr&gt; и будет больше вызовов отрисовки.&lt;/p&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;Старайтесь не использовать скрипты для автоматического расположения элементов UI: &lt;code&gt;ContentSizeFitter&lt;/code&gt;, &lt;code&gt;XXXLayoutGroup&lt;/code&gt;. Они удобные, но ресурсозатратные. Особенно если внутри большое количество элементов. Если количество и расположение элементов в процессе работы не будет меняться, можно отключить их в редакторе, после установки на нужные позиции.&lt;/p&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;Ещё про UI. Используйте разные канвасы (Canvas). При изменении почти любого параметра у любого объекта, весь канвас перерисовывается полностью! Поэтому при построении сложного UI имеет смысл статичные элементы располагать на отдельном канвасе. И конечно не изменять UI в &lt;code&gt;Update&lt;/code&gt;, а только по событиям, когда действительно что-то поменялось.&lt;/p&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;Оптимизируйте прокручивающиеся списки. Родной &lt;code&gt;ScrollView&lt;/code&gt; плохо справляется с большим количеством элементов. Используйте data-driven списки, например &lt;a href="https://mopsicus.ru../projects/uis.html"&gt;UIS&lt;/a&gt; &lt;span class="emoji"&gt;🙄&lt;/span&gt; Также есть смысл заменить &lt;code&gt;Mask&lt;/code&gt; на &lt;code&gt;RectMask2D&lt;/code&gt; в списках, потому что обычная маска работает через шейдер с обрезкой по альфе с помощью дополнительного материала (больше DC), а прямоугольная маска работает только с прямоугольниками, отсеивает пиксели и вершины за пределами области, не трогая шейдеры и не создавая лишние материалы, соответственно меньше DC и нагрузки на GPU.&lt;/p&gt; &lt;/li&gt; &lt;/ol&gt;&lt;p&gt;И списком статьи и видео по оптимизации:&lt;/p&gt;&lt;ul&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://youtu.be/7POYnqmDpAo?si=qVYJzOUWy66LZYQm"&gt;Практическое руководство по оптимизации Unity игр. Видео&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://unity.com/resources/mobile-xr-web-game-performance-optimization-unity-6"&gt;Optimize your game performance for mobile, XR, and the web in Unity&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://unity.com/ru/how-to/unity-ui-optimization-tips"&gt;Советы по оптимизации UI в Unity&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://habr.com/ru/companies/funcorp/articles/470608/"&gt;Оптимизация Unity UI от FUNCORP&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://dtf.ru/gamedev/84298-optimizaciya-unity-ui-bez-koda"&gt;Оптимизация Unity UI без кода от Banzai Games&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://vc.ru/id350787/120585-optimization"&gt;Советы: как оптимизировать 3D-игры&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://youtu.be/kzfawIay8G4?si=h3Xd_uVXiJ2fiwwt"&gt;Топ-10 ошибок в оптимизации Unity. Видео&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://habr.com/ru/companies/playgendary/articles/582950/"&gt;Оптимизация игр на Unity: проверенный в деле план&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://unity.com/blog/graphics-and-rendering-tips-from-survival-kids"&gt;Graphics and rendering tips from Survival Kids&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://mopsicus.ruhttps://habr.com/ru/companies/otus/articles/959150/"&gt;Руководство по оптимизации памяти в Unity 6&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt;</description>
  <guid>https://mopsicus.ru/notes/unity-game-optimization.html</guid>
  <link>https://mopsicus.ru/notes/unity-game-optimization.html</link>
  <pubDate>Wed, 13 Oct 2021 10:20:00 +0300</pubDate>
  <comments>https://mopsicus.ru/notes/unity-game-optimization.html#comments</comments>
  <author>mail@mopsicus.ru (Igor Lopatin)</author>
  <category>unity</category>
  <category>unity</category>
  <category>csharp</category>
  <category>architecture</category>
  <category>ui</category>
  <category>optimization</category>
  <category>list</category>
  <category>links</category>
</item>
<item>
  <title>Emoji в Unity</title>
  <description>&lt;p&gt;Как добавить поддержку эмоджи в Unity?&lt;/p&gt;&lt;p&gt;Компонент для работы с текстом в Unity — TMP, поддерживает отображение спрайтов с помощью специальных тегов. Это касается не только эмоджи, таким образом можно отобразить спрайт в тексте из любого атласа.&lt;/p&gt;&lt;p&gt;Но нас интересуют эмоджи (&lt;abbr title="вообще «правильнее» эмодзи, слово пришло из японского языка, где 絵 (э) означает картинка, а 文字 (модзи) — знак или символ"&gt;эмодзи&lt;/abbr&gt;). Много эмоджи. Помнить все юникоды всех символов — невозможно, добавить их в атлас и пронумеровать — можно, но неправильно. Поэтому будем использовать специальный хелпер, который по юникоду будет подставлять нужный спрайт в текстовый компонент.&lt;/p&gt;&lt;h2&gt;Подготовка&lt;/h2&gt;&lt;p&gt;Первое, нужен пак спрайтов с эмоджи. Мне очень нравится &lt;a href="https://mopsicus.ruhttps://joypixels.com"&gt;JoyPixels&lt;/a&gt;. Там есть все символы, постоянно обновляется и пополняется, качественная прорисовка. Для некоммерческого использования можно брать бесплатно.&lt;/p&gt;&lt;p&gt;Есть ещё &lt;a href="https://mopsicus.ruhttps://openmoji.org"&gt;OpenMoji&lt;/a&gt; — бесплатный пак со всеми символами, в том числе с разными начертаниями: контурные, цветные, чёрно-белые.&lt;/p&gt;&lt;p&gt;В общем, нужен пак со спрайтами, где каждое имя файла имеет соответствие с его юникод значением. Выглядит это как-то так:&lt;/p&gt;&lt;img class="emerge float-center" src="https://mopsicus.ru/images/assets/emoji-filenames.7e4c29a1.jpg" alt="Список эмоджи"&gt;&lt;p&gt;Также понадобится программа — &lt;a href="https://mopsicus.ruhttps://www.codeandweb.com/texturepacker"&gt;TexturePacker&lt;/a&gt; и расширение для Unity — &lt;a href="https://mopsicus.ruhttp://u3d.as/7d2"&gt;TexturePacker Importer&lt;/a&gt;. С помощью этого набора подготовим все необходимые файлы для использования в редакторе Unity.&lt;/p&gt;&lt;img class="emerge float-center" src="https://mopsicus.ru/images/assets/texture-packer.427c0d35.jpg" alt="TexturePacker с загруженными эмоджи"&gt;&lt;p&gt;У программы много настроек: различные типы текстур, параметры оптимизации, варианты упаковки и прочее. В &lt;a href="https://mopsicus.ruhttps://www.codeandweb.com/texturepacker/documentation"&gt;документации&lt;/a&gt; про всё это написано.&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;strong&gt;Unity® - Texture2D sprite sheet&lt;/strong&gt; – кнопка &lt;em&gt;Publish sprite sheet&lt;/em&gt;.&lt;/li&gt; &lt;li&gt;После этого меняем формат на &lt;strong&gt;JSON (Array)&lt;/strong&gt; и делаем экспорт еще раз.&lt;/li&gt; &lt;li&gt;Получили три файла: ХХХ.png, ХХХ.json и ХХХ.tpsheet.&lt;/li&gt; &lt;/ol&gt;&lt;div class="alert-info"&gt; &lt;p&gt;Обратите внимание что все эмоджи в один атлас конечно не поместятся, их там примерно 4 тысячи. Рекомендую разбить их на более мелкие атласы по группам, по категориям, по популярности, по частоте использования.&lt;/p&gt; &lt;/div&gt;&lt;h2&gt;Импорт в Unity&lt;/h2&gt;&lt;p&gt;Теперь нужно из полученных файлов сделать &lt;strong&gt;TMP Sprite Asset&lt;/strong&gt; — файл для TMP по которому он будет ориентироваться: какой файл показывать и с какими отступами.&lt;/p&gt;&lt;p&gt;Перетаскиваете полученные файлы в редактор и запускаете &lt;strong&gt;Window → TextMeshPro → Sprite Importer&lt;/strong&gt;.&lt;/p&gt;&lt;img class="emerge float-center" src="https://mopsicus.ru/images/assets/tmp-sprite-importer.dbe9f18a.jpg" alt="TMP Sprite Importer"&gt;&lt;p&gt;Выбираете в качестве источника данных ваш JSON файл и спрайт с эмоджи. Далее жмёте &lt;em&gt;Create&lt;/em&gt;, &lt;em&gt;Save&lt;/em&gt; по очереди и получаете TMP Sprite Asset.&lt;/p&gt;&lt;p&gt;Вот таких файлов может быть много. Вы разбиваете весь ваш пак с эмоджи на небольшие атласы 1024×1024 или 2048×2048, импортируете, создаёте спрайт ассеты для TMP.&lt;/p&gt;&lt;h2&gt;Использование&lt;/h2&gt;&lt;p&gt;Финальный этап. Как использовать полученные файлы и показывать эмоджи?&lt;/p&gt;&lt;p&gt;Если юникод эмоджи простой и не состоит из модификаторов (имя файла без дефиса), то всё будет работать даже если вы вставите эмоджи в инспекторе в поле ввода TMP. Эмоджи с модификаторами так использовать не получится, можете сами проверить. Поэтому придётся использовать скрипт который подставляет корректный спрайт.&lt;/p&gt;&lt;p&gt;Если вы не хотите каждый раз у текстового компонента TMP выставлять спрайт ассет, то можно в настройках указать какой файл брать по умолчанию. В проекте найти файл &lt;strong&gt;TMP Settings&lt;/strong&gt; или отрыть через &lt;strong&gt;Project settings → TextMesh Pro → Settings&lt;/strong&gt; и настроить блок &lt;em&gt;Default Sprite Asset&lt;/em&gt;.&lt;/p&gt;&lt;img class="emerge float-center" src="https://mopsicus.ru/images/assets/tmp-settings.546f1712.jpg" alt="TMP Settings"&gt;&lt;p&gt;Тут указано, что в моём проекте есть папка &lt;strong&gt;Resources/Emojis&lt;/strong&gt; в которой хранятся все сгенерированные выше спрайт ассеты и атласы, а также выбран какой ассет использовать по умолчанию.&lt;/p&gt;&lt;p&gt;К этому ассету в качестве «резервных» (fallback) подключены все остальные. Таким образом, если в «основном» ассете не будет нужного эмоджи, будет подгружен один из резервных и эмоджи отобразится в компоненте.&lt;/p&gt;&lt;img class="emerge float-center" src="https://mopsicus.ru/images/assets/tmp-fallback.67c380c5.jpg" alt="TMP Fallback List"&gt;&lt;h3&gt;EmojiHelper&lt;/h3&gt;&lt;p&gt;Для использования хелпера в коде, можно вызвать метод &lt;code&gt;CheckAndParseEmoji&lt;/code&gt; у вашего TMP компонента или статический метод &lt;code&gt;ParseEmojiSequence&lt;/code&gt; у хелпера:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;/// Можно применять сразу к TMP компоненту
MyTMPText.CheckAndParseEmoji(&amp;quot;string with emoji&amp;quot;);

/// Или распарсить и использовать позднее в другом месте
var tmpString = EmojiHelper.ParseEmojiSequence(&amp;quot;string with emoji&amp;quot;);
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Сейчас там большое регулярное выражение которое находит эмоджи в строке, охватывает практически все эмоджи, но при желании его можно обновить и добавить новые, т.к. новые эмоджи появляются почти каждый год.&lt;/p&gt;&lt;div class="alert"&gt; &lt;p&gt;&lt;a href="https://mopsicus.ruhttps://gist.github.com/mopsicus/3903a1f111a738375a363b9e3f058385"&gt;EmojiHelper&lt;/a&gt; доступен на Github&lt;/p&gt; &lt;/div&gt;&lt;p&gt;Вроде всё. Теперь ваши приложения и игры на Unity могут быть более привлекательными &lt;span class="emoji"&gt;😊&lt;/span&gt;&lt;/p&gt;</description>
  <guid>https://mopsicus.ru/notes/unity-emoji.html</guid>
  <link>https://mopsicus.ru/notes/unity-emoji.html</link>
  <pubDate>Sat, 11 Sep 2021 11:35:00 +0300</pubDate>
  <comments>https://mopsicus.ru/notes/unity-emoji.html#comments</comments>
  <author>mail@mopsicus.ru (Igor Lopatin)</author>
  <category>unity</category>
  <category>unity</category>
  <category>emoji</category>
  <category>textures</category>
  <category>github</category>
  <category>csharp</category>
  <category>tmp</category>
</item>
<item>
  <title>Градиентный текст в Unity</title>
  <description>&lt;p&gt;В пакете TextMeshPro есть встроенная градиентная заливка, но когда-то для моей задачи она не совсем подходила, потому что работает для каждого символа отдельно.&lt;/p&gt;&lt;p&gt;Выглядит это так:&lt;/p&gt;&lt;img class="emerge float-center" src="https://mopsicus.ru/images/assets/tmp-gradient-default.7779a812.jpg" alt="Unity TMP градиент для каждой буквы"&gt;&lt;p&gt;Пришлось написать небольшой скрипт, который вешается на текстовый компонент, берёт цвета градиента из свойств и применяет его для всего текста. Выглядит это как на обложке к заметке. Достаточно выставить настройки и прикрепить скрипт к объекту:&lt;/p&gt;&lt;img class="emerge float-center" src="https://mopsicus.ru/images/assets/tmp-gradient-inspector.ed0bd35d.jpg" alt="Unity TMP градиент для всего текста"&gt;&lt;p&gt;Сам принцип очень простой: разбиваем градиент на количество букв и потом по порядку применяем для каждой буквы свой диапазон цвета.&lt;/p&gt;&lt;div class="alert"&gt; &lt;p&gt;&lt;a href="https://mopsicus.ruhttps://gist.github.com/mopsicus/9d344451ca614d7e9937bc0c6da2b21d"&gt;Исходник&lt;/a&gt; доступен на Github&lt;/p&gt; &lt;/div&gt;</description>
  <guid>https://mopsicus.ru/notes/unity-text-gradient.html</guid>
  <link>https://mopsicus.ru/notes/unity-text-gradient.html</link>
  <pubDate>Tue, 12 May 2020 15:48:00 +0300</pubDate>
  <comments>https://mopsicus.ru/notes/unity-text-gradient.html#comments</comments>
  <author>mail@mopsicus.ru (Igor Lopatin)</author>
  <category>unity</category>
  <category>unity</category>
  <category>tmp</category>
  <category>github</category>
  <category>csharp</category>
</item>
<item>
  <title>TabsView для Unity</title>
  <description>&lt;p&gt;Раздел «Велосипедостроение» &lt;span class="emoji"&gt;😅&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Когда-то давно понадобилось сделать в UI горизонтальную прокрутку табами в одном прототипе. В iOS есть такой стандартный элемент. В Android по-моему тоже. Для Unity как известно стандартные UI компоненты довольно скудны. Поэтому каждый придумывает себе, по мере надобности. Вот и получилось когда-то такое, супер простое решение.&lt;/p&gt;&lt;div class="alert-info"&gt; &lt;p&gt;Если &lt;a href="https://mopsicus.ruhttp://dotween.demigiant.com/download.php"&gt;DOTween&lt;/a&gt; в проекте не используется, можно его выпилить и сделать переходы вручную, чтобы не тащить библиотеку.&lt;/p&gt; &lt;/div&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;/ul&gt;&lt;p&gt;Даже видео осталось:&lt;/p&gt;&lt;div class="media-wrap"&gt; &lt;iframe width="560" height="315" src="https://www.youtube.com/embed/RKvR7h9oFAI?si=QMfsFSVhbVDxODii" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen&gt;&lt;/iframe&gt; &lt;/div&gt;&lt;p&gt;Для применения в проде конечно не подходит, но для изучения — вполне.&lt;/p&gt;&lt;div class="alert"&gt; &lt;p&gt;&lt;span id="unity-tabs-view-stars"&gt;&lt;/span&gt; &lt;i class="fa-regular fa-star"&gt;&lt;/i&gt; Все &lt;a href="https://mopsicus.ruhttps://github.com/mopsicus/unity-tabs-view"&gt;исходники&lt;/a&gt; доступны на Github.&lt;/p&gt; &lt;/div&gt;</description>
  <guid>https://mopsicus.ru/notes/unity-tabs-view.html</guid>
  <link>https://mopsicus.ru/notes/unity-tabs-view.html</link>
  <pubDate>Wed, 19 Feb 2020 13:57:00 +0300</pubDate>
  <comments>https://mopsicus.ru/notes/unity-tabs-view.html#comments</comments>
  <author>mail@mopsicus.ru (Igor Lopatin)</author>
  <category>csharp</category>
  <category>github</category>
  <category>unity</category>
  <category>csharp</category>
  <category>video</category>
</item>

  </channel>
</rss>