0Pricing
Learn Rust Coding · Урок

Столкновения и состояние игры

Добавляйте правила и условия победы.

«Столкновения и состояние игры» — бесплатный урок Learn Rust Coding на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Learn Rust Coding, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Learn Rust Coding содержит 4 уроков всего.

Основы столкновений AABB

Самая простая проверка столкновений — пересечение ограничивающих прямоугольников, выровненных по осям (AABB). Два прямоугольника сталкиваются, когда пересекаются по обеим осям — x и y.

Для спрайтов этот способ достаточно быстр и подходит для многих 2D-игр, пока не требуется полноценный физический движок.

fn overlaps(a_pos: Vec2, a_size: Vec2, b_pos: Vec2, b_size: Vec2) -> bool {
    (a_pos.x - b_pos.x).abs() < (a_size.x + b_size.x) * 0.5
        && (a_pos.y - b_pos.y).abs() < (a_size.y + b_size.y) * 0.5
}

Компонент Collider

Добавьте каждой сущности компонент Collider, содержащий половинные размеры или полный размер. Затем системы считывают преобразование и коллайдер, чтобы проверять пересечения.

Хранение размера в отдельном компоненте позволяет разным сущностям использовать одну и ту же систему столкновений.

#[derive(Component)]
struct Collider {
    size: Vec2,
}

Обнаружение столкновений в системе

Чтобы сравнить игрока со множеством препятствий, получите игрока одним запросом и переберите препятствия. Два отдельных запроса позволяют избежать повторного заимствования одних и тех же данных.

При пересечении выполните нужную реакцию — нанесите урон, отразите объект или удалите сущность — через Commands либо изменив компоненты.

fn detect(
    player: Query<(&Transform, &Collider), With<Player>>,
    walls: Query<(&Transform, &Collider), With<Wall>>,
) {
    let (pt, pc) = player.single().unwrap();
    for (wt, wc) in &walls {
        if overlaps(pt.translation.truncate(), pc.size, wt.translation.truncate(), wc.size) {
            info!("hit a wall");
        }
    }
}

Реакция с помощью событий

Вместо обработки последствий непосредственно в системе отправьте пользовательское Event. Одна система обнаруживает столкновения и создаёт события, а другая считывает их и применяет эффекты.

Так обнаружение отделяется от реакции, а каждая система остаётся небольшой.

#[derive(Event)]
struct CollisionEvent { entity: Entity }

fn emit(mut writer: EventWriter<CollisionEvent>, e: Entity) {
    writer.send(CollisionEvent { entity: e });
}

Чтение событий

Система чтения получает события из очереди с помощью EventReader и обрабатывает каждое из них. События существуют два кадра, поэтому их видит любой читатель.

Такая схема публикации и подписки хорошо масштабируется: можно иметь множество источников и получателей без тесной связанности.

fn on_hit(mut reader: EventReader<CollisionEvent>, mut commands: Commands) {
    for ev in reader.read() {
        commands.entity(ev.entity).despawn();
    }
}

Обновление счёта

Когда игрок подбирает предмет, измените глобальный ресурс Score с помощью ResMut.

Поскольку ресурсы уникальны, любая система может прочитать или обновить счёт без необходимости передавать его вручную.

fn collect(mut score: ResMut<Score>, mut reader: EventReader<PickupEvent>) {
    for _ in reader.read() {
        score.0 += 10;
    }
}

Состояния: общая картина

Игры переходят между режимами: меню, игра, пауза, конец игры. Bevy представляет их перечислением States, зарегистрированным через init_state.

Одновременно активно только одно состояние, а запуск систем можно ограничить определёнными состояниями.

#[derive(States, Default, Debug, Clone, PartialEq, Eq, Hash)]
enum GameState {
    #[default]
    Menu,
    Playing,
    GameOver,
}

Регистрация состояния

Добавьте состояние в приложение с помощью init_state. После этого Bevy отслеживает текущее значение и предоставляет ресурсы State и NextState.

Прочитайте текущее состояние с помощью Res<State<GameState>>, когда системе нужно узнать текущий режим.

App::new()
    .add_plugins(DefaultPlugins)
    .init_state::<GameState>()
    .run();

Запуск систем в состоянии

Ограничьте запуск систем определённым состоянием с помощью условия run_if in_state. Перемещение и столкновения выполняются только в состоянии Playing.

Так работает пауза: выйдите из состояния Playing, чтобы остановить системы игрового процесса, и они просто перестанут запускаться.

app.add_systems(
    Update,
    (drive, detect).run_if(in_state(GameState::Playing)),
);

Переход между состояниями

Чтобы изменить состояние, задайте NextState из любой системы. Bevy применит переход на следующей границе кадра.

Используйте расписания OnEnter и OnExit, чтобы создавать интерфейс при входе в состояние или очищать его при выходе.

fn lose(mut next: ResMut<NextState<GameState>>, health: Res<Health>) {
    if health.0 == 0 {
        next.set(GameState::GameOver);
    }
}

Очистка при выходе

При выходе из Playing удалите игровые сущности, чтобы следующий раунд начался с чистого состояния. Пометьте их маркером и удалите всё, что его содержит.

Зарегистрируйте очистку в OnExit(GameState::Playing), чтобы она выполнялась ровно один раз при каждом переходе.

fn cleanup(mut commands: Commands, q: Query<Entity, With<GameEntity>>) {
    for e in &q {
        commands.entity(e).despawn();
    }
}
// app.add_systems(OnExit(GameState::Playing), cleanup);

Быстрая проверка

Подумайте, как корректно приостановить игровой процесс.

Итоги: столкновения и состояние игры

Проверки AABB вместе с компонентом Collider обнаруживают пересечения; события отделяют обнаружение от реакции, а ресурс Score отслеживает прогресс. Система States с помощью run_if, NextState и OnEnter/OnExit организует меню, игровой процесс и состояние окончания игры.

Теперь у Вас есть полный цикл: создание, управление, столкновения и переходы. Соберите полноценную игру на Bevy из этих компонентов.

Часто задаваемые вопросы

Урок «Столкновения и состояние игры» бесплатный?

Да — полный текст урока «Столкновения и состояние игры» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Learn Rust Coding, подпишись на CoddyKit PRO. Курс Learn Rust Coding содержит 4 уроков всего.

Чему я научусь в уроке «Столкновения и состояние игры»?

Добавляйте правила и условия победы. Ты практикуешь Learn Rust Coding с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Learn Rust Coding?

Предыдущий опыт не требуется. Learn Rust Coding на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.

Сколько времени занимает урок «Столкновения и состояние игры»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Learn Rust Coding?

Да. Каждый урок Learn Rust Coding включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Мышление в стиле ECS
  2. Создание и перемещение сущностей
  3. Обработка ввода и времени
  4. Столкновения и состояние игры
← Назад к Learn Rust Coding