Цветные коробки
Пришло время немного разнообразить нашу игру! Пока процесс всё ещё простоват: ставь себе коробки на правильные места. Давайте сделаем её чуть более увлекательной, добавив коробкам цвет. Мы начнём с красной и синей — но вы можете их раскрасить так, как вам будет угодно! Теперь, чтобы выиграть, вам нужно будет поставить коробку на место с тем же цветом.
Ресурсы
Для начала давайте добавим новые ресурсы. Сохраните эти картинки с помощью контекстного меню — или создайте свои!

Структура директорий должна выглядеть схожим образом (не забудьте о том, что мы удалили стандартные коробку и место для коробки)
├── resources
│ └── images
│ ├── box_blue.png
│ ├── box_red.png
│ ├── box_spot_blue.png
│ ├── box_spot_red.png
│ ├── floor.png
│ ├── player.png
│ └── wall.png
├── src
│ ├── systems
│ │ ├── gameplay_state_system.rs
│ │ ├── input_system.rs
│ │ ├── mod.rs
│ │ └── rendering_system.rs
│ ├── components.rs
│ ├── constants.rs
│ ├── entities.rs
│ ├── main.rs
│ ├── map.rs
│ └── resources.rs
├── Cargo.lock
├── Cargo.toml
Изменения компонентов
Теперь добавим перечисление для цветов (если двух вам окажется мало, то добавлять новые нужно будет именно здесь):
После чего используем это перечисление и в коробке, и в месте для неё:
Создание сущностей
Давайте добавим цвет как параметр при создании коробок и мест для них и убедимся в том, что мы назначаем правильный ресурс — согласно цвету в перечислении.
Чтобы не ошибиться, прописывая пути к ресурсам, нам нужны понятные имена. Для этого мы будем называть изображения в виде "/images/box_{}.png", где {} — это цвет коробки, которую мы создаём. Трудность в том, что сейчас наши цвета хранятся в перечислении и компилятор не имеет понятия, как преобразовать BoxColour::Red в строку "red". А ведь это было бы очень удобно — просто написать colour.to_string() и получить нужный цвет. К счастью, у Rust есть отличный способ, которым мы можем это осуществить. Для этого нам будет нужно реализовать типаж Display для перечисления BoxColour. Вот как это должно выглядеть — мы просто определяем способ, которым нужно преобразовать элементы перечисления в строки:
Теперь добавим перечисление colour в код создания сущностей и воспользуемся великолепным colour.to_string(), который мы только что сделали.
Карта
Теперь немного изменим код генерации карты, чтобы добавить цветные коробки и места для них:
- "BB" — синяя коробка (blue box)
- "RB" — красная коробка (red box)
- "BS" — синее место (blue spot)
- "RS" — красное место (red spot)
И обновим её код в main.
Игровой процесс
Теперь, когда самая тяжёлая часть позади, мы можем проверить работу нашего кода. Вы увидите, что почти всё работает — за исключением одного досадного бага. Уровень считается пройденным даже если вы ставите красную коробку на синее место — и наоборот. Давайте это исправим.
Прежде мы выяснили, что данные хранятся в компонентах, а поведение свойственно системам — таковы принципы ECS. Сейчас нам нужно реализовать какое-то поведение, а потому логично предположить, что это будет система. Помните, как мы писали систему для проверки того, выиграл игрок или нет? Так вот — мы снова возвращаемся к ней.
Немного изменим функцию запуска, чтобы проверять совпадение цвета коробки и места для неё:
Теперь, если вы скомпилируете этот код, он должен ругаться на то, что мы сравниваем два перечисления с помощью оператора ==. По умолчанию Rust не знает, как это делать, так что мы должны его научить. И лучший способ — реализовать сравнение с помощью типажа PartialEq.
Самое время обсудить аннотации derive. Мы уже использовали их ранее, но не погружались в то, для чего они на самом деле нужны. Атрибуты derive можно применить к структурам или перечислениям — они позволят добавить стандартные реализации типажей к нашим типам. Например, здесь мы сообщаем Rust, что хотим добавить PartialEq к перечислению BoxColour.
Так выглядит стандартная реализация сравнения в PartialEq. Она просто проверяет что-то на равенство самому себе. Если это так, то сравнение успешно, иначе — нет. Не переживайте, если это не особенно внесло ясность.
С помощью строчки #[derive(PartialEq)] мы поставили Rust в известность, что BoxColour теперь реализует этот типаж. И это значит, что если мы попытаемся выполнить box_colour_1 == box_colour_2, Rust будет использовать именно эту реализацию, которая просто проверяет, являются ли colour_1 и colour_2 одним и тем же объектом. Это не самое изящное сравнение на свете, но к нашему случаю оно вполне подойдёт.
ЕЩЁ: Узнать больше о
PartialEqможно здесь, а о наследуемых типажах — здесь.
Теперь мы можем скомпилировать наш код и вкусить плоды тяжёлых трудов: игра работает и поздравляет с победой только в том случае, если мы её действительно заслужили!

КОД: Увидеть весь код из данной главы можно здесь.