Compare commits

..

No commits in common. "eb01c3d89cb65348426f6a1391412ddf931a64ed" and "1af359e09f3e3f59aea4db8b8d433aa23086aeea" have entirely different histories.

24 changed files with 58 additions and 566 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 242 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 288 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

After

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 679 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Binary file not shown.

View File

@ -30,6 +30,12 @@
<script src="phaser.min.js"></script>
<!-- Import game modules using ES6 modules -->
<script type="module" src="./src/config.js"></script>
<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>
</body>
</html>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -3,9 +3,6 @@ export class Player {
this.scene = scene;
this.sprite = null;
this.cursors = null;
this.thruster = null;
this.trailParticles = [];
this.trailCounter = 0;
}
create() {
@ -16,53 +13,15 @@ export class Player {
// Setup controls
this.cursors = this.scene.input.keyboard.createCursorKeys();
// Replace cursor keys with A/D/W/S keys for movement
// 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);
const wKey = this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.W);
const sKey = this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.S);
// Create thruster effect
this.createThruster();
return { aKey, dKey, wKey, sKey };
return { aKey, dKey };
}
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
update(aKey, dKey) {
// Player movement with A/D keys
if (aKey.isDown) {
this.sprite.setVelocityX(-200);
this.sprite.setFrame(1);
@ -73,104 +32,6 @@ 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,6 +1,3 @@
import { MenuScene } from './scenes/MenuScene.js';
import { GameScene } from './scenes/GameScene.js';
export const config = {
type: Phaser.AUTO,
width: 600,
@ -13,14 +10,13 @@ export const config = {
debug: false
}
},
scene: [
MenuScene,
GameScene,
],
scene: {
preload: function() {},
create: function() {},
update: function() {}
},
scale: {
mode: Phaser.Scale.FIT,
autoCenter: Phaser.Scale.CENTER_BOTH
}
};
const game = new Phaser.Game(config);
};

View File

@ -22,25 +22,6 @@ 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() {
@ -52,9 +33,8 @@ 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/neon-city-dark-clouds2.png');
this.load.image('bg-layer3', 'assets/neon-city-dark-clouds.png');
this.load.image('logo', 'assets/logo.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', {
@ -74,10 +54,6 @@ 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();
@ -126,13 +102,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(280, 30, 'bg-layer2').setOrigin(0.5);
this.bgLayer3 = this.add.image(280, 30, 'bg-layer3').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;
//this.bgSpeed2 = 0.5;
//this.bgSpeed3 = 0.8;
}
update(time, delta) {
@ -144,71 +120,40 @@ export class GameScene extends Phaser.Scene {
// Update wave timer
this.waveTimer += delta;
// Check for wave changes using the configuration array
this.checkWaveChanges();
// 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);
}
// Get keyboard keys for player movement
// 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);
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 movement controls
this.player.update(aKey, dKey, wKey, sKey);
// Update player with A/D controls
this.player.update(aKey, dKey);
// Check if enemies are off-screen and destroy them
this.checkEnemiesOffScreen();
}
updateBackgrounds() {
// 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;
// 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
if (this.bgLayer1.y > 736) {
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
}
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() {
@ -284,18 +229,17 @@ export class GameScene extends Phaser.Scene {
this.scoreText = this.add.text(16, 16, 'Score: 0', {
fontSize: '28px',
fill: '#ffffff',
fontFamily: 'Space, Arial'
fontFamily: '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.image(500, 50, 'logo').setScale(.3).setOrigin(0.5).postFX.addGlow();
this.titleText = this.add.text(300, 50, 'Zenith Vector', {
fontSize: '40px',
fill: '#00ffcc',
fontFamily: 'Arial',
align: 'center'
});
this.titleText.setOrigin(0.5);
}
shoot(pointer) {
@ -421,30 +365,14 @@ export class GameScene extends Phaser.Scene {
const gameOverText = this.add.text(300, 400, 'GAME OVER', {
fontSize: '54px',
fill: '#ff3366',
fontFamily: 'Space, Arial'
fontFamily: 'Arial'
});
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
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');
}
});
});
// Restart game after delay
// this.time.delayedCall(2000, () => {
// this.scene.restart();
// });
}
startWave(waveNumber) {
@ -453,9 +381,9 @@ export class GameScene extends Phaser.Scene {
// Add wave indicator text
const waveText = this.add.text(300, 150, `WAVE ${waveNumber + 1}`, {
fontSize: '48px',
fontSize: '48px',
fill: '#ffff00',
fontFamily: 'Space, Arial'
fontFamily: 'Arial'
});
waveText.setOrigin(0.5);

View File

@ -1,299 +0,0 @@
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;
});
}
}