🧩 Рефакторинг Game of Life — Часть 1: Игровая логика
Привет! 👋 Сегодня начну разбор тотального рефакторинга моего старого проекта — игры «Жизнь».
Покажу, как я вычистил игровую логику: от монолитной кучи до внятной архитектуры 🔄
🚨 Проблема №1: God Object — когда один класс правит всем
🧠 Всё в одном классе: логика, отрисовка, ввод, состояния.
💀 Результат:
• Невозможно понять, где что происходит
• Одно изменение ломает три других
• Логика намертво привязана к WinForms
📸 Старый код на изображении 1
✅ Решение: разделяй и властвуй
Я разнёс код по модулям по смыслу 👇 (изображение 2)
Теперь:
• Каждый класс делает только своё дело
• Чтобы понять работу с полем — читаю только TileMap (100 строк вместо 500)
• Изменения изолированы
• Таймеры, графика и мышь теперь не зависят от логики игры
🚨 Проблема №2: Неявное управление состоянием
Пауза, рисование и возобновление были размазаны по коду 😵 (изображение 3)
Почему это проблема? 😤
❌ Неочевидное поведение
❌ Баги при добавлении функций
❌ Добавил «режим редактирования»? Теперь нужно обновить *все места*, где проверяется isStop. Забыл одно — баг 🐛
✅ Решение: State Machine 🧠
Добавил систему состояний (изображение 4)
Теперь переходы прозрачны (изображение 5)
Добавить новое состояние? Просто создаёшь новый класс. Всё.
🚨 Проблема №3: Логика и отрисовка перемешаны 🤦♂️
Расчёт поколений и рисование были в одном методе (изображение 6)
Итог:
• нельзя рассчитать без рисования (1000 поколений = 1000 отрисовок)
• нельзя перерисовать без пересчёта
• профилировать больно
✅ Решение: Model и View отдельно 🎨
Model — чистая логика (изображение 7)
View — отдельный рендерер
Теперь:
✅ Можно рассчитать без рисования
✅ Можно перерисовать без логики
✅ Можно менять визуал хоть на OpenGL
🚨 Проблема №4: Массивы повсюду
Три массива (arrayCell, nextArrayCell, lastArrayCell) в разных методах.
Постоянные проверки x < columns && y < rows и куча багов с индексами 🐛 (изображение 8)
✅ Решение: инкапсуляция в TileMap
Всё спрятано в одном месте 👇 (изображение 9)
Теперь:
• Один источник правды
• Нет багов с индексами
• Оптимизация централизована
🚨 Проблема №5: Правила игры захардкожены
if (!hasLife && textBoxB.Text.Contains(...))😬 Логика зависела от UI.
✅ Решение: GameSettings + CellType Registry (изображение 10)
Теперь каждый тип клетки знает свои правила,
а логика просто спрашивает у неё, что делать.
🔥 Можно легко добавить свои типы клеток
🔥 Свои правила
🔥 Даже комбинировать их — никаких ограничений!
🎯 Главные принципы
1️⃣ Single Responsibility Principle — каждый класс делает одно дело
2️⃣ Separation of Concerns — логика, UI, данные — раздельно
3️⃣ Encapsulation — детали реализации скрыты
4️⃣ Open/Closed Principle — легко расширять, не меняя старый код
5️⃣ Dependency Inversion — логика не зависит от UI
🔥 Выводы
Рефакторинг — это не «переписать красиво».
Это решение конкретных проблем 👇
1️⃣ Сложно понять код? → Раздели на классы
2️⃣ Баги при изменениях? → Инкапсулируй
3️⃣ Невозможно тестировать? → Отдели логику от UI
4️⃣ Трудно расширять? → Используй паттерны (State, Registry)
Ставь 🔥 если было полезно!
🔥7💋1
Game of Life: Иморт изображений.
😜 Иногда я могу так скажем сильно увлечься.
🫡 Собственно, как сейчас.
👾 В старом проекте была возомжность имортнуть файл и он бы отрисовлася по клеткам, там просто бралось среднее значение по RGB и если больше определенного значения, то клетка в белый красилась.
🚀 Но в новом я решил пойти дальше и юзаю различные алгоритмы для обработки краев, чтобы сделать контур.
⚙️ Но это не все, ведь пользователь сможет сам выбрать нужный пресет, по которому потом создатся поле.
🛠 Пока готова просто обработка фоток и их превью.
❤2🔥1💋1
👾 Ура, оно работает!
⚙️ Можно импортнуть любую фотку выбрать пресет и она конвертируется в поле клеточное.
💻 Также чутка обновил интерфейс.
⚙️ Можно импортнуть любую фотку выбрать пресет и она конвертируется в поле клеточное.
💻 Также чутка обновил интерфейс.
🔥8💋1
🏎 Пытаюсь как-то оптимзировать рендер, чтобы
👀 Сначала делал это на глаз, иногда было очевидно, хуже сделали мои нововведения или лучше.
☝️ Но не всегда.
📊 Поэтому добавил в правый верхний угол такую удобную менюшку со статистикой.
GPS (Generation Per Second) был как можно выше при больших размерах карты.👀 Сначала делал это на глаз, иногда было очевидно, хуже сделали мои нововведения или лучше.
☝️ Но не всегда.
📊 Поэтому добавил в правый верхний угол такую удобную менюшку со статистикой.
🔥6💋1
👾 Game of Life First Release
⚙️ Главной целью было отрефакторить старый код, но я сделал куда больше:
- WinForms > Wpf
- Импорт фото
- Запись эволюции
🔗 Потестировать можете тут (https://github.com/W1xon/Game-of-Life/releases/tag/v1.0.0)
🏎️ Оптимизация
🐢 Как оказалось двумерный массив
int [,] в качестве поя- не прям уж эффективен, перешел на int[].🛠️ Затем изменил логику обработки поля, вместо вложенных циклов, которые перебирают
int[,] заюзал unsafe код и указатели в одном цикле.✏️ Отрисовку картинки тоже изменил, вместо рисования всей картинки фиксируем только изменившееся клетки и перерисовывает эти области.
🏆 Как итог средний прирост GPS и составил примерно в 2 - 2.5 раза, то особенно заметно стало при импорте больших изображений.
Окно слева - старый
окно справа - новый
⚠️ В версии 1.0.0 используется не оптимизированный рендеринг, он будет в следующем релизе.
❤2🔥2💋1
💻 Небольшой интерактив для вас.
🎨 Создал свой ColorPicker, но не могу определиться с внешним видом.
🎨 Создал свой ColorPicker, но не могу определиться с внешним видом.
🔥1
🚀 Cellverse v1.1.0 — “Renaissance”
🎨 UI
Полностью переделал интерфейс — появились полупрозрачные элементы и лёгкое свечение.
— Добавил ColorPicker с RGB-слайдерами и 16 пресетами.
— Полупрозрачная панель производительности дополнена новой инфой.
— Всё оформлено в едином стиле.
⚡️ Performance
Рендеринг стал быстрее в 2 раза, а памяти жрёт в 5 раз меньше.
🔧 Под капотом
— TileMap теперь использует
HashSet<Vector>— Unsafe обработка массивов для скорости
— Упростил
CustomMessageBox— И да, проект теперь официально зовётся Cellverse, а не “Game of Life”
🔗 Релиз: v1.1.0
🧩 CmdWalker v0.1.1-alpha
Тоже не без апдейтов — исправил баги и подготовил релиз.
🔗 Релиз: v0.1.1-alpha
❤3🔥1