```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 {
|
||||
position: relative;
|
||||
cursor: none;
|
||||
}
|
||||
</style>
|
||||
</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 = {
|
||||
type: Phaser.AUTO,
|
||||
width: 800,
|
||||
height: 600,
|
||||
width: 600,
|
||||
height: 800,
|
||||
parent: 'game-container',
|
||||
physics: {
|
||||
default: 'arcade',
|
||||
|
|
@ -14,5 +14,9 @@ export const config = {
|
|||
preload: function() {},
|
||||
create: 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 {
|
||||
constructor() {
|
||||
super({ key: 'GameScene' });
|
||||
|
|
@ -6,17 +8,38 @@ export class GameScene extends Phaser.Scene {
|
|||
this.player = null;
|
||||
this.enemies = null;
|
||||
this.bullets = null;
|
||||
this.cursors = null;
|
||||
|
||||
// Game state
|
||||
this.score = 0;
|
||||
this.gameOver = false;
|
||||
|
||||
// Mouse reticle
|
||||
this.reticle = null;
|
||||
}
|
||||
|
||||
preload() {
|
||||
// Load assets here (will be implemented in a later step)
|
||||
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
|
||||
this.createPlaceholderGraphics();
|
||||
}
|
||||
|
|
@ -25,20 +48,20 @@ export class GameScene extends Phaser.Scene {
|
|||
// Set background color
|
||||
this.cameras.main.setBackgroundColor('#0a0a2a');
|
||||
|
||||
// Create player ship
|
||||
this.player = this.physics.add.sprite(400, 500, 'player');
|
||||
this.player.setCollideWorldBounds(true);
|
||||
// Create parallax backgrounds
|
||||
this.createBackgrounds();
|
||||
|
||||
// Create player instance
|
||||
this.player = new Player(this);
|
||||
const { aKey, dKey } = this.player.create();
|
||||
|
||||
// Create groups for bullets and enemies
|
||||
this.bullets = this.physics.add.group();
|
||||
this.enemies = this.physics.add.group();
|
||||
|
||||
// Setup controls
|
||||
this.cursors = this.input.keyboard.createCursorKeys();
|
||||
|
||||
// Setup collision detection
|
||||
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
|
||||
this.time.addEvent({
|
||||
|
|
@ -50,24 +73,82 @@ export class GameScene extends Phaser.Scene {
|
|||
|
||||
// Create UI elements
|
||||
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() {
|
||||
if (this.gameOver) return;
|
||||
|
||||
// Player movement
|
||||
if (this.cursors.left.isDown) {
|
||||
this.player.setVelocityX(-200);
|
||||
} else if (this.cursors.right.isDown) {
|
||||
this.player.setVelocityX(200);
|
||||
} else {
|
||||
this.player.setVelocityX(0);
|
||||
}
|
||||
// Update background positions for parallax effect
|
||||
this.updateBackgrounds();
|
||||
|
||||
// Shooting
|
||||
if (Phaser.Input.Keyboard.JustDown(this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE))) {
|
||||
this.shoot();
|
||||
// Get A/D keys for player movement
|
||||
const aKey = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.A);
|
||||
const dKey = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.D);
|
||||
|
||||
// Update player with A/D controls
|
||||
this.player.update(aKey, dKey);
|
||||
}
|
||||
|
||||
updateBackgrounds() {
|
||||
// Move background layers at different speeds
|
||||
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() {
|
||||
|
|
@ -76,17 +157,17 @@ export class GameScene extends Phaser.Scene {
|
|||
canvas.width = 32;
|
||||
canvas.height = 32;
|
||||
|
||||
// Player ship (triangle)
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.fillStyle = '#00ffcc';
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(16, 5);
|
||||
ctx.lineTo(5, 30);
|
||||
ctx.lineTo(27, 30);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
// // Player ship (triangle)
|
||||
// const ctx = canvas.getContext('2d');
|
||||
// ctx.fillStyle = '#00ffcc';
|
||||
// ctx.beginPath();
|
||||
// ctx.moveTo(16, 5);
|
||||
// ctx.lineTo(5, 30);
|
||||
// ctx.lineTo(27, 30);
|
||||
// ctx.closePath();
|
||||
// ctx.fill();
|
||||
|
||||
this.textures.addCanvas('player', canvas);
|
||||
// this.textures.addCanvas('player', canvas);
|
||||
|
||||
// Enemy (square)
|
||||
const enemyCanvas = document.createElement('canvas');
|
||||
|
|
@ -116,14 +197,14 @@ export class GameScene extends Phaser.Scene {
|
|||
createUI() {
|
||||
// Create score text
|
||||
this.scoreText = this.add.text(16, 16, 'Score: 0', {
|
||||
fontSize: '32px',
|
||||
fontSize: '28px',
|
||||
fill: '#ffffff',
|
||||
fontFamily: 'Arial'
|
||||
});
|
||||
|
||||
// Create game title
|
||||
this.titleText = this.add.text(400, 50, 'Zenith Vector', {
|
||||
fontSize: '48px',
|
||||
this.titleText = this.add.text(300, 50, 'Zenith Vector', {
|
||||
fontSize: '40px',
|
||||
fill: '#00ffcc',
|
||||
fontFamily: 'Arial',
|
||||
align: 'center'
|
||||
|
|
@ -131,13 +212,32 @@ export class GameScene extends Phaser.Scene {
|
|||
this.titleText.setOrigin(0.5);
|
||||
}
|
||||
|
||||
shoot() {
|
||||
const bullet = this.bullets.create(this.player.x, this.player.y - 20, 'bullet');
|
||||
bullet.setVelocityY(-300);
|
||||
shoot(pointer) {
|
||||
// Calculate angle between player and mouse
|
||||
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);
|
||||
|
||||
// Sound Effects
|
||||
this.sound.play('main-gun');
|
||||
}
|
||||
|
||||
spawnEnemy() {
|
||||
// Spawn enemies at random x position within game width (800px)
|
||||
const x = Phaser.Math.Between(20, 780);
|
||||
const enemy = this.enemies.create(x, 0, 'enemy');
|
||||
|
||||
|
|
@ -152,6 +252,7 @@ export class GameScene extends Phaser.Scene {
|
|||
hitEnemy(bullet, enemy) {
|
||||
bullet.destroy();
|
||||
enemy.destroy();
|
||||
this.sound.play('enemy-kill');
|
||||
|
||||
this.score += 10;
|
||||
this.scoreText.setText('Score: ' + this.score);
|
||||
|
|
@ -164,8 +265,8 @@ export class GameScene extends Phaser.Scene {
|
|||
this.gameOver = true;
|
||||
|
||||
// Show game over text
|
||||
const gameOverText = this.add.text(400, 300, 'GAME OVER', {
|
||||
fontSize: '64px',
|
||||
const gameOverText = this.add.text(300, 400, 'GAME OVER', {
|
||||
fontSize: '54px',
|
||||
fill: '#ff3366',
|
||||
fontFamily: 'Arial'
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue