\n(ShowCasing movement, just demo code will need tweaking)
\nUpdate Demo incorporates the random new direction selection, with special intersection tiles
\nLetters in SquareBrackes show possible direction change options.\n
const speed = 10;\n\n// Map with only 2 Tiles\n// 1 ... wall\n// 0 ... walkable tiles\n// 2 ... walkable tile, invisible marker to turn\nconst level = [\n [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],\n [1, 2, 0, 2, 0, 0, 2, 0, 2, 1],\n [1, 0, 1, 0, 1, 1, 0, 1, 0, 1],\n [1, 2, 0, 2, 0, 0, 2, 0, 2, 1],\n [1, 0, 1, 0, 1, 1, 0, 1, 0, 1],\n [1, 2, 0, 2, 0, 0, 2, 0, 2, 1],\n [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],\n];\n\n// demo helper variables\n// needs to be improved\nlet lastDirection = 'down';\n\n// quickfix\n// prevent double direction switch\nlet lastTile;\n\nclass DemoScene extends Phaser.Scene {\n create() {\n this.createDemoAssets();\n this.info = this.add.text(2, 2, '[]').setOrigin(0).setDepth(100).setSize(1);\n\n const map = this.make.tilemap({ data: level, tileWidth: 8, tileHeight: 8, width: 10, height: 8 });\n map.setCollision([1]);\n const tiles = map.addTilesetImage('tiles', 'tiles', 8, 8);\n const layer = map.createLayer(0, tiles, 0, 0);\n\n let ghost = this.physics.add.sprite(12, 12, 'ghost').setOrigin(0.5); // set Sprite Origin to center\n\n ghost.setVelocityY(speed);\n\n // bind ghost to scene\n this.ghost = ghost;\n\n console.info(ghost.body.velocity.y * -1);\n this.physics.add.collider(ghost, layer);\n this.physics.add.overlap(ghost, layer, (ghost, tile) => {\n // quick fix double direction switch\n if (lastTile == tile) {\n return;\n }\n //reset label\n this.info.setText(`[]`);\n\n // only check on special intersection tiles\n if (tile.index == 2) {\n \n let directions = [\n { x: 0, y: -1, name: 'up' },\n { x: 1, y: 0, name: 'right' },\n { x: 0, y: 1, name: 'down' },\n { x: -1, y: 0, name: 'left' },\n ]\n // selects all nodes that are \"walkable\" \"index == 0\"\n .filter((node) => {\n return layer.getTileAt(tile.x + node.x, tile.y + node.y).index == 0;\n })\n // creates a new array, with only the name\n .map((x) => x.name);\n\n console.info(directions);\n this.info.setText(`[${directions.map((x) => x[0]).join(',')}]`);\n\n // +4 is half tile width == middle of the tile (quickfix for demo)\n if (Phaser.Math.Distance.Between(tile.pixelX + 4, tile.pixelY + 4, ghost.x, ghost.y) < 1) {\n lastTile = tile;\n // select new direction an set speed\n this.setGhostdirection(directions);\n }\n }\n });\n }\n\n // from here helper functions for demo, should be improved/removed\n createDemoAssets() {\n let graphics = this.make.graphics();\n\n // tilesheet\n graphics.fillStyle(0xcdcdcd);\n graphics.fillRect(0, 0, 8, 8);\n graphics.fillStyle(0x7a7a7a);\n graphics.fillRect(8, 0, 8, 8);\n graphics.fillStyle(0x00ff00);\n graphics.fillRect(16, 0, 8, 8);\n graphics.generateTexture(`tiles`, 24, 8);\n\n //ghost\n graphics.fillStyle(0xff0000);\n graphics.fillRect(0, 0, 8, 8);\n graphics.generateTexture(`ghost`, 5, 5);\n }\n\n setGhostdirection(options) {\n // don't turn 180 degrees\n let goodOptions = options.filter((option) => option != lastDirection);\n\n let selectedOption = Phaser.Math.RND.pick(goodOptions);\n\n console.info(selectedOption);\n // set lastDirection\n lastDirection = selectedOption;\n // ghost speed\n switch (selectedOption) {\n case 'down':\n this.ghost.setVelocity(0, speed);\n break;\n case 'up':\n this.ghost.setVelocity(0, -speed);\n break;\n\n case 'left':\n this.ghost.setVelocity(-speed, 0);\n break;\n case 'right':\n this.ghost.setVelocity(speed, 0);\n break;\n }\n }\n}\n\nvar config = {\n width: 10 * 8,\n height: 8 * 8,\n zoom: 4,\n physics: {\n default: 'arcade',\n },\n scene: DemoScene,\n};\n\nnew Phaser.Game(config);\n\nconsole.clear();\ndocument.body.style = 'margin:0;';
\r\n<script src=\"//cdn.jsdelivr.net/npm/phaser/dist/phaser.min.js\"></script>
\r\n\n\n","author":{"@type":"Person","name":"winner_joiner"},"upvoteCount":2}}}Info: This is just demo code, it has a few quick fixes, just to make it run smooth for the demo (and to keep the code short), should be improved/optimized for "production".
\n
Reputation: 79
I am trying to make a pac_man game with Phaser 3. I have implemented the pac_man movement, however, I really need help with the ghost AI. The ghost is moving by its self, however, it is not passing through any intersections. Like, it only moves in a different direction when it collides with something else. I am trying to fix this, and yet I don't know how. I am using physics, with arcade. Here is some of the code:
newDirections(sprite) {
let directions = ['left', 'right', 'down', 'up']
return directions.filter(d => !sprite.body.blocked[d]);
}
This function is supposed to see any other paths the ghost can take. I am highly depended on sprite.body.blocked
for this (especially since I am using tiles), but it only reads when the sprite is colliding with something else.
The problem is, the sprite.body.blocked
only returns the accurate dictionary of of blocked areas on a sprite when the sprite is colliding with something. Otherwise, it only returns {none: true, up: false, down: false, left: false, right: false}
. Because of this, the ghost, 'sprite' only moves when it collides with something. Thus, it doesn't pass through any intersections.
So basically, I want something that will actively say whether or not it is blocked (touching) by an object or not, not only when it is colliding with an object.
I heavily appreciate any help or advice you'd give.
Upvotes: 1
Views: 88
Reputation: 14795
Well it depends on how you are moving the ghost, but an option would be to add a collider, that checks for overlaps (this.physics.add.overlap(...)
) and just querying the tiles around of the current tile the ghost is on (layer.getTileAt(...)
), if movement is possible.
Short Demo (Updated):
(ShowCasing movement, just demo code will need tweaking)
Update Demo incorporates the random new direction selection, with special intersection tiles
Letters in SquareBrackes show possible direction change options.
const speed = 10;
// Map with only 2 Tiles
// 1 ... wall
// 0 ... walkable tiles
// 2 ... walkable tile, invisible marker to turn
const level = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 2, 0, 2, 0, 0, 2, 0, 2, 1],
[1, 0, 1, 0, 1, 1, 0, 1, 0, 1],
[1, 2, 0, 2, 0, 0, 2, 0, 2, 1],
[1, 0, 1, 0, 1, 1, 0, 1, 0, 1],
[1, 2, 0, 2, 0, 0, 2, 0, 2, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
];
// demo helper variables
// needs to be improved
let lastDirection = 'down';
// quickfix
// prevent double direction switch
let lastTile;
class DemoScene extends Phaser.Scene {
create() {
this.createDemoAssets();
this.info = this.add.text(2, 2, '[]').setOrigin(0).setDepth(100).setSize(1);
const map = this.make.tilemap({ data: level, tileWidth: 8, tileHeight: 8, width: 10, height: 8 });
map.setCollision([1]);
const tiles = map.addTilesetImage('tiles', 'tiles', 8, 8);
const layer = map.createLayer(0, tiles, 0, 0);
let ghost = this.physics.add.sprite(12, 12, 'ghost').setOrigin(0.5); // set Sprite Origin to center
ghost.setVelocityY(speed);
// bind ghost to scene
this.ghost = ghost;
console.info(ghost.body.velocity.y * -1);
this.physics.add.collider(ghost, layer);
this.physics.add.overlap(ghost, layer, (ghost, tile) => {
// quick fix double direction switch
if (lastTile == tile) {
return;
}
//reset label
this.info.setText(`[]`);
// only check on special intersection tiles
if (tile.index == 2) {
let directions = [
{ x: 0, y: -1, name: 'up' },
{ x: 1, y: 0, name: 'right' },
{ x: 0, y: 1, name: 'down' },
{ x: -1, y: 0, name: 'left' },
]
// selects all nodes that are "walkable" "index == 0"
.filter((node) => {
return layer.getTileAt(tile.x + node.x, tile.y + node.y).index == 0;
})
// creates a new array, with only the name
.map((x) => x.name);
console.info(directions);
this.info.setText(`[${directions.map((x) => x[0]).join(',')}]`);
// +4 is half tile width == middle of the tile (quickfix for demo)
if (Phaser.Math.Distance.Between(tile.pixelX + 4, tile.pixelY + 4, ghost.x, ghost.y) < 1) {
lastTile = tile;
// select new direction an set speed
this.setGhostdirection(directions);
}
}
});
}
// from here helper functions for demo, should be improved/removed
createDemoAssets() {
let graphics = this.make.graphics();
// tilesheet
graphics.fillStyle(0xcdcdcd);
graphics.fillRect(0, 0, 8, 8);
graphics.fillStyle(0x7a7a7a);
graphics.fillRect(8, 0, 8, 8);
graphics.fillStyle(0x00ff00);
graphics.fillRect(16, 0, 8, 8);
graphics.generateTexture(`tiles`, 24, 8);
//ghost
graphics.fillStyle(0xff0000);
graphics.fillRect(0, 0, 8, 8);
graphics.generateTexture(`ghost`, 5, 5);
}
setGhostdirection(options) {
// don't turn 180 degrees
let goodOptions = options.filter((option) => option != lastDirection);
let selectedOption = Phaser.Math.RND.pick(goodOptions);
console.info(selectedOption);
// set lastDirection
lastDirection = selectedOption;
// ghost speed
switch (selectedOption) {
case 'down':
this.ghost.setVelocity(0, speed);
break;
case 'up':
this.ghost.setVelocity(0, -speed);
break;
case 'left':
this.ghost.setVelocity(-speed, 0);
break;
case 'right':
this.ghost.setVelocity(speed, 0);
break;
}
}
}
var config = {
width: 10 * 8,
height: 8 * 8,
zoom: 4,
physics: {
default: 'arcade',
},
scene: DemoScene,
};
new Phaser.Game(config);
console.clear();
document.body.style = 'margin:0;';
<script src="//cdn.jsdelivr.net/npm/phaser/dist/phaser.min.js"></script>
Info: This is just demo code, it has a few quick fixes, just to make it run smooth for the demo (and to keep the code short), should be improved/optimized for "production".
Upvotes: 2