ARTICLE AD BOX
I'm using Phaser to write a simple game, but for some reason Phaser.physics.add.overlap was bugged out, and switching the fields corrected it. Here is the full code. The issue occurs at line 173. Earlier, at line 160, the physics worked correctly.
// Global hi-score tracker
let hiScore = 0;
// Enemy definitions
const enemyTypes = [
{ key: "enemy_simple", name: "Simple", difficulty: 1, hp: 100, damage: 80, speed: 2.5, score: 100, fires: false },
{ key: "enemy_fast", name: "Fast", difficulty: 2, hp: 70, damage: 40, speed: 5, score: 400, fires: false },
{ key: "enemy_heavy", name: "Heavy", difficulty: 3, hp: 150, damage: 100, speed: 1, score: 200, fires: false },
{ key: "enemy_seeking", name: "Seeking1", difficulty: 4, hp: 100, damage: 50, speed: 3, score: 200, fires: false },
{ key: "enemy_damage", name: "Big_Damage", difficulty: 5, hp: 300, damage: 250, speed: 2.5, score: 150, fires: false },
{ key: "enemy_shooty", name: "Shooty_Mc_Pew_Pew", difficulty: 6, hp: 100, damage: 50, speed: 1.5, score: 250, fires: true, bulletDamage: 50, reloadTime: 60 }
];
const difficultyCap = enemyTypes.length;
// Menu Scene
class MenuScene extends Phaser.Scene {
constructor() {
super("MenuScene");
}
preload() {
// Load assets here so they're ready before GameScene
this.load.image("player", "/static/assets/Player_1.png");
this.load.image("enemy_simple", "/static/assets/Simple.png");
this.load.image("enemy_fast", "/static/assets/Fast.png");
this.load.image("enemy_heavy", "/static/assets/Heavy.png");
this.load.image("enemy_seeking", "/static/assets/Seeking1.png");
this.load.image("enemy_damage", "/static/assets/Heavy Damage.png");
this.load.image("enemy_shooty", "/static/assets/Shooty_Mc_Pew_Pew.png");
this.load.image("bullet", "/static/assets/pBullet.png");
this.load.image("star", "/static/assets/Star.png");
this.load.image("pause", "/static/assets/pause.png");
console.log("Images loaded correctly")
}
create() {
this.titleText = this.add.text(this.scale.width / 2, this.scale.height / 3, "Up We Go!", {
fontSize: "48px",
fill: "#fff"
}).setOrigin(0.5); // origin (0.5,0.5) centers the text
let startText = this.add.text(this.scale.width / 2, this.scale.height / 2, "START", { fontSize: "32px", fill: "#0f0" }).setOrigin(0.5);
startText.setInteractive();
startText.on("pointerdown", () => {
this.scene.start("GameScene");
this.scene.stop("MenuScene");
console.log("Game started")
});
}
}
// Game Scene
class GameScene extends Phaser.Scene {
constructor() {
super("GameScene");
}
preload() {
// Assets already loaded in MenuScene, but preload is required
}
shootBullet() {
let bullet = this.bullets.create(this.player.x, this.player.y - 20, "bullet");
bullet.setVelocityY(-300);
}
create() {
this.score = 0;
this.kills=0;
this.difficulty = 2;
this.isMobile = this.sys.game.device.os.android || this.sys.game.device.os.iOS;
// Player setup
this.player = this.physics.add.sprite(this.scale.width/2, this.scale.height/2, "player");
this.player.name="Player"
this.player.setCollideWorldBounds(true);
this.player.hp = 200;
this.player.damage = 100;
console.log("Player spawned with HP:", this.player.hp);
this.fireCooldown = 300; // milliseconds between shots
this.lastFired = 0; // timestamp of last shot
// Controls
this.cursors = this.input.keyboard.createCursorKeys();
this.input.keyboard.addKeys("W,A,S,D,SPACE");
// Make a group of stars
this.stars = this.add.group({
key: "star",
repeat: 150, // number of stars
setXY: { x: 12, y: 12, stepX: 8, stepY: 6 }
});
if (this.isMobile) {
const buttonSize = 60;
const padding = 40;
// Create buttons
this.leftBtn = this.add.rectangle(padding, this.scale.height - buttonSize - padding * 2, buttonSize, buttonSize, 0x6666ff).setInteractive();
this.rightBtn = this.add.rectangle(padding + buttonSize * 2, this.scale.height - buttonSize - padding * 2, buttonSize, buttonSize, 0x6666ff).setInteractive();
this.upBtn = this.add.rectangle(padding + buttonSize, this.scale.height - buttonSize * 2 - padding * 2, buttonSize, buttonSize, 0x6666ff).setInteractive();
this.downBtn = this.add.rectangle(padding + buttonSize, this.scale.height - padding * 2, buttonSize, buttonSize, 0x6666ff).setInteractive();
this.fireBtn = this.add.rectangle(this.scale.width - buttonSize - padding, this.scale.height - buttonSize - padding, buttonSize, buttonSize, 0xff6666).setInteractive();
// Button labels
this.add.text(this.leftBtn.x, this.leftBtn.y, "←", { fontSize: "24px", fill: "#fff" }).setOrigin(0.5);
this.add.text(this.rightBtn.x, this.rightBtn.y, "→", { fontSize: "24px", fill: "#fff" }).setOrigin(0.5);
this.add.text(this.upBtn.x, this.upBtn.y, "↑", { fontSize: "24px", fill: "#fff" }).setOrigin(0.5);
this.add.text(this.downBtn.x, this.downBtn.y, "↓", { fontSize: "24px", fill: "#fff" }).setOrigin(0.5);
this.add.text(this.fireBtn.x, this.fireBtn.y, "FIRE", { fontSize: "18px", fill: "#fff" }).setOrigin(0.5);
// Button states
this.touchControls = { left: false, right: false, up: false, down: false, fire: false };
// Input handlers
this.leftBtn.on("pointerdown", () => this.touchControls.left = true);
this.rightBtn.on("pointerdown", () => this.touchControls.right = true);
this.upBtn.on("pointerdown", () => this.touchControls.up = true);
this.downBtn.on("pointerdown", () => this.touchControls.down = true);
this.fireBtn.on("pointerdown", () => this.touchControls.fire = true);
this.input.on("pointerup", () => {
this.touchControls.left = false;
this.touchControls.right = false;
this.touchControls.up = false;
this.touchControls.down = false;
this.touchControls.fire = false;
});
}
// Randomize positions
Phaser.Actions.RandomRectangle(this.stars.getChildren(), this.physics.world.bounds);
// Scale each star down
this.stars.children.iterate(star => {
star.setScale(Phaser.Math.FloatBetween(0.15, 0.25));
star.setDepth(-1); // push stars behind everything
});
// Groups
this.bullets = this.physics.add.group();
this.enemies = this.physics.add.group();
// Enemy spawn timer
this.time.addEvent({
delay: 2000,
callback: () => this.spawnEnemy(),
loop: true
});
this.scoreText = this.add.text(this.scale.width - 20, 20, "Score: 0", {
fontSize: "24px",
fill: "#fff"
}).setOrigin(1, 0); // anchor top-right
this.killsText = this.add.text(this.scale.width - 20, 50, "Kills: 0", {
fontSize: "24px",
fill: "#fff"
}).setOrigin(1, 0); // anchor top-right
// Collision: bullets vs enemies
this.physics.add.overlap(this.bullets, this.enemies, (bullet, enemy) => {
bullet.destroy();
enemy.hp -= this.player.damage;
if (enemy.hp <= 0) {
this.score += enemy.scoreValue;
this.kills+=1
this.killsText.setText("Kills: " + this.kills)
this.scoreText.setText("Score: " + this.score);
enemy.destroy();
}
});
// Collision: enemies vs player
this.physics.add.overlap(this.enemies, this.player, (playerObj, enemyObj) => {
console.log("Player collided with "+enemyObj.name);
console.log(
"Reducing player hp by", enemyObj.damage,
"from", playerObj.hp,
"to", playerObj.hp - enemyObj.damage
);
playerObj.hp -= enemyObj.damage;
console.log("Destroying enemy-"+enemyObj.name+".");
enemyObj.destroy();
this.score += enemyObj.scoreValue;
this.kills+=1
this.killsText.setText("Kills: "+this.kills)
this.scoreText.setText("Score: " + this.score);
if (playerObj.hp <= 0) {
console.log("Player dead");
this.endGame();
}
});
console.log("Creation complete")
}
update() {
this.stars.children.iterate(star => {
star.y += 1; // move down
if (star.y > this.scale.height) {
star.y = 0; // wrap to top
star.x = Phaser.Math.Between(0, this.scale.width);
}
});
if (this.kills/100==0){
this.difficulty+=1
this.kills+=1
}
if (this.isMobile) {
if (this.touchControls.left) this.player.setVelocityX(-200);
else if (this.touchControls.right) this.player.setVelocityX(200);
else this.player.setVelocityX(0);
if (this.touchControls.up) this.player.setVelocityY(-200);
else if (this.touchControls.down) this.player.setVelocityY(200);
else this.player.setVelocityY(0);
} else {
if (this.cursors.left.isDown || this.input.keyboard.keys[65].isDown) this.player.setVelocityX(-200);
else if (this.cursors.right.isDown || this.input.keyboard.keys[68].isDown) this.player.setVelocityX(200);
else this.player.setVelocityX(0);
if (this.cursors.up.isDown || this.input.keyboard.keys[87].isDown) this.player.setVelocityY(-200);
else if (this.cursors.down.isDown || this.input.keyboard.keys[83].isDown) this.player.setVelocityY(200);
else this.player.setVelocityY(0);
}
let now = this.time.now; // Phaser's built-in clock
if (this.isMobile) {
if (this.touchControls.fire && now > this.lastFired + this.fireCooldown) {
this.shootBullet();
this.lastFired = now;
}
} else {
if (this.input.keyboard.keys[32].isDown && now > this.lastFired + this.fireCooldown) {
this.shootBullet();
this.lastFired = now;
}
}
}
spawnEnemy() {
let roll = Phaser.Math.Between(1, 3);
let chosenDifficulty = this.difficulty + (roll === 1 ? -1 : roll === 3 ? +1 : 0);
chosenDifficulty = Phaser.Math.Clamp(chosenDifficulty, 1, difficultyCap);
let eligible = enemyTypes.filter(e => e.difficulty === chosenDifficulty);
if (eligible.length > 0) {
let chosen = Phaser.Utils.Array.GetRandom(eligible);
let x = Phaser.Math.Between(50, this.sys.game.config.width - 50);
let enemy = this.enemies.create(x, 0, chosen.key);
enemy.setVelocityY(chosen.speed * 40);
// Attach stats
enemy.name = chosen.name;
enemy.hp = chosen.hp;
enemy.damage = chosen.damage;
enemy.scoreValue = chosen.score;
console.log("Enemy ",enemy.name,"created")
}
}
endGame() {
hiScore = Math.max(hiScore, this.score);
this.scene.start("GameOverScene", { score: this.score, hiScore: hiScore });
}
}
// Game Over Scene
class GameOverScene extends Phaser.Scene {
constructor() {
super("GameOverScene");
}
preload() {}
create(data) {
this.add.text(this.scale.width / 2, this.scale.height / 3, `Score: ${data.score}`, { fontSize: "32px", fill: "#fff" }).setOrigin(0.5);
this.add.text(this.scale.width / 2, this.scale.height / 2, `Hi-Score: ${data.hiScore}`, { fontSize: "32px", fill: "#ff0" }).setOrigin(0.5);
this.time.delayedCall(3000, () => {
this.scene.stop("GameScene");
this.scene.start("MenuScene");
});
}
}
// Game config
const config = {
type: Phaser.AUTO,
width: window.innerWidth,
height: window.innerHeight,
backgroundColor: "#000000",
parent: "game",
physics: {
default: "arcade",
arcade: { debug: false }
},
scene: [MenuScene, GameScene, GameOverScene],
scale: {
mode: Phaser.Scale.RESIZE, // auto-resize with window
autoCenter: Phaser.Scale.CENTER_BOTH
}
};
new Phaser.Game(config);
By the way, yes, I did use a neural network to assist my in writing this code, because I am inexperienced at JavaScript.
