О чем этот пример

В создании игр часто требуется измерять дистанцию между объектами. Мы привыкли к евклидову расстоянию (прямая линия) или манхэттенскому (сумма по осям). Но есть и третий, мощный вариант — расстояние Чебышёва, которое идеально подходит для сеточных миров, ходьбы короля в шахматах или определения зоны обнаружения врага в виде квадрата. В этой статье разберем, как использовать `Phaser.Math.Distance.Chebyshev` на практике, чтобы легко создавать квадратные области влияния и детекторы приближения.

Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.

Живой запуск

Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.

Исходный код


class Example extends Phaser.Scene
{
    graphic;
    ufo;
    player;
    cursors;

    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('eyes', 'assets/sprites/slimeeyes.png');
        this.load.image('ufo', 'assets/sprites/ufo.png');
    }

    create ()
    {
        this.cursors = this.input.keyboard.createCursorKeys();

        this.player = this.add.image(400, 300, 'eyes');

        this.ufo = this.add.image(200, 150, 'ufo');

        this.graphic = this.add.graphics({ lineStyle: { color: 0x00ffff } });
    }

    update ()
    {
        if (this.cursors.left.isDown)
        {
            this.player.x -= 5;
        }
        else if (this.cursors.right.isDown)
        {
            this.player.x += 5;
        }

        if (this.cursors.up.isDown)
        {
            this.player.y -= 5;
        }
        else if (this.cursors.down.isDown)
        {
            this.player.y += 5;
        }

        const dist = Phaser.Math.Distance.Chebyshev(this.player.x, this.player.y, this.ufo.x, this.ufo.y);

        this.graphic
            .clear()
            .strokeRect(this.player.x - dist, this.player.y - dist, 2 * dist, 2 * dist);
    }
}

const config = {
    type: Phaser.AUTO,
    parent: 'phaser-example',
    scene: Example
};

const game = new Phaser.Game(config);

Что такое расстояние Чебышёва?

В отличие от привычного евклидова расстояния (корень из суммы квадратов разностей), расстояние Чебышёва определяется как максимум из абсолютных разностей координат по осям X и Y.

Грубо говоря, это длина стороны квадрата, в который можно заключить две точки, если его стороны параллельны осям координат. Именно это свойство делает его невероятно полезным для игр с сеточной или клеточной логикой, где движение возможно в 8 направлениях (как король в шахматах).

const dist = Phaser.Math.Distance.Chebyshev(x1, y1, x2, y2);
// dist = Math.max(Math.abs(x1 - x2), Math.abs(y1 - y2))

Структура примера: сцена, объекты и графика

В примере создается базовая сцена Phaser с тремя ключевыми объектами: игроком (player), НЛО (ufo) и графическим контейнером (graphic). Игрок управляется стрелками клавиатуры, НЛО статично. Графический объект будет использоваться для визуализации.

preload() {
    this.load.image('eyes', 'assets/sprites/slimeeyes.png');
    this.load.image('ufo', 'assets/sprites/ufo.png');
}

create() {
    this.cursors = this.input.keyboard.createCursorKeys();
    this.player = this.add.image(400, 300, 'eyes');
    this.ufo = this.add.image(200, 150, 'ufo');
    this.graphic = this.add.graphics({ lineStyle: { color: 0x00ffff } });
}

Расчет и визуализация в реальном времени

В методе update() происходит магия. Сначала обрабатывается ввод с клавиатуры для перемещения спрайта игрока. Затем, на каждом кадре, вычисляется расстояние Чебышёва между центром спрайта игрока и центром НЛО с помощью Phaser.Math.Distance.Chebyshev.

Полученное значение dist — это радиус квадрата (половина его стороны). Чтобы нарисовать квадрат, центр которого находится на игроке, мы вычисляем его левый верхний угол как (player.x - dist, player.y - dist), а ширину и высоту как 2 * dist.

update() {
    // ... обработка управления игроком ...

    const dist = Phaser.Math.Distance.Chebyshev(this.player.x, this.player.y, this.ufo.x, this.ufo.y);

    this.graphic
        .clear()
        .strokeRect(this.player.x - dist, this.player.y - dist, 2 * dist, 2 * dist);
}

Важно: метод .clear() вызывается каждый кадр, чтобы стереть предыдущий квадрат и нарисовать новый, соответствующий текущему расстоянию.

Практическое применение в играх

Зачем это нужно? Эта техника отлично подходит для: 1. **Квадратных зон обнаружения:** Если враг должен "увидеть" игрока только когда тот находится в пределах определенного квадрата вокруг него. 2. **Областей эффектов (AoE):** Для мгновенных или постоянных эффектов (яд, благословение), действующих в квадратной области. 3. **Поведения NPC:** Задание патрульной зоны в виде квадрата или определение, когда игрок вошел в "личное пространство" персонажа.

Проверка попадания в такую зону становится тривиальной:

if (Phaser.Math.Distance.Chebyshev(player.x, player.y, guard.x, guard.y) <= guardVisionRadius) {
    // Охранник обнаружил игрока!
}

Что попробовать дальше

Расстояние Чебышёва — это элегантный и производительный инструмент для работы с квадратными областями в играх на Phaser. Оно избавляет от необходимости проверять две координаты отдельно и идеально ложится на сеточную логику. **Идеи для экспериментов:** 1. Сделайте так, чтобы НЛО начинало преследовать игрока, когда тот заходит в квадратную зону. 2. Реализуйте "минное поле": разместите несколько статичных мин и визуализируйте опасную зону вокруг каждой, когда игрок приближается. 3. Используйте это расстояние для создания пошаговой тактической игры, где юниты могут атаковать цели в пределах квадрата определенного размера.