Compare commits

...

2 Commits

Author SHA1 Message Date
Brian Fertig eb01c3d89c ```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.
```
2025-08-06 15:20:55 -06:00
Brian Fertig 7b56d5b46a feat: Implement multi-layer parallax backgrounds, font loading, and improved game flow
- Added multiple background layers (bg-layer2, bg-layer3) with parallax scrolling effect
- Implemented proper background positioning and continuous scrolling logic
- Integrated custom 'Space' font for UI elements and game over text
- Updated menu scene integration and game flow with fade-out transition to menu
- Enhanced visual assets including logo display and improved background layer management
2025-08-06 14:19:10 -06:00
24 changed files with 566 additions and 58 deletions

BIN
assets/ClassCoder.ttf Normal file

Binary file not shown.

BIN
assets/Vector.ttf Normal file

Binary file not shown.

BIN
assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 KiB

BIN
assets/menu-alien.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 KiB

BIN
assets/menu-hunter.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 KiB

BIN
assets/menu-ship.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 MiB

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 679 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
assets/sounds/thruster.mp3 Normal file

Binary file not shown.

BIN
assets/space-age.otf Normal file

Binary file not shown.

View File

@ -30,12 +30,6 @@
<script src="phaser.min.js"></script>
<!-- Import game modules using ES6 modules -->
<script type="module">
import { config } from './src/config.js';
import { GameScene } from './src/scenes/GameScene.js';
const game = new Phaser.Game(config);
game.scene.add('GameScene', GameScene, true);
</script>
<script type="module" src="./src/config.js"></script>
</body>
</html>

BIN
raw/class-coder.zip Normal file

Binary file not shown.

BIN
raw/saucer.zip Normal file

Binary file not shown.

BIN
raw/space-age.zip Normal file

Binary file not shown.

BIN
raw/vector.zip 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

@ -1,3 +1,6 @@
import { MenuScene } from './scenes/MenuScene.js';
import { GameScene } from './scenes/GameScene.js';
export const config = {
type: Phaser.AUTO,
width: 600,
@ -10,13 +13,14 @@ export const config = {
debug: false
}
},
scene: {
preload: function() {},
create: function() {},
update: function() {}
},
scene: [
MenuScene,
GameScene,
],
scale: {
mode: Phaser.Scale.FIT,
autoCenter: Phaser.Scale.CENTER_BOTH
}
};
const game = new Phaser.Game(config);

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() {
@ -33,8 +52,9 @@ export class GameScene extends Phaser.Scene {
// 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');
this.load.image('bg-layer2', 'assets/neon-city-dark-clouds2.png');
this.load.image('bg-layer3', 'assets/neon-city-dark-clouds.png');
this.load.image('logo', 'assets/logo.png');
// Load Sprites
this.load.spritesheet('player-sprite', 'assets/player-sprite.png', {
@ -54,6 +74,10 @@ 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');
// Create simple placeholder graphics for now
this.createPlaceholderGraphics();
@ -102,13 +126,13 @@ export class GameScene extends Phaser.Scene {
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);
this.bgLayer2 = this.add.image(280, 30, 'bg-layer2').setOrigin(0.5);
this.bgLayer3 = this.add.image(280, 30, '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;
this.bgSpeed2 = 0.5;
this.bgSpeed3 = 0.8;
}
update(time, delta) {
@ -120,40 +144,71 @@ 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.x -= this.bgSpeed2;
//this.bgLayer3.x -= 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) {
this.bgLayer1.y = 30; // Width of game + layer width
this.bgLayer1.y = 0;
}
if (this.bgLayer2.y > 1536) {
this.bgLayer2.y = 0;
}
if (this.bgLayer3.y > 1536) {
this.bgLayer3.y = 0;
}
}
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
}
}
// if (this.bgLayer2.x < -400) {
// this.bgLayer2.x = 1200;
// }
// if (this.bgLayer3.x < -400) {
// this.bgLayer3.x = 1200;
// }
}
setupMouseInput() {
@ -229,17 +284,18 @@ export class GameScene extends Phaser.Scene {
this.scoreText = this.add.text(16, 16, 'Score: 0', {
fontSize: '28px',
fill: '#ffffff',
fontFamily: 'Arial'
fontFamily: 'Space, Arial'
});
// Create game title
this.titleText = this.add.text(300, 50, 'Zenith Vector', {
fontSize: '40px',
fill: '#00ffcc',
fontFamily: 'Arial',
align: 'center'
});
this.titleText.setOrigin(0.5);
// this.titleText = this.add.text(300, 50, 'Zenith Vector', {
// fontSize: '40px',
// fill: '#00ffcc',
// fontFamily: 'Arial',
// align: 'center'
// });
// this.titleText.setOrigin(0.5);
this.titleText = this.add.image(500, 50, 'logo').setScale(.3).setOrigin(0.5).postFX.addGlow();
}
shoot(pointer) {
@ -365,14 +421,30 @@ export class GameScene extends Phaser.Scene {
const gameOverText = this.add.text(300, 400, 'GAME OVER', {
fontSize: '54px',
fill: '#ff3366',
fontFamily: 'Arial'
fontFamily: 'Space, Arial'
});
gameOverText.setOrigin(0.5);
// Restart game after delay
// this.time.delayedCall(2000, () => {
// this.scene.restart();
// });
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
this.tweens.add({
targets: this.cameras.main,
alpha: 0, // Fade to transparent
duration: 1000, // 1 second fade
ease: 'Linear',
onComplete: () => {
this.bgMusic.stop();
this.scene.start('MenuScene');
}
});
});
}
startWave(waveNumber) {
@ -383,7 +455,7 @@ export class GameScene extends Phaser.Scene {
const waveText = this.add.text(300, 150, `WAVE ${waveNumber + 1}`, {
fontSize: '48px',
fill: '#ffff00',
fontFamily: 'Arial'
fontFamily: 'Space, Arial'
});
waveText.setOrigin(0.5);

299
src/scenes/MenuScene.js Normal file
View File

@ -0,0 +1,299 @@
export class MenuScene extends Phaser.Scene {
constructor() {
super('MenuScene');
}
preload() {
this.load.image('menu-logo','assets/logo.png');
this.load.image('menu-ship','assets/menu-ship.png');
this.load.image('menu-hunter','assets/menu-hunter.png');
this.load.image('menu-alien','assets/menu-alien.png');
this.load.image('reticle', 'assets/reticle.png');
this.load.audio('menu-bgMusic', 'assets/music/NeonVelocity.mp3');
// Load custom font
this.load.font('Space', 'assets/space-age.otf');
this.load.font('coder', 'assets/ClassCoder.ttf');
}
create() {
// Create menu background and title
this.add.rectangle(300, 400, 600, 800, 0x111111);
// Create starfield effect
this.createStarfield();
const logo = this.add.image(300, 250, 'menu-logo').setOrigin(0.5).setScale(0.5).setAlpha(0.7);
const menuHunter = this.add.image(450, 700, 'menu-hunter').setScale(0.6).setOrigin(0.5).setAlpha(0);
const menuShip = this.add.image(900, -300, 'menu-ship').setOrigin(0.5).setScale(.6);
const menuAlien = this.add.image(150, 700, 'menu-alien').setScale(0.6).setOrigin(0.5).setAlpha(0);
this.tweens.add({
targets: menuShip,
x: 500,
y: 100,
duration: 2000,
ease: 'Cubic',
delay: 3000
});
this.tweens.add({
targets: logo,
alpha: 1,
scale: 1,
duration: 5000,
ease: 'Bounce',
});
this.tweens.add({
targets: [menuHunter, menuAlien],
alpha: 1,
duration: 3000,
delay: 4000,
ease: 'Bounce'
})
// Controls display with typing animation
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',
fill: '#00ff00'
}).setOrigin(0.5);
// Typing animation
this.time.delayedCall(1000, () => {
let i = 0;
const typingTimer = this.time.addEvent({
delay: 50, // Characters per second
callback: () => {
if (i < controlsText.length) {
this.controlsText.text += controlsText.charAt(i);
i++;
} else {
typingTimer.remove();
}
},
callbackScope: this,
loop: true
});
});
// Menu button
this.startButton = this.add.text(300, 550, ' START GAME ', {
fontFamily: 'Space, Arial',
fontSize: '48px',
fill: '#fff'
}).setOrigin(0.5);
// Add color cycling effect for synthwave colors
const synthwaveColors = ['#ffffff', '#ff00ff', '#00ffff', '#ffff00', '#ff00cc'];
let colorIndex = 0;
this.time.addEvent({
delay: 1500,
callback: () => {
colorIndex = (colorIndex + 1) % synthwaveColors.length;
this.startButton.setFill(synthwaveColors[colorIndex]);
},
loop: true
});
this.tweens.add({
targets: this.startButton,
scale: 1.2,
duration: 1500,
repeat: -1,
ease: 'Sine',
yoyo: true,
});
this.startButton.setInteractive();
this.startButton.on('pointerdown', () => {
// Start the actual game scene
this.bgMusic.stop();
this.scene.start('GameScene');
});
// Play background music
this.bgMusic = this.sound.add('menu-bgMusic', { loop: true });
this.bgMusic.play();
// Setup mouse input for shooting and reticle
this.setupMouseInput();
// Add scanline effect (narrow and moving)
this.scanlines = this.add.graphics({ x: 0, y: 0 });
this.scanlines.fillStyle(0x00ff00, 0.15); // Green scanlines with moderate opacity
// Create narrow horizontal lines that move vertically
for (let i = 0; i < 20; i++) {
const y = (i * 40) % 800;
this.scanlines.fillRect(0, y, 600, 2); // Narrow lines (2px height)
}
// Add VHS artifacts
this.vhsArtifacts = this.add.graphics({ x: 0, y: 0 });
this.vhsArtifacts.fillStyle(0xff00ff, 0.1); // Magenta artifacts
this.vhsArtifacts.fillRect(0, 0, 600, 800);
// Add VHS artifacts 2
this.vhsArtifacts2 = this.add.graphics({ x: 0, y: 0 });
this.vhsArtifacts2.fillStyle(0xff00ff, 0.1); // Magenta artifacts
this.vhsArtifacts2.fillRect(0, 0, 600, 800);
this.tweens.add({
targets: this.vhsArtifacts2,
alpha: 0.1,
duration: 10000,
yoyo: true,
repeat: -1
});
// Start artifact effects
this.artifactTimer = this.time.addEvent({
delay: 200,
callback: this.updateVHSArtifacts,
callbackScope: this,
loop: true
});
}
createStarfield() {
// Create a graphics object for the starfield
this.starfield = this.add.graphics({ x: 0, y: 0 });
// Star configuration
this.stars = [];
const starCount = 200;
// Initialize stars with random positions and z-depths
for (let i = 0; i < starCount; i++) {
const star = {
x: Math.random() * 600,
y: Math.random() * 800,
z: Math.random() * 10 + 1, // Depth from 1 to 11
size: Math.random() * 2 + 1,
speed: Math.random() * 0.5 + 0.2 // Significantly reduced speed
};
// Set color based on depth (closer stars are brighter)
const brightness = Math.max(0.5, 1 - star.z / 15); // Closer stars brighter
const grayValue = Math.floor(150 + brightness * 105);
star.color = (grayValue << 16) | (grayValue << 8) | grayValue;
this.stars.push(star);
}
// Change direction timer - increased to make it more subtle
this.directionChangeTimer = this.time.addEvent({
delay: 5000, // Increase delay to 5 seconds
callback: this.changeStarDirection,
callbackScope: this,
loop: true
});
}
update() {
// Update star positions
if (this.starfield && this.stars) {
this.starfield.clear();
for (let i = 0; i < this.stars.length; i++) {
const star = this.stars[i];
// Move stars toward camera (decrease z) - greatly reduced speed
star.z -= star.speed * 0.02;
// Reset stars that move too close to camera
if (star.z <= 0) {
star.z = Math.random() * 10 + 1; // Reset with new depth
star.x = Math.random() * 600; // Random x position
star.y = Math.random() * 800; // Random y position
// Recalculate color based on new depth
const brightness = Math.max(0.5, 1 - star.z / 15);
const grayValue = Math.floor(150 + brightness * 105);
star.color = (grayValue << 16) | (grayValue << 8) | grayValue;
}
// Apply perspective projection to create parallax effect
const scale = 20 / (star.z + 20); // Scale based on depth
const screenX = star.x * scale;
const screenY = star.y * scale;
// Draw the star with its color and scaled size
this.starfield.fillStyle(star.color);
this.starfield.fillCircle(screenX, screenY, star.size * scale);
}
}
// Update scanline position
if (this.scanlines) {
this.scanlines.y = (this.scanlines.y + 0.5) % 800;
}
}
updateVHSArtifacts() {
// Randomly update VHS artifacts
if (this.vhsArtifacts && Math.random() > 0.7) {
// Clear previous artifacts but keep some for flickering effect
this.vhsArtifacts.clear();
// Add random color shifts and distortions
for (let i = 0; i < 5; i++) {
const x = Math.random() * 600;
const y = Math.random() * 800;
const width = Math.random() * 10 + 1;
const height = Math.random() * 20 + 5;
// Random color (neon colors for synthwave)
const colors = [0xff00ff, 0x00ffff, 0xffff00];
const color = Phaser.Display.Color.GetColor32(
...colors[Math.floor(Math.random() * colors.length)].toString(16).split('').map(c => parseInt(c, 16) * 17)
);
// Use higher opacity for flickering effect (instead of very low opacity)
this.vhsArtifacts.fillStyle(color, Math.random() * 0.3 + 0.1); // Increased from 0.05 to 0.3-0.1 range
this.vhsArtifacts.fillRect(x, y, width, height);
}
}
}
changeStarDirection() {
// Change direction of all stars (they'll move toward camera)
for (let i = 0; i < this.stars.length; i++) {
const star = this.stars[i];
// Randomly adjust speed and depth - much slower changes
star.speed = Math.random() * 1 + 0.5; // Reduced speed range
star.z += (Math.random() * 0.5 - 0.25); // Very subtle z position change
// Keep stars within reasonable bounds
if (star.z < 0.5) {
star.z = 0.5;
} else if (star.z > 15) {
star.z = 15;
}
// Recalculate color based on new depth and speed
const brightness = Math.max(0.5, 1 - star.z / 15);
const grayValue = Math.floor(150 + brightness * 105);
star.color = (grayValue << 16) | (grayValue << 8) | grayValue;
}
}
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;
});
}
}