```git commit message
feat: Implement Phaser 3.90 framework with player movement, parallax backgrounds, mouse reticle, and sound effects - Replaced old Player class with new ES6 module-based implementation using A/D keys for movement - Added parallax background scrolling with multiple layers - Implemented mouse reticle with glow effect instead of default cursor - Integrated audio assets for game sounds and background music - Updated game configuration to 600x800 resolution with FIT scaling - Enhanced shooting mechanics with angle-based bullet trajectory calculation - Improved UI elements including score text and game over screen - Added enemy spawning and collision detection systems ```
This commit is contained in:
parent
fa73b3c44f
commit
8a7b321872
|
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
name: Phaser 3.90 Framework
|
||||||
|
---
|
||||||
|
|
||||||
|
- Follow best practices for Phaser 3.90
|
||||||
|
- Follow best ES6 practices
|
||||||
|
- Write class imports using full .js paths, NPM package manager or any other web packager is not installed
|
||||||
|
- Break things out into classes when it makes sense
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 2.4 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.4 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 95 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
Binary file not shown.
Binary file not shown.
|
|
@ -17,6 +17,7 @@
|
||||||
}
|
}
|
||||||
#game-container {
|
#game-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
cursor: none;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,40 @@
|
||||||
|
export class Player {
|
||||||
|
constructor(scene) {
|
||||||
|
this.scene = scene;
|
||||||
|
this.sprite = null;
|
||||||
|
this.cursors = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
create() {
|
||||||
|
// Create player ship
|
||||||
|
this.sprite = this.scene.physics.add.sprite(300, 700, 'player-sprite', 0);
|
||||||
|
this.sprite.setCollideWorldBounds(true);
|
||||||
|
|
||||||
|
// Setup controls
|
||||||
|
this.cursors = this.scene.input.keyboard.createCursorKeys();
|
||||||
|
|
||||||
|
// Replace cursor keys with A/D keys for movement
|
||||||
|
const aKey = this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.A);
|
||||||
|
const dKey = this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.D);
|
||||||
|
|
||||||
|
return { aKey, dKey };
|
||||||
|
}
|
||||||
|
|
||||||
|
update(aKey, dKey) {
|
||||||
|
// Player movement with A/D keys
|
||||||
|
if (aKey.isDown) {
|
||||||
|
this.sprite.setVelocityX(-200);
|
||||||
|
this.sprite.setFrame(1);
|
||||||
|
} else if (dKey.isDown) {
|
||||||
|
this.sprite.setVelocityX(200);
|
||||||
|
this.sprite.setFrame(2);
|
||||||
|
} else {
|
||||||
|
this.sprite.setVelocityX(0);
|
||||||
|
this.sprite.setFrame(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shoot() {
|
||||||
|
// Shooting functionality will be handled in GameScene
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
export const config = {
|
export const config = {
|
||||||
type: Phaser.AUTO,
|
type: Phaser.AUTO,
|
||||||
width: 800,
|
width: 600,
|
||||||
height: 600,
|
height: 800,
|
||||||
parent: 'game-container',
|
parent: 'game-container',
|
||||||
physics: {
|
physics: {
|
||||||
default: 'arcade',
|
default: 'arcade',
|
||||||
|
|
@ -14,5 +14,9 @@ export const config = {
|
||||||
preload: function() {},
|
preload: function() {},
|
||||||
create: function() {},
|
create: function() {},
|
||||||
update: function() {}
|
update: function() {}
|
||||||
|
},
|
||||||
|
scale: {
|
||||||
|
mode: Phaser.Scale.FIT,
|
||||||
|
autoCenter: Phaser.Scale.CENTER_BOTH
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
class Player extends Phaser.GameObjects.Container {
|
|
||||||
constructor(scene, x, y) {
|
|
||||||
super(scene, x, y);
|
|
||||||
|
|
||||||
// Create the player sprite
|
|
||||||
this.sprite = scene.add.sprite(0, 0, 'player');
|
|
||||||
this.add(this.sprite);
|
|
||||||
|
|
||||||
// Set up input controls using A/D keys instead of arrow keys
|
|
||||||
this.keys = scene.input.keyboard.addKeys({
|
|
||||||
a: Phaser.Input.Keyboard.KeyCodes.A,
|
|
||||||
d: Phaser.Input.Keyboard.KeyCodes.D
|
|
||||||
});
|
|
||||||
|
|
||||||
// Player properties
|
|
||||||
this.speed = 200;
|
|
||||||
this.health = 100;
|
|
||||||
this.isAlive = true;
|
|
||||||
|
|
||||||
// Set initial position
|
|
||||||
this.setPosition(x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
update() {
|
|
||||||
if (!this.isAlive) return;
|
|
||||||
|
|
||||||
// Move with A/D keys instead of arrow keys
|
|
||||||
if (this.keys.a.isDown) {
|
|
||||||
this.x -= this.speed * this.scene.game.loop.delta / 1000;
|
|
||||||
}
|
|
||||||
if (this.keys.d.isDown) {
|
|
||||||
this.x += this.speed * this.scene.game.loop.delta / 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keep player within bounds
|
|
||||||
this.x = Phaser.Math.Clamp(this.x, 25, 775);
|
|
||||||
|
|
||||||
// Update position of the container's child sprite
|
|
||||||
this.sprite.setPosition(0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
aimAt(targetX, targetY) {
|
|
||||||
// Not used in current implementation but kept for potential future use
|
|
||||||
}
|
|
||||||
|
|
||||||
fire() {
|
|
||||||
if (!this.isAlive) return;
|
|
||||||
|
|
||||||
// Fire bullet from player position
|
|
||||||
const bullet = new Bullet(this.scene, this.x, this.y - 20);
|
|
||||||
this.scene.bullets.add(bullet);
|
|
||||||
|
|
||||||
// Set bullet velocity
|
|
||||||
bullet.setVelocityY(-300);
|
|
||||||
}
|
|
||||||
|
|
||||||
hit(damage) {
|
|
||||||
if (!this.isAlive) return;
|
|
||||||
|
|
||||||
this.health -= damage;
|
|
||||||
|
|
||||||
if (this.health <= 0) {
|
|
||||||
this.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { Player } from '../Player.js';
|
||||||
|
|
||||||
export class GameScene extends Phaser.Scene {
|
export class GameScene extends Phaser.Scene {
|
||||||
constructor() {
|
constructor() {
|
||||||
super({ key: 'GameScene' });
|
super({ key: 'GameScene' });
|
||||||
|
|
@ -6,17 +8,38 @@ export class GameScene extends Phaser.Scene {
|
||||||
this.player = null;
|
this.player = null;
|
||||||
this.enemies = null;
|
this.enemies = null;
|
||||||
this.bullets = null;
|
this.bullets = null;
|
||||||
this.cursors = null;
|
|
||||||
|
|
||||||
// Game state
|
// Game state
|
||||||
this.score = 0;
|
this.score = 0;
|
||||||
this.gameOver = false;
|
this.gameOver = false;
|
||||||
|
|
||||||
|
// Mouse reticle
|
||||||
|
this.reticle = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
preload() {
|
preload() {
|
||||||
// Load assets here (will be implemented in a later step)
|
// Load assets here (will be implemented in a later step)
|
||||||
console.log('Loading assets...');
|
console.log('Loading assets...');
|
||||||
|
|
||||||
|
// Load reticle image
|
||||||
|
this.load.image('reticle', 'assets/reticle.png');
|
||||||
|
|
||||||
|
// Load background images
|
||||||
|
this.load.image('bg-layer1', 'assets/neon-city-bg.png');
|
||||||
|
//this.load.image('bg-layer2', 'assets/bg-layer2.png');
|
||||||
|
//this.load.image('bg-layer3', 'assets/bg-layer3.png');
|
||||||
|
|
||||||
|
// Load Sprites
|
||||||
|
this.load.spritesheet('player-sprite', 'assets/player-sprite.png', {
|
||||||
|
frameWidth: 48,
|
||||||
|
frameHeight: 48
|
||||||
|
});
|
||||||
|
|
||||||
|
// Load background music and sounds
|
||||||
|
this.load.audio('bgm', 'assets/music/NeonPulse.mp3');
|
||||||
|
this.load.audio('main-gun', 'assets/sounds/main-gun.mp3');
|
||||||
|
this.load.audio('enemy-kill', 'assets/sounds/enemy-kill.mp3');
|
||||||
|
|
||||||
// Create simple placeholder graphics for now
|
// Create simple placeholder graphics for now
|
||||||
this.createPlaceholderGraphics();
|
this.createPlaceholderGraphics();
|
||||||
}
|
}
|
||||||
|
|
@ -25,20 +48,20 @@ export class GameScene extends Phaser.Scene {
|
||||||
// Set background color
|
// Set background color
|
||||||
this.cameras.main.setBackgroundColor('#0a0a2a');
|
this.cameras.main.setBackgroundColor('#0a0a2a');
|
||||||
|
|
||||||
// Create player ship
|
// Create parallax backgrounds
|
||||||
this.player = this.physics.add.sprite(400, 500, 'player');
|
this.createBackgrounds();
|
||||||
this.player.setCollideWorldBounds(true);
|
|
||||||
|
// Create player instance
|
||||||
|
this.player = new Player(this);
|
||||||
|
const { aKey, dKey } = this.player.create();
|
||||||
|
|
||||||
// Create groups for bullets and enemies
|
// Create groups for bullets and enemies
|
||||||
this.bullets = this.physics.add.group();
|
this.bullets = this.physics.add.group();
|
||||||
this.enemies = this.physics.add.group();
|
this.enemies = this.physics.add.group();
|
||||||
|
|
||||||
// Setup controls
|
|
||||||
this.cursors = this.input.keyboard.createCursorKeys();
|
|
||||||
|
|
||||||
// Setup collision detection
|
// Setup collision detection
|
||||||
this.physics.add.overlap(this.bullets, this.enemies, this.hitEnemy, null, this);
|
this.physics.add.overlap(this.bullets, this.enemies, this.hitEnemy, null, this);
|
||||||
this.physics.add.overlap(this.player, this.enemies, this.hitPlayer, null, this);
|
this.physics.add.overlap(this.player.sprite, this.enemies, this.hitPlayer, null, this);
|
||||||
|
|
||||||
// Start enemy spawning
|
// Start enemy spawning
|
||||||
this.time.addEvent({
|
this.time.addEvent({
|
||||||
|
|
@ -50,24 +73,82 @@ export class GameScene extends Phaser.Scene {
|
||||||
|
|
||||||
// Create UI elements
|
// Create UI elements
|
||||||
this.createUI();
|
this.createUI();
|
||||||
|
|
||||||
|
// Setup mouse input for shooting and reticle
|
||||||
|
this.setupMouseInput();
|
||||||
|
|
||||||
|
// Play background music
|
||||||
|
this.bgMusic = this.sound.add('bgm', { loop: true });
|
||||||
|
this.bgMusic.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
createBackgrounds() {
|
||||||
|
// Create multiple background layers with different speeds
|
||||||
|
this.bgLayer1 = this.add.image(280, 30, 'bg-layer1').setOrigin(0.5);
|
||||||
|
//this.bgLayer2 = this.add.image(400, 300, 'bg-layer2').setOrigin(0.5);
|
||||||
|
//this.bgLayer3 = this.add.image(400, 300, 'bg-layer3').setOrigin(0.5);
|
||||||
|
|
||||||
|
// Set different speeds for parallax effect (smaller = slower)
|
||||||
|
this.bgSpeed1 = 0.2;
|
||||||
|
//this.bgSpeed2 = 0.5;
|
||||||
|
//this.bgSpeed3 = 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
if (this.gameOver) return;
|
if (this.gameOver) return;
|
||||||
|
|
||||||
// Player movement
|
// Update background positions for parallax effect
|
||||||
if (this.cursors.left.isDown) {
|
this.updateBackgrounds();
|
||||||
this.player.setVelocityX(-200);
|
|
||||||
} else if (this.cursors.right.isDown) {
|
// Get A/D keys for player movement
|
||||||
this.player.setVelocityX(200);
|
const aKey = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.A);
|
||||||
} else {
|
const dKey = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.D);
|
||||||
this.player.setVelocityX(0);
|
|
||||||
|
// Update player with A/D controls
|
||||||
|
this.player.update(aKey, dKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shooting
|
updateBackgrounds() {
|
||||||
if (Phaser.Input.Keyboard.JustDown(this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE))) {
|
// Move background layers at different speeds
|
||||||
this.shoot();
|
this.bgLayer1.y += this.bgSpeed1;
|
||||||
|
//this.bgLayer2.x -= this.bgSpeed2;
|
||||||
|
//this.bgLayer3.x -= this.bgSpeed3;
|
||||||
|
|
||||||
|
// Reset positions to create continuous scrolling effect
|
||||||
|
console.log(this.bgLayer1.y);
|
||||||
|
if (this.bgLayer1.y > 736) {
|
||||||
|
this.bgLayer1.y = 30; // Width of game + layer width
|
||||||
}
|
}
|
||||||
|
// if (this.bgLayer2.x < -400) {
|
||||||
|
// this.bgLayer2.x = 1200;
|
||||||
|
// }
|
||||||
|
// if (this.bgLayer3.x < -400) {
|
||||||
|
// this.bgLayer3.x = 1200;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
setupMouseInput() {
|
||||||
|
// Create mouse reticle as sprite (replacing the circle)
|
||||||
|
this.reticle = this.add.sprite(0, 0, 'reticle');
|
||||||
|
this.reticle.setOrigin(0.5);
|
||||||
|
this.reticle.preFX.addGlow(0x00ffcc, .7);
|
||||||
|
|
||||||
|
// Update reticle position with mouse
|
||||||
|
this.input.on('pointermove', (pointer) => {
|
||||||
|
if (this.gameOver) return;
|
||||||
|
|
||||||
|
this.reticle.x = pointer.x;
|
||||||
|
this.reticle.y = pointer.y;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Shooting with left mouse button
|
||||||
|
this.input.on('pointerdown', (pointer) => {
|
||||||
|
if (this.gameOver) return;
|
||||||
|
|
||||||
|
if (pointer.leftButtonReleased()) {
|
||||||
|
this.shoot(pointer);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
createPlaceholderGraphics() {
|
createPlaceholderGraphics() {
|
||||||
|
|
@ -76,17 +157,17 @@ export class GameScene extends Phaser.Scene {
|
||||||
canvas.width = 32;
|
canvas.width = 32;
|
||||||
canvas.height = 32;
|
canvas.height = 32;
|
||||||
|
|
||||||
// Player ship (triangle)
|
// // Player ship (triangle)
|
||||||
const ctx = canvas.getContext('2d');
|
// const ctx = canvas.getContext('2d');
|
||||||
ctx.fillStyle = '#00ffcc';
|
// ctx.fillStyle = '#00ffcc';
|
||||||
ctx.beginPath();
|
// ctx.beginPath();
|
||||||
ctx.moveTo(16, 5);
|
// ctx.moveTo(16, 5);
|
||||||
ctx.lineTo(5, 30);
|
// ctx.lineTo(5, 30);
|
||||||
ctx.lineTo(27, 30);
|
// ctx.lineTo(27, 30);
|
||||||
ctx.closePath();
|
// ctx.closePath();
|
||||||
ctx.fill();
|
// ctx.fill();
|
||||||
|
|
||||||
this.textures.addCanvas('player', canvas);
|
// this.textures.addCanvas('player', canvas);
|
||||||
|
|
||||||
// Enemy (square)
|
// Enemy (square)
|
||||||
const enemyCanvas = document.createElement('canvas');
|
const enemyCanvas = document.createElement('canvas');
|
||||||
|
|
@ -116,14 +197,14 @@ export class GameScene extends Phaser.Scene {
|
||||||
createUI() {
|
createUI() {
|
||||||
// Create score text
|
// Create score text
|
||||||
this.scoreText = this.add.text(16, 16, 'Score: 0', {
|
this.scoreText = this.add.text(16, 16, 'Score: 0', {
|
||||||
fontSize: '32px',
|
fontSize: '28px',
|
||||||
fill: '#ffffff',
|
fill: '#ffffff',
|
||||||
fontFamily: 'Arial'
|
fontFamily: 'Arial'
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create game title
|
// Create game title
|
||||||
this.titleText = this.add.text(400, 50, 'Zenith Vector', {
|
this.titleText = this.add.text(300, 50, 'Zenith Vector', {
|
||||||
fontSize: '48px',
|
fontSize: '40px',
|
||||||
fill: '#00ffcc',
|
fill: '#00ffcc',
|
||||||
fontFamily: 'Arial',
|
fontFamily: 'Arial',
|
||||||
align: 'center'
|
align: 'center'
|
||||||
|
|
@ -131,13 +212,32 @@ export class GameScene extends Phaser.Scene {
|
||||||
this.titleText.setOrigin(0.5);
|
this.titleText.setOrigin(0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
shoot() {
|
shoot(pointer) {
|
||||||
const bullet = this.bullets.create(this.player.x, this.player.y - 20, 'bullet');
|
// Calculate angle between player and mouse
|
||||||
bullet.setVelocityY(-300);
|
const angle = Phaser.Math.Angle.Between(
|
||||||
|
this.player.sprite.x,
|
||||||
|
this.player.sprite.y,
|
||||||
|
pointer.x,
|
||||||
|
pointer.y
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create bullet at player position
|
||||||
|
const bullet = this.bullets.create(this.player.sprite.x, this.player.sprite.y, 'bullet');
|
||||||
|
|
||||||
|
// Set velocity based on angle and speed
|
||||||
|
const speed = 400;
|
||||||
|
bullet.setVelocityX(Math.cos(angle) * speed);
|
||||||
|
bullet.setVelocityY(Math.sin(angle) * speed);
|
||||||
|
|
||||||
|
// Scale bullet
|
||||||
bullet.setScale(1.5);
|
bullet.setScale(1.5);
|
||||||
|
|
||||||
|
// Sound Effects
|
||||||
|
this.sound.play('main-gun');
|
||||||
}
|
}
|
||||||
|
|
||||||
spawnEnemy() {
|
spawnEnemy() {
|
||||||
|
// Spawn enemies at random x position within game width (800px)
|
||||||
const x = Phaser.Math.Between(20, 780);
|
const x = Phaser.Math.Between(20, 780);
|
||||||
const enemy = this.enemies.create(x, 0, 'enemy');
|
const enemy = this.enemies.create(x, 0, 'enemy');
|
||||||
|
|
||||||
|
|
@ -152,6 +252,7 @@ export class GameScene extends Phaser.Scene {
|
||||||
hitEnemy(bullet, enemy) {
|
hitEnemy(bullet, enemy) {
|
||||||
bullet.destroy();
|
bullet.destroy();
|
||||||
enemy.destroy();
|
enemy.destroy();
|
||||||
|
this.sound.play('enemy-kill');
|
||||||
|
|
||||||
this.score += 10;
|
this.score += 10;
|
||||||
this.scoreText.setText('Score: ' + this.score);
|
this.scoreText.setText('Score: ' + this.score);
|
||||||
|
|
@ -164,8 +265,8 @@ export class GameScene extends Phaser.Scene {
|
||||||
this.gameOver = true;
|
this.gameOver = true;
|
||||||
|
|
||||||
// Show game over text
|
// Show game over text
|
||||||
const gameOverText = this.add.text(400, 300, 'GAME OVER', {
|
const gameOverText = this.add.text(300, 400, 'GAME OVER', {
|
||||||
fontSize: '64px',
|
fontSize: '54px',
|
||||||
fill: '#ff3366',
|
fill: '#ff3366',
|
||||||
fontFamily: 'Arial'
|
fontFamily: 'Arial'
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue