# Код-ревью проекта «Змейка» ## Общая оценка Крепкая, аккуратно организованная работа. Автор хорошо владеет идиомами Bevy ECS: разделение на модули по расписаниям (`startup` / `update` / `fixed_update` / `post_update`), использование событий и обзёрверов, разнесение доменной модели в `domain/`. Логика движения по сетке с разбиением шага на границе (`split_by_grid`) — нетривиальная и покрыта хорошим юнит-тестом. Документирующие комментарии присутствуют почти везде. Есть один реальный функциональный баг (таймер спавна еды) и несколько игровых/стилистических замечаний. --- ## Критичные замечания ### 1. Таймер спавна еды тикается общим временем вместо дельты кадра `src/fixed_update.rs:92` ```rust if meal_timer.tick(time.elapsed()).just_finished() && query.count() < MAX_MEALS_ON_FIELD as usize { ``` `Timer::tick` принимает **дельту** времени за кадр, а `time.elapsed()` возвращает **суммарное** время с момента старта (постоянно растущее). В результате: - Первые ~2 секунды таймер копит `elapsed`, затем на каждом fixed-шаге тикается на всё накопленное время (2.0s, 2.03s, 2.06s …). Для `Repeating`-таймера это значит `just_finished()` становится `true` практически **каждый кадр** после первых 2 секунд. - Как следствие поле мгновенно заполняется до `MAX_MEALS_ON_FIELD` (5 штук), а после поедания еды тут же добивается обратно — заявленной механики «одна еда раз в 2 секунды» нет. **Исправление:** ```rust if meal_timer.tick(time.delta()).just_finished() && query.count() < MAX_MEALS_ON_FIELD as usize { ``` --- ## Средние замечания ### 2. Еда может появиться прямо под змеёй `src/fixed_update.rs:93-103` `used_positions` собирается **только из других объектов еды** — позиции головы и сегментов хвоста не учитываются. Поэтому еда легко спавнится под телом змеи, что портит игровой процесс (съедается «бесплатно» либо появляется в недосягаемом месте). Стоит добавить в исключаемые позиции клетки, занятые `Head`/`Tail`. ### 3. Столкновение с границей триггерится каждый кадр `src/post_update.rs:6-16` После выхода за границу скорость обнуляется, но координаты головы остаются за пределами поля, поэтому `Collision::Bounds` генерируется на **каждом** `PostUpdate`, и `handle_collisions` бесконечно переустанавливает скорость/цвет. У столкновения с хвостом такой проблемы нет (там есть отсечка `velocity.0 == 0.0`). Логичнее ввести явное состояние «игра окончена» (например, компонент-маркер `GameOver` или `State`), которое остановит проверки и позволит сделать рестарт. Сейчас после проигрыша игру можно только перезапустить процессом. --- ## Мелкие замечания и стиль ### 4. Диапазон случайной позиции еды меньше поля и смещён `src/fixed_update.rs:96-99` ```rust const ABS: i8 = (FIELD_SIZE as f32 / 2.0) as i8; // = 10 let rand_x = rng.random::() % ABS; // диапазон -9..=9 ``` При `FIELD_SIZE = 20` поле по логике занимает клетки `-10..=10`, но еда никогда не появляется в крайних клетках `±10`. Также остаток от деления даёт чуть неравномерное распределение и `0` встречается вдвое чаще краёв. Для равномерного распределения удобнее `rng.random_range(-ABS..=ABS)`. ### 5. Неограниченное ускорение/замедление времени `src/update.rs:30-40` `Equal`/`Minus` умножают `relative_speed` без верхней/нижней границы — можно довести скорость до неиграбельных значений (или почти до нуля). Стоит ограничить, например, диапазоном `[0.125, 8.0]`. ### 6. Опечатки в именах (для читаемости кода) - `check_bounds_colisions`, `check_tail_colisions` → `collisions` (`src/post_update.rs`, `src/game.rs`) - `GridBarier` → `GridBarrier` (`src/domain/events.rs` и все использования) - `splited` → `split` (`src/fixed_update.rs:23`) Не влияет на работу, но публичные имена типов/систем лучше писать без ошибок. ### 8. Учебный «мусор» в репозитории поставки - `tests/rust_learning.rs` — это личные эксперименты (`dbg!` по `saturating_sub`, `ceil`, `floor` и т.п.), а не тесты проекта. В сдаваемой работе их лучше убрать или вынести за пределы `tests/`. - `examples/` содержит туториалы по Bevy (`bevy_learning.rs`, `paddle_demo.rs` …), не относящиеся к игре. Для учебного проекта допустимо, но стоит хотя бы упомянуть их статус в README. --- ## Что сделано хорошо - **Чистая модульная структура**: доменная модель отделена от систем, системы сгруппированы по расписаниям Bevy. - **Идиоматичный ECS**: события (`Collision`, `GrowUp`, `GridBarier`), обзёрверы, `Single`-запросы, ресурсы (`SnakePath`, `MealRespawnTimer`). - **Аккуратная механика поворотов**: змея не может развернуться в себя (только `rotate_left`/ `rotate_right`), а первый сегмент за головой исключён из проверки столкновений — грамотное решение классических проблем «Змейки». - **`split_by_grid`** — сложная логика движения через узлы сетки покрыта развёрнутым юнит-тестом со множеством сценариев. - Настроен `rustfmt.toml`, продуманный релизный профиль (`opt-level="z"`, `lto`, `strip`), ведётся логирование в файл и stdout.