Что-то, что иногда происходит...

Dev Log Chapter

Контекстная фильтрация и смена хронологии логов

Завершена верстка продуктовых страниц по стандартизированному шаблону. Проведена оптимизация производительности: тяжелые CSS-маски фонов заменены на предварительно обработанные изображения (background-image). Внедрена продуктовая идея контекстной навигации: теперь со страницы проекта можно в один клик перейти в журнал его разработки. Для этого в MarkdownService настроена фильтрация логов по параметру URL, при которой порядок постов автоматически меняется на прямой (\(1 \rightarrow 2 \rightarrow 3\)). Это позволяет читать историю создания продукта как последовательную повесть, сохраняя при этом чистоту архитектуры (KISS) без усложнения главного макета.

Кастомный Markdown-движок и двуязычная архитектура

Реализована архитектура динамического C#-движка для публикации постов. Интегрированы библиотеки Markdig и YamlDotNet для автоматического считывания метаданных (дат, заголовков, массивов скриншотов) из YAML-шапок .md файлов. В MarkdownService настроен механизм парсинга двуязычных секций прямо в теле документов через HTML-комментарии ( и ) с подтяжкой текущего языка из сессии. Для раздела One Man Army разработана «газетная» Z-верстка на основе float с чередованием артов, а для Dev Log создан изолированный легковесный просмотрщик скриншотов (Lightbox).

DevLog Screenshot
DevLog Screenshot

Глобальная навигация и модуль квантовых ошибок

Завершена реорганизация системы «хлебных крошек» (Breadcrumbs). Логика их формирования перенесена на уровень глобального макета _Layout, что полностью исключило дублирование HTML-кода и рассинхронизацию стилей. Параллельно разработан кастомный модуль обработки серверных исключений (404/500). Через middleware UseStatusCodePagesWithReExecute запросы перенаправляются на единую страницу Error без изменения URL. Внедрена мультистатусная локализация и динамическая иллюстрация с Котом Шрёдингера, меняющаяся в зависимости от активной темы сайта.

DevLog Screenshot
DevLog Screenshot

Эволюция галереи и нативный API просмотра

Начата стандартизация страниц проектов. Первоначальная идея горизонтальной карусели для медиа-галереи провалила тесты: смешивание форматов 16:9, 9:16 и видеороликов ломало композицию, а сжатие интерфейсов делало скриншоты нечитаемыми. Концепция была полностью переработана в пользу системы «Сетка категорий + Нативный Лайтбокс». Просмотрщик крупного плана построен на базе HTML-элемента

с использованием браузерного механизма Top Layer API. Это избавило архитектуру от CSS-конфликтов слоев (z-index) и обеспечило корректное наложение модального окна поверх остального контента. Написан JS-модуль для зацикленной навигации с клавиатуры.

Внедрение мультиязычности и отказ от хардкода

Спроектирована и внедрена система локализации интерфейса. Произведен полный отказ от хардкода: элементы UI вынесены в централизованные JSON-словари. Для их обработки написан LocalizationService, инжектируемый в Razor-страницы. Реализован механизм бесшовного переключения языков через глобальный перехватчик (middleware) в Program.cs. Выбранный язык фиксируется в сессии, а смена происходит прозрачно, с сохранением текущей страницы и без сложной серверной маршрутизации. Настроен динамический рендеринг контента с корректной передачей HTML-тегов из JSON-базы в модальные окна.

DevLog Screenshot
DevLog Screenshot

Сетка проектов и физика частиц на HTML5 Canvas

Разработана страница Projects. Архитектурно изменен подход к компоновке: блок InCubation перенесен внутрь общего Flex-контейнера, превратившись в «жидкий» замыкающий модуль (flex-grow), автоматически заполняющий пространство без единого media-запроса. Внутри этого блока развернута фоновая система частиц на базе HTML5 Canvas. Запрограммировано «дыхание вакуума»: броуновское движение точек с таймером, периодически собирающим из хаоса замирающие изометрические структуры кубов. Для карточек проектов реализовано послойное наложение постеров, где цветной слой плавно проявляется через CSS-транзиции при наведении.

Верстка Главной страницы и CSS-маскирование

Сверстан главный экран (Hero Section) на базе Flexbox. В процессе выявилась проблема некорректного масштабирования фонового изображения на ультрашироких мониторах. Решением стала жесткая фиксация размера фона (1440x1080) с привязкой к центру и внедрение комбинированной CSS-маски с функцией calc() для плавного растворения границ изображения в базовом цвете. Для защиты десктопной верстки от деформаций применена блокировка ширины (min-width: 1200px) с активацией скролла. В навигационные карточки добавлены микро-взаимодействия: внешнее свечение и кастомная CSS-анимация @keyframes icon-pop для отклика иконок при наведении.

DevLog Screenshot
DevLog Screenshot

Мастер-шаблон, SVG-графика и бесшовные темы

Дан старт фазе Production. Развернут базовый проект ASP.NET Core и создан мастер-шаблон _Layout. Ключевой фичей интерфейса стала механика динамического переключения Темной и Светлой тем оформления через CSS Variables без перезагрузки страницы. Для поддержки этой системы все текстовые указатели и растровые иконки были заменены на Inline SVG-графику. Использование свойства fill="currentColor" обеспечило автоматическое перекрашивание иконок при смене темы. Настроена среда горячей перезагрузки (Hot Reload), а базовая типографика выстроена вокруг семейства шрифтов Inter с внедрением тонких начертаний.

Архитектура без баз данных и выбор Markdown

Проведен детальный разбор логики работы будущего сайта и утверждена контентная архитектура. Принято стратегическое решение отказаться от тяжелых баз данных в пользу концепции Stateless на базе ASP.NET Core (Razor Pages). Для контентных разделов (Dev Log, One Man Army) внедрена система публикации через файловое хранилище .md документов. Планируется, что движок будет парсить блок метаданных YAML Front Matter для извлечения дат и тегов, а затем динамически оборачивать текст в утвержденный дизайн-код. Также зафиксирована раздельная логика маршрутизации: лента Dev Log будет строиться по принципу LIFO (новые посты сверху), а история автора — по принципу FIFO (хронологическое чтение).

DevLog Screenshot

Пре-продакшен, UI-driven подход и рождение inHub

Начата разработка личной веб-площадки, призванной стать центральным хабом для всех программных проектов. На этапе пре-продакшена был сформирован Project Vision с философией «One Mind. Endless Ideas». Был выбран UI-driven подход: проектирование технической архитектуры осознанно было отложено до полного понимания визуальных концептов. Отрисованы макеты Главной страницы в светлой и темной темах, а также продумана базовая навигационная логика с промежуточным хабом проектов. В процессе дизайна площадка получила официальное название inHub и была классифицирована как полноценный самостоятельный продукт, выступающий главным полигоном для освоения стека веб-технологий.

DevLog Screenshot

Финтех-барьеры и приостановка коммерческого релиза

Этап публикации столкнулся с непреодолимыми регуляторными и техническими барьерами международных финансовых платформ для независимых разработчиков. При настройке выплат в Microsoft Partner Center система заблокировала налоговый профиль физического лица, потребовав корпоративную учетную запись Entra ID и бизнес-регистрацию.

Попытки развернуть продажи автономной версии на Gumroad и Lemon Squeezy также зашли в тупик: интеграция Stripe Connect отключила прямые банковские выплаты для региона, а альтернативные каналы заблокированы для приёма коммерческих средств физическими лицами без регистрации в США.

Продукт полностью заморожен в статусе Production Ready (v1.0). Готовый дистрибутив, маркетинговый пак и исходный код законсервированы. Этот опыт показал необходимость создания собственной независимой веб-платформы, что стало прямым импульсом для перехода к веб-разработке.

Финальная сборка: Inno Setup, MSIX и автоапдейтер

Продукт полностью подготовлен к дистрибуции. Скомпилирован автономный инсталлятор inNote_Setup_v1.0.exe (Inno Setup) с алгоритмом сжатия LZMA2, упаковавший монолит объёмом ~162 МБ в компактный дистрибутив весом 48.6 МБ. Параллельно через Windows Application Packaging Project собран нативный MSIX-пакет (.appxupload) под архитектуру x64 для Microsoft Store.

Разработан фоновый сервис автообновлений UpdateService. Сервис опрашивает публичный конфигурационный файл на GitHub через асинхронные запросы HttpClient с таймаутом в 3 секунды. Внедрён кастомный UI для оповещения о новых версиях и разработан флаг IsStoreVersion: при его значении true внутренний апдейтер полностью блокируется, изолируя сборку для Microsoft Store, где за обновление отвечает сама ОС.

DevLog Screenshot

Алгоритм спасения заметок и отказоустойчивость данных

Проведено комплексное альфа-тестирование утилиты на двухмониторной рабочей станции. Ограничители координат переведены на SystemParameters.VirtualScreen. Выявлен и устранён баг «потерянного окна»: при отключении внешнего дисплея заметки оставались в несуществующих координатах. Разработан спасательный алгоритм, который детектит вылет за пределы видимости и телепортирует «осиротевшие» стикеры в центр главного экрана.

Для предотвращения повреждения заметок при внезапном отключении питания в DataManager внедрена трехслойная система защиты (Data Resilience):

Атомарная запись: сохранение всегда ведётся через временный файл .tmp с последующей мгновенной заменой рабочего файла.

Автовосстановление: при каждом успешном сохранении создаётся бэкап .bak.

Карантин: при повреждении основного файла данные восстанавливаются из бэкапа, а повреждённый файл изолируется в карантин .corrupted для возможности ручного чтения.

DevLog Screenshot

Мультиязычность на лету и рефакторинг функционала

Разработана архитектура динамической локализации (i18n) с поддержкой четырёх языков: английского, испанского, немецкого и французского. Весь хардкод из XAML-разметки вынесен в изолированные словари ресурсов Lang.*.xaml. Создан LanguageManager, умеющий автоматически определять язык ОС пользователя (CultureInfo.CurrentUICulture) и переключать словари в коллекции MergedDictionaries прямо во время работы приложения без его перезапуска.

На этом же этапе проведён рефакторинг спорных фич: полностью отменена разработка механизма вычеркивания текста (чек-листа). Стандартный TextBox не поддерживал частичное форматирование, а переход на RichTextBox ломал компактную структуру JSON-сохранений и вступал в конфликт с алгоритмом автоподгонки кегля. Принято решение сохранить чистоту кодовой базы.

Глобальные хоткеи (Win32 API) и интеграция с реестромy

Внедрён функционал мгновенного создания заметок из любой точки операционной системы. Спроектирован класс HotKeyManager, использующий низкоуровневый импорт Win32 API из библиотеки user32.dll. Через дескриптор окна зарегистрирован глобальный хоткей Ctrl+Alt+N. Во избежание падения процесса при старте подписка привязана к событию SourceInitialized, когда ОС гарантированно выделяет валидный Handle.

Параллельно реализован StartupManager, управляющий автозагрузкой утилиты вместе с Windows. Модуль работает напрямую с реестром текущего пользователя (HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run). В реестр записывается чистый путь к исполняемому файлу, а логика включения и отключения опции вынесена в параметры конфигурации appConfig.json.

Фоновый запуск и гибридный сервис System Tray

Приложение переведено в формат автономной фоновой утилиты. Из разметки App.xaml полностью удалён параметр StartupUri, а режим закрытия программы переключён на ShutdownMode.OnExplicitShutdown. Это позволило утилите стартовать «невидимо» для инициализации внутренних сервисов без создания фантомных окон.

Для управления приложением разработан гибридный сервис TrayManager. Ввиду устаревшего визуального стиля WinForms NotifyIcon, стандартное контекстное меню было заменено на программно сгенерированное меню на чистом WPF, подтягивающее стили из единого словаря ресурсов. Для решения проблемы «зависания» меню при клике вне его области внутрь сервиса интегрировано скрытое фокусное окно размером 0х0 px, которое перетягивает фокус операционной системы на WPF-поток перед отрисовкой интерфейса трея.

Глобальный менеджер заметок и защита SSD

Завершён основной блок разработки логики управления окнами. Создан глобальный сервис-оркестратор NoteManager, реализованный в виде паттерна Синглтон (Singleton). Класс хранит живую коллекцию ObservableCollection в памяти, отслеживает активные экземпляры MainWindow, управляет их Z-индексами и связывает UI с данными через DataContext.

Прямая запись изменений на диск при каждом введённом символе создавала избыточную нагрузку на накопитель и выстраивала очереди файлового ввода-вывода. Для решения проблемы внедрён механизм умного автосохранения (Debounce) на базе DispatcherTimer. Таймер выдерживает 500 миллисекунд «тишины» после последнего действия пользователя (ввод текста, смещение окна, изменение цвета или угла) и только после этого выполняет единичную пакетную запись состояния в JSON-файл.

DevLog Screenshot

Дискретная тригонометрическая система вращения

Реализована функция кастомного наклона заметок на рабочем столе. Для исключения эффекта «тряски» холста при перемещении мыши расчёт углов переведён с динамических координат вращающегося элемента на статичное координатное пространство базового окна.

Вычисление дельты векторов выполняется через математическую функцию Math.Atan2 относительно неподвижного центра окна (180, 180). Внедрён дискретный замок с шагом в 5° путём округления вычисленного значения (Math.Round(angle / 5.0) * 5.0). Итоговый угол жёстко ограничен рамками от -15° до +15° и связан с моделью данных. Чтобы исключить случайно вызовы вращения при наборе текста, зона взаимодействия разделена: клик в пределах 35 px от любого угла стикера активирует режим поворота с подгрузкой курсора rotate_cursor.cur, а клик по центру отвечает за ввод и перемещение.

Рукописный шрифт Caveat и алгоритм автомасштабирования

Завершена работа над текстовым слоем стикера. В бинарную сборку приложения в качестве внутреннего ресурса интегрирован кастомный рукописный шрифт Caveat (Caveat-Regular.ttf). Это гарантирует идентичное отображение заметок на любых пользовательских ПК без необходимости установки шрифта в систему Windows.

Столкновение со стандартным движком типографики WPF вскрыло проблему избыточных межстрочных интервалов рукописного шрифта. Путём переключения режима на LineStackingStrategy.BlockLineHeight сняты системные ограничения, а межстрочный интервал зафиксирован на коэффициенте 0.88 от размера кегля. Разработан цикл реактивного автомасштабирования: при наборе текста система ужимает шрифт от 50 px до 20 px. Для защиты от переполнения физических границ листа (ExtentHeight > 253 px) внедрён триггер оверрана, который блокирует ввод как с клавиатуры, так и при вставке из буфера обмена, выполняя откаты со сохранением каретки.

DevLog Screenshot

Навигация через ПКМ и математика кастомного драга

Внедрена ключевая механическая особенность утилиты — перемещение окна по рабочему столу с помощью правой кнопки мыши (ПКМ). В процессе реализации возник архитектурный тупик: стандартный слой ввода TextBox полностью поглощал клики мыши, а встроенный метод WPF this.DragMove() аппаратно зашит исключительно на левую кнопку мыши.

Проблема перехвата решена подпиской на предварительные события окна PreviewMouseRightButtonDown, PreviewMouseMove и PreviewMouseRightButtonUp. Это позволило перехватывать сигналы ввода на стадии туннелирования до их спуска в TextBox. Написан кастомный алгоритм расчёта координат: метод PointToScreen() вычисляет абсолютную позицию курсора на экране, из которой вычитается точка клика, динамически смещая свойства Left и Top окна. Захват ввода через CaptureMouse() предотвратил срыв курсора при резких движениях, а для разделения перетаскивания и вызова меню внедрена дедзона чувствительности в 3 пикселя.

DevLog Screenshot

Бескаркасный интерфейс и векторная маска объёма

Сформирован кастомный визуальный слой приложения, полностью отказавшийся от стандартных рамок и заголовков Windows (WindowStyle="None", AllowsTransparency="True"). Чтобы превратить стандартное окно в реалистичный бумажный лист, спроектирован прямоугольный холст NoteCanvas в базовых габаритах 283х283 px.

На этапе проектирования выявлена проблема клиппинга: при включении диффузных теней DropShadowEffect границы окна обрезали мягкие края тени. Внешний контейнер MainWindow был принудительно расширен до 360х360 px, что создало необходимый воздушный зазор. Для воссоздания фактуры физической бумаги поверх цвета наложена векторная маска LinearGradientBrush под углом 50° с 8 точками перехода, создающая эффект цилиндрического изгиба и светового блика. Также внедрена палитра из 8 пастельных цветов с XAML-триггерами DataTriggers и инверсией тёмной темы, где чёрный градиент автоматически переключается в матовый белый отсвет.

Точка старта и выбор архитектуры MVVM

Принято решение расширить инженерный стек и освоить десктопную разработку под Windows в качестве второго направления после геймдева. Проект задуман как лёгкая альтернатива системным заметкам с акцентом на эстетику и отсутствие лишнего UI.

Заложена архитектурная основа на C# 12 и .NET 8 с использованием паттерна MVVM. Разработана базовая модель данных NoteModel, описывающая состояние стикера (координаты, размеры, индекс цвета, угол поворота, Z-слой и флаги видимости). Модель реализует интерфейс INotifyPropertyChanged с использованием атрибута [CallerMemberName] под строгий Nullable-контекст framework, что позволило добиться сборки проекта с нулевым количеством предупреждений компилятора. Для управления дисковыми операциями создан статический класс DataManager, отвечающий за базовую JSON-сериализацию списка заметок в файл конфигурации.

DevLog Screenshot

Релиз в Google Play и подведение итогов

Пройден полный цикл разработки: от первых эскизов и сборки в Unity до публикации в Google Play.

Завершён этап подготовки релизных материалов, заблокирован и сдан блок бюрократических проверок Google Play Console. Для выполнения требований платформы 2024+ годов успешно проведён 14-дневный закрытый бета-тест с привлечением 22 тестировщиков с Reddit по принципу Test-for-Test.

Проект официально опубликован в магазине и зафиксирован в качестве первой завершённой точки в истории соло-разработки.

Сражения за систему сохранений

Интеграция системы сохранений на финише разработки привела к ряду архитектурных проблем. Первые две попытки (наивная запись JSON и фрагментация данных по подсистемам) оказались неудачными: ломался онбординг, усложнялась логика, а кнопка Continue вела себя непредсказуемо. Решением стал откат к чистой сборке и создание единой точки управления через GameManager (SaveGame(), LoadGame(), DeleteSave()).

Отдельно устранён баг с отсутствием GameManager в стартовой сцене через прямое удаление файла сейва (File.Delete()). Реализовано три корректных точки сброса прогресса: в меню паузы, главном меню и после прохождения игры.

Сборка DRiot_v0.9.1 зафиксирована как стабильный предрелизный билд.

DevLog Screenshot

Порядок в звуковом хаосе

Завершена разработка глобальной аудиосистемы. Внедрён централизованный AudioManager на базе паттерна Singleton с защитой от уничтожения при смене сцен (DontDestroyOnLoad).

Все SFX распределены по событиям: клики UI-кнопок, перемешка плиток, сборка деталей, спавн дронов и финал. Выполнена балансировка громкости эффектов и настроены плавные переходы музыки. Сцена Победы драматизирована: длинный звук взрыва плавно переходит в финальный трек.

Звук — это не декорация, он задает ритм и эмоциональный контекст геймплея.

DevLog Screenshot
DevLog Screenshot

Три недели полировки

Блок работ по полировке UI, сцены Победы, исправлению конфликтов слоев интерфейса и корутин. Проект был восстановлен из стабильного архива и пересобран.

Переработана логика PauseMenu: панели меню теперь изолированы, а анимации дронов и огня скрываются через CanvasGroup без остановки фоновых корутин. Добавлено окно подтверждения для кнопки New Game во избежание случайного сброса прогресса. Увеличена задержка перед переходом к финальной сцене, чтобы сделать момент Победы более выразительным.

Полировка — это не просто косметика, а проверка архитектуры на прочность.

Война за одну кнопку (Skip Puzzle)

Зафиксирован баг, на решение которого потребовалась неделя. Проблема заключалась в связях между кнопкой пропуска пазла и состояниями игры. В состоянии PuzzleActive кнопка должна спавнить новый чертёж, а в IntroNext — работать как пауза.

Логика регулярно сбоила из-за того, что PuzzleManager не содержал ссылки на GameManager. Проблема была решена через добавление прямой ссылки и явный вызов SetState(GameState.PuzzleActive) при старте пазла.

Даже мелкая UI-фича способна вскрыть серьезные архитектурные просчёты.

Ассеты и классы

Подготовлены комплекты ассетов: игровые объекты, фоны, UI-панели и кнопки. Особое внимание было уделено строгому именованию объектов. Вместо неинформативных имён вроде Game Background 1 внедрена структурированная система: 1_Background_Start, 2_Background_Intro, 3_Background_Game, 4_Background_Final. Аналогичный подход применён и к деталям (например, FinishedPart-1).

В Unity спроектирована первичная архитектура классов: разделение на глобальные и локальные менеджеры и настройка их взаимодействия. Процесс проектирования проходил итеративно — от общих сущностей к детальной проработке подсистем.

Для фиксации стабильных состояний и возможности отката создана система ручного версионирования. Рабочие сборки копировались в отдельные архивы (например, DRiot_v0.2) с сохранением содержимого директорий Assets, Packages и ProjectSettings.

Точка старта

Начата проработка идей первого проекта на Unity. Отбросив концепции «Игры мечты», было выбрано направление простой 2D-головоломки. Тема сформировалась почти сразу: технологический сеттинг с роботами, лазерами и противостоянием. Идеей механики послужила классическая настольная игра «Пятнашка».

Геймплейная концепция оформилась в сборку дронов из чертежей. На игровом поле восстанавливаются перемешанные фрагменты чертежей компонентов. Для создания одного боевого дрона необходимо собрать комплект из 6 разных деталей, после чего дрон автоматически наносит урон вражеской Корпорации.

После фиксации идеи начался процесс отрисовки первых эскизов и графических ассетов в Photoshop и Illustrator.

Screenshot view