```git commit message

Add vertical movement controls (W/S keys), thruster effects with particle trails, dynamic background scrolling speed based on player position, and improved wave system with configurable waves

This commit introduces several key gameplay enhancements:
- Implemented W/S keyboard controls for vertical movement with boundary limits
- Added thruster visual effects with flickering animation and sound when moving upward
- Created particle trail effects that follow the player during thrust
- Implemented dynamic background scrolling speed that increases as player moves upward
- Enhanced wave system with configurable wave settings for easier management and extension
- Updated menu controls display to reflect new W/A/S/D movement scheme

The changes improve player mobility, visual feedback, and game progression system while maintaining existing functionality.
```
This commit is contained in:
Brian Fertig 2025-08-06 15:20:55 -06:00
parent 7b56d5b46a
commit eb01c3d89c
4 changed files with 215 additions and 19 deletions

BIN
assets/sounds/thruster.mp3 Normal file

Binary file not shown.

View File

@ -3,6 +3,9 @@ export class Player {
this.scene = scene;
this.sprite = null;
this.cursors = null;
this.thruster = null;
this.trailParticles = [];
this.trailCounter = 0;
}
create() {
@ -13,15 +16,53 @@ export class Player {
// Setup controls
this.cursors = this.scene.input.keyboard.createCursorKeys();
// Replace cursor keys with A/D keys for movement
// Replace cursor keys with A/D/W/S 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);
const wKey = this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.W);
const sKey = this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.S);
return { aKey, dKey };
// Create thruster effect
this.createThruster();
return { aKey, dKey, wKey, sKey };
}
update(aKey, dKey) {
// Player movement with A/D keys
createThruster() {
// Create a simple thruster graphic using canvas
const canvas = document.createElement('canvas');
canvas.width = 32;
canvas.height = 32;
const ctx = canvas.getContext('2d');
// Draw thruster flame effect (yellow/orange gradient)
const gradient = ctx.createRadialGradient(16, 16, 0, 16, 16, 16);
gradient.addColorStop(0, '#ffff00');
gradient.addColorStop(0.5, '#ff9900');
gradient.addColorStop(1, '#ff3300');
ctx.beginPath();
ctx.arc(16, 16, 12, 0, Math.PI * 2);
ctx.fillStyle = gradient;
ctx.fill();
// Add some flickering effect with a second inner circle
ctx.beginPath();
ctx.arc(16, 16, 8, 0, Math.PI * 2);
ctx.fillStyle = '#ffffff';
ctx.fill();
this.scene.textures.addCanvas('thruster', canvas);
// Create thruster sprite (initially hidden)
this.thruster = this.scene.add.sprite(0, 0, 'thruster');
this.thruster.setOrigin(0.5);
this.thruster.setVisible(false);
}
update(aKey, dKey, wKey, sKey) {
// Player movement with A/D keys for horizontal
if (aKey.isDown) {
this.sprite.setVelocityX(-200);
this.sprite.setFrame(1);
@ -32,6 +73,104 @@ export class Player {
this.sprite.setVelocityX(0);
this.sprite.setFrame(0);
}
// Player movement with W/S keys for vertical
if (wKey.isDown) {
// Limit upward movement to 200px from bottom of screen (500px y position)
if (this.sprite.y > 500) {
this.sprite.setVelocityY(-200);
} else {
this.sprite.setVelocityY(0);
}
} else if (sKey.isDown) {
// Limit downward movement to 700px from top of screen
if (this.sprite.y < 700) {
this.sprite.setVelocityY(200);
} else {
this.sprite.setVelocityY(0);
}
} else {
this.sprite.setVelocityY(0);
}
// Handle thruster effect when player is accelerating upward
if (wKey.isDown) {
// Show and position the thruster
if (!this.thruster.visible) {
this.thruster.setVisible(true);
// Play sound and store the instance
this.thrusterSoundInstance = this.scene.sound.add('thruster');
this.thrusterSoundInstance.play();
}
// Position the thruster behind the player (below the ship)
this.thruster.x = this.sprite.x;
this.thruster.y = this.sprite.y + 25; // Position below player
// Add some flickering animation effect
const scale = 0.8 + Math.sin(this.scene.time.now * 0.01) * 0.2;
this.thruster.setScale(scale);
// Create trail particles when moving upward
if (this.sprite.y > 500) {
this.createTrail();
}
} else {
// Hide the thruster when W key is not pressed
if (this.thruster.visible) {
this.thruster.setVisible(false);
// Stop the thruster sound when thrusting stops
if (this.thrusterSoundInstance) {
this.thrusterSoundInstance.stop();
this.thrusterSoundInstance = null;
}
}
}
// Update trail particles
this.updateTrails();
}
createTrail() {
// Only create a new trail particle every few frames to avoid too many particles
this.trailCounter++;
if (this.trailCounter < 3) return;
this.trailCounter = 0;
// Create a trail particle at player position
const trailParticle = this.scene.add.sprite(this.sprite.x, this.sprite.y + 30, 'thruster');
trailParticle.setOrigin(0.5);
// Make it smaller and more transparent for the trail effect
trailParticle.setScale(0.4);
trailParticle.setAlpha(0.7);
// Store particle with its fade speed
this.trailParticles.push({
sprite: trailParticle,
alphaSpeed: 0.01,
scaleSpeed: -0.005
});
}
updateTrails() {
// Update and remove old trail particles
for (let i = this.trailParticles.length - 1; i >= 0; i--) {
const particle = this.trailParticles[i];
// Gradually fade out the particle
particle.sprite.setAlpha(particle.sprite.alpha - particle.alphaSpeed);
// Gradually shrink the particle
particle.sprite.setScale(particle.sprite.scale + particle.scaleSpeed);
// Remove particles that are fully faded or too small
if (particle.sprite.alpha <= 0 || particle.sprite.scale <= 0) {
particle.sprite.destroy();
this.trailParticles.splice(i, 1);
}
}
}
shoot() {

View File

@ -22,6 +22,25 @@ export class GameScene extends Phaser.Scene {
// Wave system
this.waveTimer = 0;
this.currentWave = 0;
// Wave configuration - easier to manage and extend
this.waveConfig = [
{
waveNumber: 0,
time: 0,
enemyTypes: [0] // Only basic enemies in wave 1
},
{
waveNumber: 1,
time: 20000,
enemyTypes: [1] // Enemies that can shoot in wave 2
},
{
waveNumber: 2,
time: 40000,
enemyTypes: [2] // Special enemies in wave 3
}
];
}
preload() {
@ -55,6 +74,7 @@ export class GameScene extends Phaser.Scene {
this.load.audio('player-death', 'assets/sounds/player-death.mp3');
this.load.audio('enemy-shoot', 'assets/sounds/enemy-shoot.mp3');
this.load.audio('next-wave', 'assets/sounds/next-wave.mp3');
this.load.audio('thruster', 'assets/sounds/thruster.mp3');
// Load Fonts
this.load.font('Space', 'assets/space-age.otf');
@ -124,29 +144,47 @@ export class GameScene extends Phaser.Scene {
// Update wave timer
this.waveTimer += delta;
// Check for wave changes every 30 seconds (30,000 milliseconds)
if (this.waveTimer >= 30000 && this.currentWave === 0) {
this.startWave(1);
} else if (this.waveTimer >= 60000 && this.currentWave === 1) {
this.startWave(2);
}
// Check for wave changes using the configuration array
this.checkWaveChanges();
// Get A/D keys for player movement
// Get keyboard 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);
const wKey = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.W);
const sKey = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.S);
// Update player with A/D controls
this.player.update(aKey, dKey);
// Update player with movement controls
this.player.update(aKey, dKey, wKey, sKey);
// Check if enemies are off-screen and destroy them
this.checkEnemiesOffScreen();
}
updateBackgrounds() {
// Move background layers at different speeds
this.bgLayer1.y += this.bgSpeed1;
this.bgLayer2.y += this.bgSpeed2;
this.bgLayer3.y += this.bgSpeed3;
// Calculate speed multiplier based on player position
// When player is at 700px (bottom), use normal speed
// When player is at 500px (top), move faster (2x)
const maxSpeed = 5.0; // Maximum speed multiplier
const minSpeed = 1.0; // Minimum speed multiplier
let speedMultiplier = minSpeed;
if (this.player && this.player.sprite) {
const playerY = this.player.sprite.y;
// Map Y position to speed multiplier (700px = normal, 500px = max)
if (playerY < 700) {
speedMultiplier = minSpeed + ((700 - playerY) / (700 - 500)) * (maxSpeed - minSpeed);
}
// Ensure we don't go below minimum
speedMultiplier = Math.max(minSpeed, speedMultiplier);
}
// Move background layers at different speeds with the calculated multiplier
this.bgLayer1.y += this.bgSpeed1 * speedMultiplier;
this.bgLayer2.y += this.bgSpeed2 * speedMultiplier;
this.bgLayer3.y += this.bgSpeed3 * speedMultiplier;
// Reset positions to create continuous scrolling effect
if (this.bgLayer1.y > 736) {
@ -160,6 +198,19 @@ export class GameScene extends Phaser.Scene {
}
}
checkWaveChanges() {
// Iterate through wave configurations to find which wave should start
for (let i = 0; i < this.waveConfig.length; i++) {
const wave = this.waveConfig[i];
// If we haven't started this wave yet and the timer has passed
if (this.waveTimer >= wave.time && this.currentWave === wave.waveNumber - 1) {
this.startWave(wave.waveNumber);
break; // Only start one wave per update cycle
}
}
}
setupMouseInput() {
// Create mouse reticle as sprite (replacing the circle)
this.reticle = this.add.sprite(0, 0, 'reticle');
@ -244,7 +295,7 @@ export class GameScene extends Phaser.Scene {
// align: 'center'
// });
// this.titleText.setOrigin(0.5);
this.titleText = this.add.image(300, 50, 'logo').setScale(.1).setOrigin(0.5);
this.titleText = this.add.image(500, 50, 'logo').setScale(.3).setOrigin(0.5).postFX.addGlow();
}
shoot(pointer) {
@ -374,6 +425,12 @@ export class GameScene extends Phaser.Scene {
});
gameOverText.setOrigin(0.5);
this.tweens.add({
targets: this.bgMusic,
volume: 0,
duration: 5000,
});
// Return to menu after delay
this.time.delayedCall(5000, () => {
// Fade camera out before returning to menu

View File

@ -54,7 +54,7 @@ export class MenuScene extends Phaser.Scene {
})
// Controls display with typing animation
const controlsText = 'Controls: A and D to go Left and Right. Mouse to Aim and Fire';
const controlsText = 'Controls: W,A,S,D to Move. Mouse to Aim and Fire';
this.controlsText = this.add.text(300, 500, '', {
fontFamily: 'Coder, Arial',
fontSize: '16px',