/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Asteroid
var Asteroid = Container.expand(function () {
var self = Container.call(this);
var rock = self.attachAsset('asteroid', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = rock.width;
self.height = rock.height;
self.speed = 10 + Math.random() * 6;
self.rotationSpeed = (Math.random() - 0.5) * 0.1;
self.update = function () {
self.y += self.speed;
rock.rotation += self.rotationSpeed;
};
return self;
});
// Enemy Bullet
var EnemyBullet = Container.expand(function () {
var self = Container.call(this);
var bullet = self.attachAsset('enemyBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = bullet.width;
self.height = bullet.height;
self.speed = 22;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Enemy Ship
var EnemyShip = Container.expand(function () {
var self = Container.call(this);
var ship = self.attachAsset('enemyShip', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = ship.width;
self.height = ship.height;
self.speed = 7 + Math.random() * 4;
self.shootCooldown = 60 + Math.floor(Math.random() * 60);
self.update = function () {
self.y += self.speed;
if (self.shootCooldown > 0) {
self.shootCooldown--;
} else {
self.shoot();
self.shootCooldown = 90 + Math.floor(Math.random() * 60);
}
};
self.shoot = function () {
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y + self.height / 2 + bullet.height / 2;
enemyBullets.push(bullet);
game.addChild(bullet);
};
return self;
});
// HeartDrop (falling heart item)
var HeartDrop = Container.expand(function () {
var self = Container.call(this);
var heart = self.attachAsset('heart', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = heart.width;
self.height = heart.height;
self.speed = 10;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Player Bullet
var PlayerBullet = Container.expand(function () {
var self = Container.call(this);
var bullet = self.attachAsset('playerBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = bullet.width;
self.height = bullet.height;
self.speed = -36;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Player Ship
var PlayerShip = Container.expand(function () {
var self = Container.call(this);
var ship = self.attachAsset('playerShip', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = ship.width;
self.height = ship.height;
self.shootCooldown = 0;
self.invincible = false;
self.invincibleTicks = 0;
self.update = function () {
if (self.shootCooldown > 0) self.shootCooldown--;
if (self.invincible) {
self.invincibleTicks--;
if (self.invincibleTicks <= 0) {
self.invincible = false;
ship.alpha = 1;
} else {
// Flicker effect
ship.alpha = LK.ticks % 10 < 5 ? 0.4 : 1;
}
}
};
// Shoot method
self.shoot = function () {
if (self.shootCooldown <= 0) {
// Shoot 6 bullets, evenly spread (performance optimized)
var bulletCount = 6;
var spread = 120; // total spread in pixels
for (var i = 0; i < bulletCount; i++) {
var offset = -spread / 2 + spread / (bulletCount - 1) * i;
var bullet = new PlayerBullet();
bullet.x = self.x + offset;
bullet.y = self.y - self.height / 2 - bullet.height / 2;
playerBullets.push(bullet);
game.addChild(bullet);
}
LK.getSound('shoot').play();
self.shootCooldown = 12; // 12 ticks cooldown
}
};
// Invincibility after hit
self.setInvincible = function (ticks) {
self.invincible = true;
self.invincibleTicks = ticks;
};
// Add down handler so clicking the ship makes it shoot
self.down = function (x, y, obj) {
// Only allow shooting if game is not over
if (typeof gameOver !== "undefined" && !gameOver) {
self.shoot();
}
};
return self;
});
// Powerup
var Powerup = Container.expand(function () {
var self = Container.call(this);
var p = self.attachAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = p.width;
self.height = p.height;
self.speed = 9;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Star (background)
var Star = Container.expand(function () {
var self = Container.call(this);
// Use a white ellipse as a star, random small size
var size = 2 + Math.random() * 4;
var star = self.attachAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
width: size,
height: size,
color: 0xffffff
});
self.width = size;
self.height = size;
self.speed = 2 + Math.random() * 4;
self.alpha = 0.3 + Math.random() * 0.5;
self.update = function () {
self.y += self.speed;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Play background music
// Spaceship (player)
// Player bullet
// Enemy ship
// Enemy bullet
// Asteroid
// Power-up
// Explosion effect (for flash)
// Sounds
// Music
LK.playMusic('galacticMusic');
// Game variables
var stars = [];
var starTick = 0;
var player;
var playerBullets = [];
var enemyShips = [];
var enemyBullets = [];
var asteroids = [];
var powerups = [];
var score = 0;
var lives = 30;
var level = 1;
var spawnTick = 0;
var asteroidTick = 0;
var powerupTick = 0;
var heartDropTick = 0;
var heartDrops = [];
var dragging = false;
var dragOffsetX = 0;
var dragOffsetY = 0;
var lastTouchX = 0;
var lastTouchY = 0;
var gameOver = false;
// Score display
var scoreTxt = new Text2('0', {
size: 100,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Lives display
var livesTxt = new Text2('♥♥♥', {
size: 80,
fill: 0xFF6666
});
livesTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(livesTxt);
// Add a heart image next to the lives text
var heartIcon = LK.getAsset('heart', {
anchorX: 1,
anchorY: 0,
x: -80,
y: 0,
width: 60,
height: 60
});
livesTxt.addChild(heartIcon);
// Center player at bottom center
player = new PlayerShip();
player.x = 2048 / 2;
player.y = 2732 - 300;
game.addChild(player);
// Touch controls
game.down = function (x, y, obj) {
if (gameOver) return;
// Only start drag if touch is on player
var local = player.toLocal(game.toGlobal({
x: x,
y: y
}));
if (local.x > -player.width / 2 && local.x < player.width / 2 && local.y > -player.height / 2 && local.y < player.height / 2) {
dragging = true;
dragOffsetX = player.x - x;
dragOffsetY = player.y - y;
}
lastTouchX = x;
lastTouchY = y;
};
game.move = function (x, y, obj) {
if (gameOver) return;
// Make the player ship follow the mouse/touch position directly, clamped to screen bounds
var halfW = player.width / 2;
var halfH = player.height / 2;
var newX = x;
var newY = y;
if (newX < halfW) newX = halfW;
if (newX > 2048 - halfW) newX = 2048 - halfW;
if (newY < halfH + 200) newY = halfH + 200; // Don't allow too high
if (newY > 2732 - halfH) newY = 2732 - halfH;
player.x = newX;
player.y = newY;
lastTouchX = x;
lastTouchY = y;
};
game.up = function (x, y, obj) {
dragging = false;
lastTouchX = x;
lastTouchY = y;
};
// Tap to shoot (anywhere)
game.tap = function (x, y, obj) {
if (gameOver) return;
player.shoot();
};
// For compatibility, also allow shooting on down if not dragging
game.down = function (origDown) {
return function (x, y, obj) {
origDown(x, y, obj);
if (!dragging && !gameOver) {
player.shoot();
}
};
}(game.down);
// Main update loop
game.update = function () {
// --- Star background update ---
starTick++;
// Spawn a new star every 6-10 ticks (slower for performance)
if (starTick >= 6 + Math.floor(Math.random() * 5)) {
starTick = 0;
var s = new Star();
s.x = 10 + Math.random() * (2048 - 20);
s.y = -s.height;
s.alpha = 0.3 + Math.random() * 0.5;
stars.push(s);
// Add to back of display list (background)
game.addChildAt(s, 0);
}
// Update and remove off-screen stars
for (var i = stars.length - 1; i >= 0; i--) {
var s = stars[i];
s.update();
if (s.y > 2732 + s.height) {
s.destroy();
stars.splice(i, 1);
}
}
if (gameOver) return;
// Update player
player.update();
// Auto-shoot: player fires every update if not on cooldown and not game over
if (!gameOver) {
player.shoot();
}
// Update player bullets
// Limit max bullets for performance
var maxPlayerBullets = 60;
if (playerBullets.length > maxPlayerBullets) {
// Remove oldest bullets if over limit
for (var rm = 0; rm < playerBullets.length - maxPlayerBullets; rm++) {
var oldB = playerBullets[rm];
oldB.destroy();
}
playerBullets.splice(0, playerBullets.length - maxPlayerBullets);
}
for (var i = playerBullets.length - 1; i >= 0; i--) {
var b = playerBullets[i];
b.update();
// Remove if off screen
if (b.y < -b.height) {
b.destroy();
playerBullets.splice(i, 1);
continue;
}
// Check collision with enemies
for (var j = enemyShips.length - 1; j >= 0; j--) {
var e = enemyShips[j];
if (b.intersects(e)) {
// Destroy enemy
LK.getSound('enemyExplode').play();
score += 100;
scoreTxt.setText(score);
// Flash enemy
LK.effects.flashObject(e, 0xffffff, 200);
e.destroy();
enemyShips.splice(j, 1);
// Remove bullet
b.destroy();
playerBullets.splice(i, 1);
break;
}
}
// Check collision with asteroids
for (var k = asteroids.length - 1; k >= 0; k--) {
var a = asteroids[k];
if (b.intersects(a)) {
// Destroy asteroid
LK.getSound('enemyExplode').play();
score += 50;
scoreTxt.setText(score);
LK.effects.flashObject(a, 0xffffff, 150);
a.destroy();
asteroids.splice(k, 1);
// Remove bullet
b.destroy();
playerBullets.splice(i, 1);
break;
}
}
}
// Update enemy ships
for (var i = enemyShips.length - 1; i >= 0; i--) {
var e = enemyShips[i];
e.update();
// Remove if off screen
if (e.y > 2732 + e.height) {
e.destroy();
enemyShips.splice(i, 1);
continue;
}
// Check collision with player
if (!player.invincible && e.intersects(player)) {
handlePlayerHit();
e.destroy();
enemyShips.splice(i, 1);
continue;
}
}
// Update enemy bullets
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var b = enemyBullets[i];
b.update();
if (b.y > 2732 + b.height) {
b.destroy();
enemyBullets.splice(i, 1);
continue;
}
if (!player.invincible && b.intersects(player)) {
handlePlayerHit();
b.destroy();
enemyBullets.splice(i, 1);
continue;
}
}
// Update asteroids
for (var i = asteroids.length - 1; i >= 0; i--) {
var a = asteroids[i];
a.update();
if (a.y > 2732 + a.height) {
a.destroy();
asteroids.splice(i, 1);
continue;
}
if (!player.invincible && a.intersects(player)) {
handlePlayerHit();
a.destroy();
asteroids.splice(i, 1);
continue;
}
}
// Update powerups
for (var i = powerups.length - 1; i >= 0; i--) {
var p = powerups[i];
p.update();
if (p.y > 2732 + p.height) {
p.destroy();
powerups.splice(i, 1);
continue;
}
if (p.intersects(player)) {
LK.getSound('powerup').play();
score += 250;
scoreTxt.setText(score);
// Grant temporary invincibility
player.setInvincible(120);
p.destroy();
powerups.splice(i, 1);
continue;
}
}
// Update heart drops
for (var i = heartDrops.length - 1; i >= 0; i--) {
var h = heartDrops[i];
h.update();
if (h.y > 2732 + h.height) {
h.destroy();
heartDrops.splice(i, 1);
continue;
}
if (h.intersects(player)) {
// Collect heart: +1 life, max 30
if (lives < 30) {
lives++;
updateLivesDisplay();
}
h.destroy();
heartDrops.splice(i, 1);
continue;
}
}
// Spawning logic
spawnTick++;
asteroidTick++;
powerupTick++;
// Enemy spawn
var enemyInterval = Math.max(60, 180 - level * 10);
if (spawnTick >= enemyInterval) {
spawnTick = 0;
var e = new EnemyShip();
e.x = 200 + Math.random() * (2048 - 400);
e.y = -e.height;
enemyShips.push(e);
game.addChild(e);
}
// Asteroid spawn
var asteroidInterval = Math.max(80, 220 - level * 8);
if (asteroidTick >= asteroidInterval) {
asteroidTick = 0;
var a = new Asteroid();
a.x = 100 + Math.random() * (2048 - 200);
a.y = -a.height;
asteroids.push(a);
game.addChild(a);
}
// Powerup spawn
if (powerupTick >= 900) {
powerupTick = 0;
var p = new Powerup();
p.x = 200 + Math.random() * (2048 - 400);
p.y = -p.height;
powerups.push(p);
game.addChild(p);
}
// Heart drop spawn (random, about every 400-700 ticks)
heartDropTick++;
if (heartDropTick >= 400 + Math.floor(Math.random() * 300)) {
heartDropTick = 0;
var h = new HeartDrop();
h.x = 200 + Math.random() * (2048 - 400);
h.y = -h.height;
heartDrops.push(h);
game.addChild(h);
}
// Level up every 1000 points
var newLevel = Math.floor(score / 1000) + 1;
if (newLevel > level) {
level = newLevel;
// Optionally, flash screen or show level up
LK.effects.flashScreen(0x00ffcc, 400);
}
};
// Handle player hit
function handlePlayerHit() {
if (player.invincible) return;
lives--;
updateLivesDisplay();
LK.getSound('playerExplode').play();
LK.effects.flashScreen(0xff0000, 600);
player.setInvincible(90);
if (lives <= 0) {
endGame();
}
}
// Update lives display
function updateLivesDisplay() {
var s = '';
for (var i = 0; i < lives; i++) s += '♥';
livesTxt.setText(s);
}
// End game
function endGame() {
gameOver = true;
LK.showGameOver();
}
// Reset game state on restart
game.on('reset', function () {
// Remove all objects
for (var i = 0; i < stars.length; i++) stars[i].destroy();
for (var i = 0; i < playerBullets.length; i++) playerBullets[i].destroy();
for (var i = 0; i < enemyShips.length; i++) enemyShips[i].destroy();
for (var i = 0; i < enemyBullets.length; i++) enemyBullets[i].destroy();
for (var i = 0; i < asteroids.length; i++) asteroids[i].destroy();
for (var i = 0; i < powerups.length; i++) powerups[i].destroy();
for (var i = 0; i < heartDrops.length; i++) heartDrops[i].destroy();
stars = [];
playerBullets = [];
enemyShips = [];
enemyBullets = [];
asteroids = [];
powerups = [];
heartDrops = [];
score = 0;
level = 1;
lives = 30;
spawnTick = 0;
asteroidTick = 0;
powerupTick = 0;
heartDropTick = 0;
starTick = 0;
dragging = false;
gameOver = false;
scoreTxt.setText('0');
updateLivesDisplay();
player.x = 2048 / 2;
player.y = 2732 - 300;
player.invincible = false;
player.invincibleTicks = 0;
LK.playMusic('galacticMusic');
});
// For touch devices, allow tap to shoot
game.on('tap', function (x, y, obj) {
if (gameOver) return;
player.shoot();
}); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Asteroid
var Asteroid = Container.expand(function () {
var self = Container.call(this);
var rock = self.attachAsset('asteroid', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = rock.width;
self.height = rock.height;
self.speed = 10 + Math.random() * 6;
self.rotationSpeed = (Math.random() - 0.5) * 0.1;
self.update = function () {
self.y += self.speed;
rock.rotation += self.rotationSpeed;
};
return self;
});
// Enemy Bullet
var EnemyBullet = Container.expand(function () {
var self = Container.call(this);
var bullet = self.attachAsset('enemyBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = bullet.width;
self.height = bullet.height;
self.speed = 22;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Enemy Ship
var EnemyShip = Container.expand(function () {
var self = Container.call(this);
var ship = self.attachAsset('enemyShip', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = ship.width;
self.height = ship.height;
self.speed = 7 + Math.random() * 4;
self.shootCooldown = 60 + Math.floor(Math.random() * 60);
self.update = function () {
self.y += self.speed;
if (self.shootCooldown > 0) {
self.shootCooldown--;
} else {
self.shoot();
self.shootCooldown = 90 + Math.floor(Math.random() * 60);
}
};
self.shoot = function () {
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y + self.height / 2 + bullet.height / 2;
enemyBullets.push(bullet);
game.addChild(bullet);
};
return self;
});
// HeartDrop (falling heart item)
var HeartDrop = Container.expand(function () {
var self = Container.call(this);
var heart = self.attachAsset('heart', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = heart.width;
self.height = heart.height;
self.speed = 10;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Player Bullet
var PlayerBullet = Container.expand(function () {
var self = Container.call(this);
var bullet = self.attachAsset('playerBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = bullet.width;
self.height = bullet.height;
self.speed = -36;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Player Ship
var PlayerShip = Container.expand(function () {
var self = Container.call(this);
var ship = self.attachAsset('playerShip', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = ship.width;
self.height = ship.height;
self.shootCooldown = 0;
self.invincible = false;
self.invincibleTicks = 0;
self.update = function () {
if (self.shootCooldown > 0) self.shootCooldown--;
if (self.invincible) {
self.invincibleTicks--;
if (self.invincibleTicks <= 0) {
self.invincible = false;
ship.alpha = 1;
} else {
// Flicker effect
ship.alpha = LK.ticks % 10 < 5 ? 0.4 : 1;
}
}
};
// Shoot method
self.shoot = function () {
if (self.shootCooldown <= 0) {
// Shoot 6 bullets, evenly spread (performance optimized)
var bulletCount = 6;
var spread = 120; // total spread in pixels
for (var i = 0; i < bulletCount; i++) {
var offset = -spread / 2 + spread / (bulletCount - 1) * i;
var bullet = new PlayerBullet();
bullet.x = self.x + offset;
bullet.y = self.y - self.height / 2 - bullet.height / 2;
playerBullets.push(bullet);
game.addChild(bullet);
}
LK.getSound('shoot').play();
self.shootCooldown = 12; // 12 ticks cooldown
}
};
// Invincibility after hit
self.setInvincible = function (ticks) {
self.invincible = true;
self.invincibleTicks = ticks;
};
// Add down handler so clicking the ship makes it shoot
self.down = function (x, y, obj) {
// Only allow shooting if game is not over
if (typeof gameOver !== "undefined" && !gameOver) {
self.shoot();
}
};
return self;
});
// Powerup
var Powerup = Container.expand(function () {
var self = Container.call(this);
var p = self.attachAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = p.width;
self.height = p.height;
self.speed = 9;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Star (background)
var Star = Container.expand(function () {
var self = Container.call(this);
// Use a white ellipse as a star, random small size
var size = 2 + Math.random() * 4;
var star = self.attachAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
width: size,
height: size,
color: 0xffffff
});
self.width = size;
self.height = size;
self.speed = 2 + Math.random() * 4;
self.alpha = 0.3 + Math.random() * 0.5;
self.update = function () {
self.y += self.speed;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Play background music
// Spaceship (player)
// Player bullet
// Enemy ship
// Enemy bullet
// Asteroid
// Power-up
// Explosion effect (for flash)
// Sounds
// Music
LK.playMusic('galacticMusic');
// Game variables
var stars = [];
var starTick = 0;
var player;
var playerBullets = [];
var enemyShips = [];
var enemyBullets = [];
var asteroids = [];
var powerups = [];
var score = 0;
var lives = 30;
var level = 1;
var spawnTick = 0;
var asteroidTick = 0;
var powerupTick = 0;
var heartDropTick = 0;
var heartDrops = [];
var dragging = false;
var dragOffsetX = 0;
var dragOffsetY = 0;
var lastTouchX = 0;
var lastTouchY = 0;
var gameOver = false;
// Score display
var scoreTxt = new Text2('0', {
size: 100,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Lives display
var livesTxt = new Text2('♥♥♥', {
size: 80,
fill: 0xFF6666
});
livesTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(livesTxt);
// Add a heart image next to the lives text
var heartIcon = LK.getAsset('heart', {
anchorX: 1,
anchorY: 0,
x: -80,
y: 0,
width: 60,
height: 60
});
livesTxt.addChild(heartIcon);
// Center player at bottom center
player = new PlayerShip();
player.x = 2048 / 2;
player.y = 2732 - 300;
game.addChild(player);
// Touch controls
game.down = function (x, y, obj) {
if (gameOver) return;
// Only start drag if touch is on player
var local = player.toLocal(game.toGlobal({
x: x,
y: y
}));
if (local.x > -player.width / 2 && local.x < player.width / 2 && local.y > -player.height / 2 && local.y < player.height / 2) {
dragging = true;
dragOffsetX = player.x - x;
dragOffsetY = player.y - y;
}
lastTouchX = x;
lastTouchY = y;
};
game.move = function (x, y, obj) {
if (gameOver) return;
// Make the player ship follow the mouse/touch position directly, clamped to screen bounds
var halfW = player.width / 2;
var halfH = player.height / 2;
var newX = x;
var newY = y;
if (newX < halfW) newX = halfW;
if (newX > 2048 - halfW) newX = 2048 - halfW;
if (newY < halfH + 200) newY = halfH + 200; // Don't allow too high
if (newY > 2732 - halfH) newY = 2732 - halfH;
player.x = newX;
player.y = newY;
lastTouchX = x;
lastTouchY = y;
};
game.up = function (x, y, obj) {
dragging = false;
lastTouchX = x;
lastTouchY = y;
};
// Tap to shoot (anywhere)
game.tap = function (x, y, obj) {
if (gameOver) return;
player.shoot();
};
// For compatibility, also allow shooting on down if not dragging
game.down = function (origDown) {
return function (x, y, obj) {
origDown(x, y, obj);
if (!dragging && !gameOver) {
player.shoot();
}
};
}(game.down);
// Main update loop
game.update = function () {
// --- Star background update ---
starTick++;
// Spawn a new star every 6-10 ticks (slower for performance)
if (starTick >= 6 + Math.floor(Math.random() * 5)) {
starTick = 0;
var s = new Star();
s.x = 10 + Math.random() * (2048 - 20);
s.y = -s.height;
s.alpha = 0.3 + Math.random() * 0.5;
stars.push(s);
// Add to back of display list (background)
game.addChildAt(s, 0);
}
// Update and remove off-screen stars
for (var i = stars.length - 1; i >= 0; i--) {
var s = stars[i];
s.update();
if (s.y > 2732 + s.height) {
s.destroy();
stars.splice(i, 1);
}
}
if (gameOver) return;
// Update player
player.update();
// Auto-shoot: player fires every update if not on cooldown and not game over
if (!gameOver) {
player.shoot();
}
// Update player bullets
// Limit max bullets for performance
var maxPlayerBullets = 60;
if (playerBullets.length > maxPlayerBullets) {
// Remove oldest bullets if over limit
for (var rm = 0; rm < playerBullets.length - maxPlayerBullets; rm++) {
var oldB = playerBullets[rm];
oldB.destroy();
}
playerBullets.splice(0, playerBullets.length - maxPlayerBullets);
}
for (var i = playerBullets.length - 1; i >= 0; i--) {
var b = playerBullets[i];
b.update();
// Remove if off screen
if (b.y < -b.height) {
b.destroy();
playerBullets.splice(i, 1);
continue;
}
// Check collision with enemies
for (var j = enemyShips.length - 1; j >= 0; j--) {
var e = enemyShips[j];
if (b.intersects(e)) {
// Destroy enemy
LK.getSound('enemyExplode').play();
score += 100;
scoreTxt.setText(score);
// Flash enemy
LK.effects.flashObject(e, 0xffffff, 200);
e.destroy();
enemyShips.splice(j, 1);
// Remove bullet
b.destroy();
playerBullets.splice(i, 1);
break;
}
}
// Check collision with asteroids
for (var k = asteroids.length - 1; k >= 0; k--) {
var a = asteroids[k];
if (b.intersects(a)) {
// Destroy asteroid
LK.getSound('enemyExplode').play();
score += 50;
scoreTxt.setText(score);
LK.effects.flashObject(a, 0xffffff, 150);
a.destroy();
asteroids.splice(k, 1);
// Remove bullet
b.destroy();
playerBullets.splice(i, 1);
break;
}
}
}
// Update enemy ships
for (var i = enemyShips.length - 1; i >= 0; i--) {
var e = enemyShips[i];
e.update();
// Remove if off screen
if (e.y > 2732 + e.height) {
e.destroy();
enemyShips.splice(i, 1);
continue;
}
// Check collision with player
if (!player.invincible && e.intersects(player)) {
handlePlayerHit();
e.destroy();
enemyShips.splice(i, 1);
continue;
}
}
// Update enemy bullets
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var b = enemyBullets[i];
b.update();
if (b.y > 2732 + b.height) {
b.destroy();
enemyBullets.splice(i, 1);
continue;
}
if (!player.invincible && b.intersects(player)) {
handlePlayerHit();
b.destroy();
enemyBullets.splice(i, 1);
continue;
}
}
// Update asteroids
for (var i = asteroids.length - 1; i >= 0; i--) {
var a = asteroids[i];
a.update();
if (a.y > 2732 + a.height) {
a.destroy();
asteroids.splice(i, 1);
continue;
}
if (!player.invincible && a.intersects(player)) {
handlePlayerHit();
a.destroy();
asteroids.splice(i, 1);
continue;
}
}
// Update powerups
for (var i = powerups.length - 1; i >= 0; i--) {
var p = powerups[i];
p.update();
if (p.y > 2732 + p.height) {
p.destroy();
powerups.splice(i, 1);
continue;
}
if (p.intersects(player)) {
LK.getSound('powerup').play();
score += 250;
scoreTxt.setText(score);
// Grant temporary invincibility
player.setInvincible(120);
p.destroy();
powerups.splice(i, 1);
continue;
}
}
// Update heart drops
for (var i = heartDrops.length - 1; i >= 0; i--) {
var h = heartDrops[i];
h.update();
if (h.y > 2732 + h.height) {
h.destroy();
heartDrops.splice(i, 1);
continue;
}
if (h.intersects(player)) {
// Collect heart: +1 life, max 30
if (lives < 30) {
lives++;
updateLivesDisplay();
}
h.destroy();
heartDrops.splice(i, 1);
continue;
}
}
// Spawning logic
spawnTick++;
asteroidTick++;
powerupTick++;
// Enemy spawn
var enemyInterval = Math.max(60, 180 - level * 10);
if (spawnTick >= enemyInterval) {
spawnTick = 0;
var e = new EnemyShip();
e.x = 200 + Math.random() * (2048 - 400);
e.y = -e.height;
enemyShips.push(e);
game.addChild(e);
}
// Asteroid spawn
var asteroidInterval = Math.max(80, 220 - level * 8);
if (asteroidTick >= asteroidInterval) {
asteroidTick = 0;
var a = new Asteroid();
a.x = 100 + Math.random() * (2048 - 200);
a.y = -a.height;
asteroids.push(a);
game.addChild(a);
}
// Powerup spawn
if (powerupTick >= 900) {
powerupTick = 0;
var p = new Powerup();
p.x = 200 + Math.random() * (2048 - 400);
p.y = -p.height;
powerups.push(p);
game.addChild(p);
}
// Heart drop spawn (random, about every 400-700 ticks)
heartDropTick++;
if (heartDropTick >= 400 + Math.floor(Math.random() * 300)) {
heartDropTick = 0;
var h = new HeartDrop();
h.x = 200 + Math.random() * (2048 - 400);
h.y = -h.height;
heartDrops.push(h);
game.addChild(h);
}
// Level up every 1000 points
var newLevel = Math.floor(score / 1000) + 1;
if (newLevel > level) {
level = newLevel;
// Optionally, flash screen or show level up
LK.effects.flashScreen(0x00ffcc, 400);
}
};
// Handle player hit
function handlePlayerHit() {
if (player.invincible) return;
lives--;
updateLivesDisplay();
LK.getSound('playerExplode').play();
LK.effects.flashScreen(0xff0000, 600);
player.setInvincible(90);
if (lives <= 0) {
endGame();
}
}
// Update lives display
function updateLivesDisplay() {
var s = '';
for (var i = 0; i < lives; i++) s += '♥';
livesTxt.setText(s);
}
// End game
function endGame() {
gameOver = true;
LK.showGameOver();
}
// Reset game state on restart
game.on('reset', function () {
// Remove all objects
for (var i = 0; i < stars.length; i++) stars[i].destroy();
for (var i = 0; i < playerBullets.length; i++) playerBullets[i].destroy();
for (var i = 0; i < enemyShips.length; i++) enemyShips[i].destroy();
for (var i = 0; i < enemyBullets.length; i++) enemyBullets[i].destroy();
for (var i = 0; i < asteroids.length; i++) asteroids[i].destroy();
for (var i = 0; i < powerups.length; i++) powerups[i].destroy();
for (var i = 0; i < heartDrops.length; i++) heartDrops[i].destroy();
stars = [];
playerBullets = [];
enemyShips = [];
enemyBullets = [];
asteroids = [];
powerups = [];
heartDrops = [];
score = 0;
level = 1;
lives = 30;
spawnTick = 0;
asteroidTick = 0;
powerupTick = 0;
heartDropTick = 0;
starTick = 0;
dragging = false;
gameOver = false;
scoreTxt.setText('0');
updateLivesDisplay();
player.x = 2048 / 2;
player.y = 2732 - 300;
player.invincible = false;
player.invincibleTicks = 0;
LK.playMusic('galacticMusic');
});
// For touch devices, allow tap to shoot
game.on('tap', function (x, y, obj) {
if (gameOver) return;
player.shoot();
});
a cool ship. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
an asteroid. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
bullet. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
an evil futuristic looking ship. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
explosion of metal and fire cloud. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
fire bullet. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
green health plus. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
heart. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat