О чем этот пример
При создании платформеров или игр с видом сверху столкновения с элементами окружения — это основа геймплея. Часто требуется не просто блокировать движение игрока, а инициировать специфические события, например, сбор предметов или активацию переключателей. Стандартный метод `collide` для этого не подходит. В этой статье мы разберем, как использовать метод `overlapTiles` из Arcade Physics для обработки кастомных пересечений с тайлами. Вы научитесь фильтровать определенные тайлы на карте, проверять наложение на них игрока и выполнять произвольный код в ответ, например, удалять собранные предметы.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
cursors;
pickups;
player;
layer;
tileset;
map;
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('tiles', 'assets/tilemaps/tiles/gridtiles.png');
this.load.tilemapTiledJSON('map', 'assets/tilemaps/maps/simple-map.json');
this.load.image('player', 'assets/sprites/phaser-dude.png');
}
create ()
{
this.map = this.make.tilemap({ key: 'map', tileWidth: 32, tileHeight: 32 });
this.tileset = this.map.addTilesetImage('tiles');
this.layer = this.map.createLayer('Level1', this.tileset);
this.map.setCollision([ 20, 48 ]);
this.pickups = this.map.filterTiles(tile => tile.index === 82);
this.player = this.add.rectangle(96, 96, 24, 38, 0xffff00);
this.physics.add.existing(this.player);
this.cursors = this.input.keyboard.createCursorKeys();
this.cursors.up.on('down', () =>
{
if (this.player.body.blocked.down)
{
this.player.body.setVelocityY(-360);
}
}, this);
this.info = this.add.text(10, 10, 'Player');
}
update ()
{
this.player.body.setVelocityX(0);
if (this.cursors.left.isDown)
{
this.player.body.setVelocityX(-200);
}
else if (this.cursors.right.isDown)
{
this.player.body.setVelocityX(200);
}
// Collide player against the tilemap layer
this.physics.collide(this.player, this.layer);
// Custom tile overlap check
this.physics.world.overlapTiles(this.player, this.pickups, this.hitPickup, null, this);
// Debug info
const blocked = this.player.body.blocked;
this.info.setText(`left: ${blocked.left} right: ${blocked.right} down: ${blocked.down}`);
}
hitPickup (player, tile)
{
this.map.removeTile(tile, 29, false);
this.pickups = this.map.filterTiles(tile => tile.index === 82);
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
parent: 'phaser-example',
physics: {
default: 'arcade',
arcade: {
gravity: { y: 600 }
}
},
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены и загрузка ресурсов
В методе preload загружаются необходимые ресурсы: изображение для тайлов, JSON-файл с картой, созданной в Tiled, и спрайт игрока. Обратите внимание на использование setBaseURL для указания базового пути.
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('tiles', 'assets/tilemaps/tiles/gridtiles.png');
this.load.tilemapTiledJSON('map', 'assets/tilemaps/maps/simple-map.json');
this.load.image('player', 'assets/spiles/phaser-dude.png');
}
Создание карты, игрока и физики
В create создается tilemap, слой и задаются тайлы, которые будут твердыми (через setCollision). Игрок представлен прямоугольником (add.rectangle) с физическим телом (physics.add.existing).
Ключевой момент — фильтрация тайлов. Метод map.filterTiles проходит по всем тайлам слоя и возвращает массив тех, которые соответствуют условию. В данном случае это тайлы с индексом 82 — наши собираемые предметы (pickups).
create ()
{
this.map = this.make.tilemap({ key: 'map', tileWidth: 32, tileHeight: 32 });
this.tileset = this.map.addTilesetImage('tiles');
this.layer = this.map.createLayer('Level1', this.tileset);
this.map.setCollision([ 20, 48 ]);
this.pickups = this.map.filterTiles(tile => tile.index === 82);
this.player = this.add.rectangle(96, 96, 24, 38, 0xffff00);
this.physics.add.existing(this.player);
this.cursors = this.input.keyboard.createCursorKeys();
// ... настройка прыжка ...
}
Основной игровой цикл и два типа проверок
В методе update происходит движение игрока и две принципиально разные физические проверки.
1. **`this.physics.collide(this.player, this.layer)`**: Стандартная проверка на столкновение. Она использует ранее заданные через `setCollision` тайлы (индексы 20 и 48) и останавливает движение игрока при контакте с ними.
2. **`this.physics.world.overlapTiles(this.player, this.pickups, this.hitPickup, null, this)`**: Проверка на *пересечение* (overlap) с массивом тайлов `this.pickups`. Если пересечение обнаружено, вызывается кастомная callback-функция `hitPickup`.
update ()
{
// Движение игрока влево/вправо
this.player.body.setVelocityX(0);
if (this.cursors.left.isDown) {
this.player.body.setVelocityX(-200);
} else if (this.cursors.right.isDown) {
this.player.body.setVelocityX(200);
}
// 1. Столкновение с препятствиями
this.physics.collide(this.player, this.layer);
// 2. Пересечение с собираемыми предметами
this.physics.world.overlapTiles(this.player, this.pickups, this.hitPickup, null, this);
}
Обработчик кастомного события
Функция hitPickup вызывается при каждом обнаружении пересечения игрока с тайлом из массива pickups. Она получает два аргумента: игровой объект (player) и тайл (tile), с которым произошло пересечение.
Внутри функции:
- `this.map.removeTile(tile, 29, false)` заменяет собранный тайл (индекс 82) на тайл с индексом 29 (например, на пустоту). Третий параметр `false` означает, что перестраивать карту коллизий для этого слоя не нужно.
- После изменения карты необходимо обновить массив `this.pickups`, так как один из тайлов был удален. Для этого `filterTiles` вызывается заново.
hitPickup (player, tile)
{
// Заменяем тайл предмета на пустой тайл (индекс 29)
this.map.removeTile(tile, 29, false);
// Обновляем массив активных предметов
this.pickups = this.map.filterTiles(tile => tile.index === 82);
}
Что попробовать дальше
Использование overlapTiles разделяет логику твердых препятствий и интерактивных объектов. Это делает код чище и гибче. Для экспериментов попробуйте:
1. Добавить анимацию или звук при сборе предмета.
2. Сделать предметы, которые не исчезают, а, например, телепортируют игрока.
3. Использовать разные callback-функции для разных типов тайлов (ключи, враги, бонусы).
4. Комбинировать overlapTiles с группами спрайтов для сложных интерактивных сцен.
