User prompt
make animation enemy fall after hit
User prompt
add background asset
User prompt
player death after one hit
User prompt
erase level text
User prompt
endless game and delete boss
User prompt
player and enemy also boss get explode when hit ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
fix control player response
User prompt
take player possition first on the middle when game play
User prompt
fix smooth control
User prompt
get smooth control ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
fix level
User prompt
reduce lag
User prompt
reduce bullet speed player and enemy
User prompt
reduce speed of gameplay
User prompt
player multi direction shoot bullet
Code edit (1 edits merged)
Please save this source code
User prompt
Galactic Gunner: Auto-Blaster
Initial prompt
side scroller auto shooting game
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Enemy var Enemy = Container.expand(function () { var self = Container.call(this); var enemyGfx = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5 }); self.width = enemyGfx.width; self.height = enemyGfx.height; self.speed = 5 + Math.random() * 4; self.hp = 1; self.shootCooldown = 0; self.type = 'normal'; // or 'boss' self.update = function () { // Check if enemy is in falling state if (self.isFalling) { return; // Let the falling animation be managed separately } self.x -= self.speed; if (self.shootCooldown > 0) { self.shootCooldown--; } }; return self; }); // Enemy bullet var EnemyBullet = Container.expand(function () { var self = Container.call(this); var bulletGfx = self.attachAsset('enemyBullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 8; self.update = function () { self.x -= self.speed; }; return self; }); // Explosion effect var Explosion = Container.expand(function () { var self = Container.call(this); var explosionGfx = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5, tint: 0xff5500 }); self.lifespan = 20; self.scale = 0.2; // Animation effect self.start = function (size) { self.scale = size || 1; explosionGfx.scale.x = 0.2; explosionGfx.scale.y = 0.2; explosionGfx.alpha = 1; explosionGfx.tint = 0xff5500; // Growth animation tween(explosionGfx, { scaleX: self.scale, scaleY: self.scale }, { duration: 200, easing: tween.easeOut }); // Color change animation tween(explosionGfx, { tint: 0xffcc00 }, { duration: 100, easing: tween.linear, onFinish: function onFinish() { tween(explosionGfx, { tint: 0xffffaa, alpha: 0 }, { duration: 300, easing: tween.easeOut }); } }); }; self.update = function () { self.lifespan--; if (self.lifespan <= 0) { self.destroy(); } }; return self; }); // Player bullet var PlayerBullet = Container.expand(function () { var self = Container.call(this); var bulletGfx = self.attachAsset('playerBullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 12; self.update = function () { // Default angle is 0 (right) if (typeof self.angle === "undefined") { self.angle = 0; } self.x += self.speed * Math.cos(self.angle); self.y += self.speed * Math.sin(self.angle); }; return self; }); // Power-up var Powerup = Container.expand(function () { var self = Container.call(this); var powerGfx = self.attachAsset('powerup', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 6; self.type = 'power'; // could be 'power', 'heal', etc. self.update = function () { self.x -= self.speed; }; return self; }); // Player spaceship var Ship = Container.expand(function () { var self = Container.call(this); var shipGfx = self.attachAsset('ship', { anchorX: 0.5, anchorY: 0.5 }); self.width = shipGfx.width; self.height = shipGfx.height; self.fireCooldown = 0; self.powerLevel = 1; // 1=single, 2=double, 3=triple shot self.invincible = false; self.invincibleTimer = 0; // Invincibility flash effect self.setInvincible = function (duration) { self.invincible = true; self.invincibleTimer = duration; tween(shipGfx, { alpha: 0.4 }, { duration: 100, easing: tween.linear, onFinish: function onFinish() { tween(shipGfx, { alpha: 1 }, { duration: 100, easing: tween.linear }); } }); }; // Called every tick self.update = function () { if (self.invincible) { self.invincibleTimer -= 1; if (self.invincibleTimer <= 0) { self.invincible = false; shipGfx.alpha = 1; } } // Track last drag direction for multi-directional shooting if (typeof self.lastShootAngle === "undefined") { self.lastShootAngle = 0; } if (typeof self.lastX === "undefined") { self.lastX = self.x; } if (typeof self.lastY === "undefined") { self.lastY = self.y; } if (self.x !== self.lastX || self.y !== self.lastY) { // Calculate angle of movement var dx = self.x - self.lastX; var dy = self.y - self.lastY; if (dx !== 0 || dy !== 0) { self.lastShootAngle = Math.atan2(dy, dx); } } self.lastX = self.x; self.lastY = self.y; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000010 }); /**** * Game Code ****/ // Background var background = LK.getAsset('background', { anchorX: 0, anchorY: 0, x: 0, y: 0 }); game.addChild(background); // Music // Sound effects // Boss // Power-up // Enemy bullet // Enemy // Player bullet // Spaceship (player) // Play background music LK.playMusic('bgmusic'); // Game variables var ship; var playerBullets = []; var enemies = []; var enemyBullets = []; var powerups = []; var fallingEnemies = []; var scoreTxt; var levelTxt; var dragNode = null; var lastShipX = 0, lastShipY = 0; var spawnTimer = 0; var powerupTimer = 0; var level = 1; var gameOver = false; // Bullet pools for reusing objects (reduces garbage collection) var playerBulletPool = []; var enemyBulletPool = []; // Helper function to get bullet from pool or create new one function getPlayerBulletFromPool() { if (playerBulletPool.length > 0) { var bullet = playerBulletPool.pop(); bullet.visible = true; return bullet; } return new PlayerBullet(); } function getEnemyBulletFromPool() { if (enemyBulletPool.length > 0) { var bullet = enemyBulletPool.pop(); bullet.visible = true; return bullet; } return new EnemyBullet(); } // Helper function to return bullet to pool function returnPlayerBulletToPool(bullet, index) { bullet.visible = false; playerBulletPool.push(bullet); playerBullets.splice(index, 1); } function returnEnemyBulletToPool(bullet, index) { bullet.visible = false; enemyBulletPool.push(bullet); enemyBullets.splice(index, 1); } // Score display scoreTxt = new Text2('0', { size: 120, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Level display removed // Create player ship ship = new Ship(); game.addChild(ship); ship.x = 2048 / 2; ship.y = 2732 / 2; // Make ship visible and interactive ship.interactive = true; ship.buttonMode = true; // Helper: clamp ship inside screen function clampShipPosition() { var halfW = ship.width / 2; var halfH = ship.height / 2; if (ship.x < 100 + halfW) { ship.x = 100 + halfW; } if (ship.x > 2048 - halfW) { ship.x = 2048 - halfW; } if (ship.y < halfH) { ship.y = halfH; } if (ship.y > 2732 - halfH) { ship.y = 2732 - halfH; } } // Handle dragging ship function handleMove(x, y, obj) { if (dragNode) { // Direct positioning for immediate response dragNode.x = x; dragNode.y = y; clampShipPosition(); } } game.move = handleMove; game.down = function (x, y, obj) { // Improved touch detection with slightly larger hitbox for better responsiveness var local = ship.toLocal(game.toGlobal({ x: x, y: y })); var touchPadding = 20; // Extra padding for easier touch if (local.x > -ship.width / 2 - touchPadding && local.x < ship.width / 2 + touchPadding && local.y > -ship.height / 2 - touchPadding && local.y < ship.height / 2 + touchPadding) { dragNode = ship; // Immediate position update handleMove(x, y, obj); } }; game.up = function (x, y, obj) { // Make sure position is updated one last time before releasing if (dragNode) { handleMove(x, y, obj); } dragNode = null; }; // Spawn enemy function spawnEnemy() { var e = new Enemy(); e.x = 2048 + e.width / 2; e.y = 200 + Math.random() * (2732 - 400); e.hp = 1 + Math.floor(level / 2); e.speed = 5 + Math.random() * 4 + level * 0.6; e.shootCooldown = 90 + Math.floor(Math.random() * 80); enemies.push(e); game.addChild(e); } // Spawn powerup function spawnPowerup() { var p = new Powerup(); p.x = 2048 + 60; p.y = 200 + Math.random() * (2732 - 400); p.type = 'power'; powerups.push(p); game.addChild(p); } // Main update loop game.update = function () { if (gameOver) { return; } // Ship update ship.update(); // Auto-fire if (ship.fireCooldown > 0) { ship.fireCooldown--; } if (ship.fireCooldown <= 0) { // Fire based on power level, but allow multi-directional shooting var bulletAngles = [ship.lastShootAngle || 0]; if (ship.powerLevel === 2) { bulletAngles = [(ship.lastShootAngle || 0) - Math.PI / 16, (ship.lastShootAngle || 0) + Math.PI / 16]; } if (ship.powerLevel >= 3) { bulletAngles = [(ship.lastShootAngle || 0) - Math.PI / 8, ship.lastShootAngle || 0, (ship.lastShootAngle || 0) + Math.PI / 8]; } for (var i = 0; i < bulletAngles.length; ++i) { var b = getPlayerBulletFromPool(); // Start at ship's nose b.x = ship.x + Math.cos(bulletAngles[i]) * (ship.width / 2 + 10); b.y = ship.y + Math.sin(bulletAngles[i]) * (ship.width / 2 + 10); b.angle = bulletAngles[i]; playerBullets.push(b); if (!b.parent) { game.addChild(b); } } LK.getSound('shoot').play(); ship.fireCooldown = 18; } // Update player bullets - optimized to reduce collision checking var bulletsToRemove = []; for (var i = playerBullets.length - 1; i >= 0; --i) { var b = playerBullets[i]; b.update(); // Remove if off screen if (b.x > 2048 + 50 || b.x < -50 || b.y > 2732 + 50 || b.y < -50) { returnPlayerBulletToPool(b, i); continue; } // Check if bullet already marked for removal if (bulletsToRemove.indexOf(i) !== -1) { continue; } // Hit enemy - only check enemies in proximity var bulletHitSomething = false; for (var j = enemies.length - 1; j >= 0; --j) { var e = enemies[j]; // Only check collision if enemy is close to bullet (rough proximity check) if (Math.abs(e.x - b.x) < 150 && Math.abs(e.y - b.y) < 150) { if (b.intersects(e)) { e.hp--; LK.getSound('explosion').play(); // Small hit explosion var hitExplosion = new Explosion(); hitExplosion.x = b.x; hitExplosion.y = b.y; hitExplosion.start(0.5); game.addChild(hitExplosion); bulletHitSomething = true; if (e.hp <= 0) { // Create explosion at enemy position var explosion = new Explosion(); explosion.x = e.x; explosion.y = e.y; explosion.start(1.2); game.addChild(explosion); LK.setScore(LK.getScore() + 10); scoreTxt.setText(LK.getScore()); // Level progression for endless game if (LK.getScore() > 0 && LK.getScore() % 100 === 0) { level++; // Flash effect LK.effects.flashScreen(0x00ff00, 800); // Power up as reward every few levels if (level % 3 === 0) { ship.powerLevel = Math.min(ship.powerLevel + 1, 3); } } // Add falling animation instead of destroying immediately e.isFalling = true; e.fallSpeed = 2; e.fallRotation = (Math.random() - 0.5) * 0.2; e.fallAlpha = 1; // Remove from enemies array but don't destroy yet enemies.splice(j, 1); // Add to separate falling enemies array to be managed separately if (!fallingEnemies) { fallingEnemies = []; } fallingEnemies.push(e); } break; } } } // If bullet hit an enemy, return the bullet to the pool and continue to next bullet if (bulletHitSomething) { returnPlayerBulletToPool(b, i); continue; } } // Update enemies - optimized var enemiesNearPlayer = 0; for (var i = enemies.length - 1; i >= 0; --i) { var e = enemies[i]; e.update(); // Remove if off screen if (e.x < -e.width / 2) { e.destroy(); enemies.splice(i, 1); continue; } // Check if enemy is near player var isNearPlayer = Math.abs(e.x - ship.x) < 400; if (isNearPlayer) { enemiesNearPlayer++; } // Enemy shoots if (e.shootCooldown <= 0 && e.x < 2048 && e.x > 0) { // Limit shooting frequency based on how many enemies are near player if (enemiesNearPlayer < 3 || Math.random() < 0.3) { var eb = getEnemyBulletFromPool(); eb.x = e.x - e.width / 2 - 10; eb.y = e.y; enemyBullets.push(eb); if (!eb.parent) { game.addChild(eb); } LK.getSound('enemyShoot').play(); } // Increase cooldown when many enemies are present var cooldownBase = 80 + Math.floor(Math.random() * 60); e.shootCooldown = cooldownBase * (1 + (enemies.length > 10 ? 0.5 : 0)); } // Collide with ship - only check when close if (!ship.invincible && isNearPlayer && Math.abs(e.y - ship.y) < 150) { if (e.intersects(ship)) { // Ship hit // Create explosion at collision point var explosion = new Explosion(); explosion.x = (ship.x + e.x) / 2; explosion.y = (ship.y + e.y) / 2; explosion.start(1); game.addChild(explosion); // Game over immediately when player is hit by enemy gameOver = true; LK.effects.flashObject(ship, 0xff0000, 600); LK.effects.flashScreen(0xff0000, 400); LK.showGameOver(); return; } } } // Update enemy bullets - batch process with reduced collision checks var bulletsToCheck = []; // First update all bullets and mark ones to remove (off-screen) for (var i = enemyBullets.length - 1; i >= 0; --i) { var eb = enemyBullets[i]; eb.update(); if (eb.x < -50) { returnEnemyBulletToPool(eb, i); continue; } // Only check collision if ship is not invincible and bullet is near ship if (!ship.invincible && Math.abs(eb.x - ship.x) < 150 && Math.abs(eb.y - ship.y) < 150) { bulletsToCheck.push({ index: i, bullet: eb }); } } // Now only check collisions for bullets near the ship if (!ship.invincible) { for (var i = 0; i < bulletsToCheck.length; i++) { var bulletInfo = bulletsToCheck[i]; var eb = bulletInfo.bullet; if (eb.intersects(ship)) { // Create explosion at bullet position var explosion = new Explosion(); explosion.x = eb.x; explosion.y = eb.y; explosion.start(1); game.addChild(explosion); LK.effects.flashObject(ship, 0xff0000, 600); LK.effects.flashScreen(0xff0000, 400); eb.destroy(); enemyBullets.splice(bulletInfo.index, 1); // Game over immediately when player is hit by bullet gameOver = true; LK.showGameOver(); return; break; // Exit loop after first hit } } } // Update powerups - only check collection when close for (var i = powerups.length - 1; i >= 0; --i) { var p = powerups[i]; p.update(); if (p.x < -50) { p.destroy(); powerups.splice(i, 1); continue; } // Collect - only check intersection when close to ship if (Math.abs(p.x - ship.x) < 120 && Math.abs(p.y - ship.y) < 120) { if (p.intersects(ship)) { LK.getSound('powerup').play(); ship.powerLevel = Math.min(ship.powerLevel + 1, 3); LK.setScore(LK.getScore() + 30); scoreTxt.setText(LK.getScore()); p.destroy(); powerups.splice(i, 1); } } } // Spawning logic - always active in endless mode spawnTimer--; if (spawnTimer <= 0) { // Spawn more enemies at higher levels var enemiesToSpawn = 1 + Math.floor(level / 5); for (var i = 0; i < enemiesToSpawn; i++) { if (enemies.length < 25) { // Cap max enemies to prevent lag spawnEnemy(); } } spawnTimer = Math.max(30, 100 - level * 3); } // Powerup spawn powerupTimer--; if (powerupTimer <= 0) { spawnPowerup(); powerupTimer = 600 + Math.floor(Math.random() * 400); } // Increase enemy difficulty with level if (LK.ticks % 600 === 0) { for (var i = 0; i < enemies.length; i++) { // Slightly increase enemy speed based on level if (Math.random() < 0.3) { enemies[i].speed += 0.2; } } } // Update falling enemies if (fallingEnemies && fallingEnemies.length > 0) { for (var i = fallingEnemies.length - 1; i >= 0; i--) { var fe = fallingEnemies[i]; if (fe.isFalling) { // Add gravity effect fe.fallSpeed += 0.3; fe.y += fe.fallSpeed; // Add rotation fe.rotation += fe.fallRotation; // Fade out fe.fallAlpha -= 0.02; if (fe.fallAlpha <= 0) { fe.fallAlpha = 0; } fe.alpha = fe.fallAlpha; // Remove when fallen off screen or fully faded if (fe.y > 2732 + fe.height || fe.fallAlpha <= 0) { fe.destroy(); fallingEnemies.splice(i, 1); } } } } // No win condition - endless game }; // Reset game state on restart game.on('reset', function () { // Return bullets to pools instead of destroying for (var i = 0; i < playerBullets.length; ++i) { playerBullets[i].visible = false; playerBulletPool.push(playerBullets[i]); } for (var i = 0; i < enemyBullets.length; ++i) { enemyBullets[i].visible = false; enemyBulletPool.push(enemyBullets[i]); } // Destroy other objects for (var i = 0; i < enemies.length; ++i) { enemies[i].destroy(); } // Destroy falling enemies if (fallingEnemies) { for (var i = 0; i < fallingEnemies.length; ++i) { fallingEnemies[i].destroy(); } } for (var i = 0; i < powerups.length; ++i) { powerups[i].destroy(); } playerBullets = []; enemies = []; enemyBullets = []; powerups = []; fallingEnemies = []; level = 1; ship.x = 2048 / 2; // Center horizontally ship.y = 2732 / 2; ship.powerLevel = 1; ship.invincible = false; ship.invincibleTimer = 0; LK.setScore(0); scoreTxt.setText('0'); spawnTimer = 30; powerupTimer = 400; gameOver = false; });
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Enemy
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGfx = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = enemyGfx.width;
self.height = enemyGfx.height;
self.speed = 5 + Math.random() * 4;
self.hp = 1;
self.shootCooldown = 0;
self.type = 'normal'; // or 'boss'
self.update = function () {
// Check if enemy is in falling state
if (self.isFalling) {
return; // Let the falling animation be managed separately
}
self.x -= self.speed;
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
};
return self;
});
// Enemy bullet
var EnemyBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGfx = self.attachAsset('enemyBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.update = function () {
self.x -= self.speed;
};
return self;
});
// Explosion effect
var Explosion = Container.expand(function () {
var self = Container.call(this);
var explosionGfx = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0xff5500
});
self.lifespan = 20;
self.scale = 0.2;
// Animation effect
self.start = function (size) {
self.scale = size || 1;
explosionGfx.scale.x = 0.2;
explosionGfx.scale.y = 0.2;
explosionGfx.alpha = 1;
explosionGfx.tint = 0xff5500;
// Growth animation
tween(explosionGfx, {
scaleX: self.scale,
scaleY: self.scale
}, {
duration: 200,
easing: tween.easeOut
});
// Color change animation
tween(explosionGfx, {
tint: 0xffcc00
}, {
duration: 100,
easing: tween.linear,
onFinish: function onFinish() {
tween(explosionGfx, {
tint: 0xffffaa,
alpha: 0
}, {
duration: 300,
easing: tween.easeOut
});
}
});
};
self.update = function () {
self.lifespan--;
if (self.lifespan <= 0) {
self.destroy();
}
};
return self;
});
// Player bullet
var PlayerBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGfx = self.attachAsset('playerBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 12;
self.update = function () {
// Default angle is 0 (right)
if (typeof self.angle === "undefined") {
self.angle = 0;
}
self.x += self.speed * Math.cos(self.angle);
self.y += self.speed * Math.sin(self.angle);
};
return self;
});
// Power-up
var Powerup = Container.expand(function () {
var self = Container.call(this);
var powerGfx = self.attachAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 6;
self.type = 'power'; // could be 'power', 'heal', etc.
self.update = function () {
self.x -= self.speed;
};
return self;
});
// Player spaceship
var Ship = Container.expand(function () {
var self = Container.call(this);
var shipGfx = self.attachAsset('ship', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = shipGfx.width;
self.height = shipGfx.height;
self.fireCooldown = 0;
self.powerLevel = 1; // 1=single, 2=double, 3=triple shot
self.invincible = false;
self.invincibleTimer = 0;
// Invincibility flash effect
self.setInvincible = function (duration) {
self.invincible = true;
self.invincibleTimer = duration;
tween(shipGfx, {
alpha: 0.4
}, {
duration: 100,
easing: tween.linear,
onFinish: function onFinish() {
tween(shipGfx, {
alpha: 1
}, {
duration: 100,
easing: tween.linear
});
}
});
};
// Called every tick
self.update = function () {
if (self.invincible) {
self.invincibleTimer -= 1;
if (self.invincibleTimer <= 0) {
self.invincible = false;
shipGfx.alpha = 1;
}
}
// Track last drag direction for multi-directional shooting
if (typeof self.lastShootAngle === "undefined") {
self.lastShootAngle = 0;
}
if (typeof self.lastX === "undefined") {
self.lastX = self.x;
}
if (typeof self.lastY === "undefined") {
self.lastY = self.y;
}
if (self.x !== self.lastX || self.y !== self.lastY) {
// Calculate angle of movement
var dx = self.x - self.lastX;
var dy = self.y - self.lastY;
if (dx !== 0 || dy !== 0) {
self.lastShootAngle = Math.atan2(dy, dx);
}
}
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000010
});
/****
* Game Code
****/
// Background
var background = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
game.addChild(background);
// Music
// Sound effects
// Boss
// Power-up
// Enemy bullet
// Enemy
// Player bullet
// Spaceship (player)
// Play background music
LK.playMusic('bgmusic');
// Game variables
var ship;
var playerBullets = [];
var enemies = [];
var enemyBullets = [];
var powerups = [];
var fallingEnemies = [];
var scoreTxt;
var levelTxt;
var dragNode = null;
var lastShipX = 0,
lastShipY = 0;
var spawnTimer = 0;
var powerupTimer = 0;
var level = 1;
var gameOver = false;
// Bullet pools for reusing objects (reduces garbage collection)
var playerBulletPool = [];
var enemyBulletPool = [];
// Helper function to get bullet from pool or create new one
function getPlayerBulletFromPool() {
if (playerBulletPool.length > 0) {
var bullet = playerBulletPool.pop();
bullet.visible = true;
return bullet;
}
return new PlayerBullet();
}
function getEnemyBulletFromPool() {
if (enemyBulletPool.length > 0) {
var bullet = enemyBulletPool.pop();
bullet.visible = true;
return bullet;
}
return new EnemyBullet();
}
// Helper function to return bullet to pool
function returnPlayerBulletToPool(bullet, index) {
bullet.visible = false;
playerBulletPool.push(bullet);
playerBullets.splice(index, 1);
}
function returnEnemyBulletToPool(bullet, index) {
bullet.visible = false;
enemyBulletPool.push(bullet);
enemyBullets.splice(index, 1);
}
// Score display
scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Level display removed
// Create player ship
ship = new Ship();
game.addChild(ship);
ship.x = 2048 / 2;
ship.y = 2732 / 2;
// Make ship visible and interactive
ship.interactive = true;
ship.buttonMode = true;
// Helper: clamp ship inside screen
function clampShipPosition() {
var halfW = ship.width / 2;
var halfH = ship.height / 2;
if (ship.x < 100 + halfW) {
ship.x = 100 + halfW;
}
if (ship.x > 2048 - halfW) {
ship.x = 2048 - halfW;
}
if (ship.y < halfH) {
ship.y = halfH;
}
if (ship.y > 2732 - halfH) {
ship.y = 2732 - halfH;
}
}
// Handle dragging ship
function handleMove(x, y, obj) {
if (dragNode) {
// Direct positioning for immediate response
dragNode.x = x;
dragNode.y = y;
clampShipPosition();
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
// Improved touch detection with slightly larger hitbox for better responsiveness
var local = ship.toLocal(game.toGlobal({
x: x,
y: y
}));
var touchPadding = 20; // Extra padding for easier touch
if (local.x > -ship.width / 2 - touchPadding && local.x < ship.width / 2 + touchPadding && local.y > -ship.height / 2 - touchPadding && local.y < ship.height / 2 + touchPadding) {
dragNode = ship;
// Immediate position update
handleMove(x, y, obj);
}
};
game.up = function (x, y, obj) {
// Make sure position is updated one last time before releasing
if (dragNode) {
handleMove(x, y, obj);
}
dragNode = null;
};
// Spawn enemy
function spawnEnemy() {
var e = new Enemy();
e.x = 2048 + e.width / 2;
e.y = 200 + Math.random() * (2732 - 400);
e.hp = 1 + Math.floor(level / 2);
e.speed = 5 + Math.random() * 4 + level * 0.6;
e.shootCooldown = 90 + Math.floor(Math.random() * 80);
enemies.push(e);
game.addChild(e);
}
// Spawn powerup
function spawnPowerup() {
var p = new Powerup();
p.x = 2048 + 60;
p.y = 200 + Math.random() * (2732 - 400);
p.type = 'power';
powerups.push(p);
game.addChild(p);
}
// Main update loop
game.update = function () {
if (gameOver) {
return;
}
// Ship update
ship.update();
// Auto-fire
if (ship.fireCooldown > 0) {
ship.fireCooldown--;
}
if (ship.fireCooldown <= 0) {
// Fire based on power level, but allow multi-directional shooting
var bulletAngles = [ship.lastShootAngle || 0];
if (ship.powerLevel === 2) {
bulletAngles = [(ship.lastShootAngle || 0) - Math.PI / 16, (ship.lastShootAngle || 0) + Math.PI / 16];
}
if (ship.powerLevel >= 3) {
bulletAngles = [(ship.lastShootAngle || 0) - Math.PI / 8, ship.lastShootAngle || 0, (ship.lastShootAngle || 0) + Math.PI / 8];
}
for (var i = 0; i < bulletAngles.length; ++i) {
var b = getPlayerBulletFromPool();
// Start at ship's nose
b.x = ship.x + Math.cos(bulletAngles[i]) * (ship.width / 2 + 10);
b.y = ship.y + Math.sin(bulletAngles[i]) * (ship.width / 2 + 10);
b.angle = bulletAngles[i];
playerBullets.push(b);
if (!b.parent) {
game.addChild(b);
}
}
LK.getSound('shoot').play();
ship.fireCooldown = 18;
}
// Update player bullets - optimized to reduce collision checking
var bulletsToRemove = [];
for (var i = playerBullets.length - 1; i >= 0; --i) {
var b = playerBullets[i];
b.update();
// Remove if off screen
if (b.x > 2048 + 50 || b.x < -50 || b.y > 2732 + 50 || b.y < -50) {
returnPlayerBulletToPool(b, i);
continue;
}
// Check if bullet already marked for removal
if (bulletsToRemove.indexOf(i) !== -1) {
continue;
}
// Hit enemy - only check enemies in proximity
var bulletHitSomething = false;
for (var j = enemies.length - 1; j >= 0; --j) {
var e = enemies[j];
// Only check collision if enemy is close to bullet (rough proximity check)
if (Math.abs(e.x - b.x) < 150 && Math.abs(e.y - b.y) < 150) {
if (b.intersects(e)) {
e.hp--;
LK.getSound('explosion').play();
// Small hit explosion
var hitExplosion = new Explosion();
hitExplosion.x = b.x;
hitExplosion.y = b.y;
hitExplosion.start(0.5);
game.addChild(hitExplosion);
bulletHitSomething = true;
if (e.hp <= 0) {
// Create explosion at enemy position
var explosion = new Explosion();
explosion.x = e.x;
explosion.y = e.y;
explosion.start(1.2);
game.addChild(explosion);
LK.setScore(LK.getScore() + 10);
scoreTxt.setText(LK.getScore());
// Level progression for endless game
if (LK.getScore() > 0 && LK.getScore() % 100 === 0) {
level++;
// Flash effect
LK.effects.flashScreen(0x00ff00, 800);
// Power up as reward every few levels
if (level % 3 === 0) {
ship.powerLevel = Math.min(ship.powerLevel + 1, 3);
}
}
// Add falling animation instead of destroying immediately
e.isFalling = true;
e.fallSpeed = 2;
e.fallRotation = (Math.random() - 0.5) * 0.2;
e.fallAlpha = 1;
// Remove from enemies array but don't destroy yet
enemies.splice(j, 1);
// Add to separate falling enemies array to be managed separately
if (!fallingEnemies) {
fallingEnemies = [];
}
fallingEnemies.push(e);
}
break;
}
}
}
// If bullet hit an enemy, return the bullet to the pool and continue to next bullet
if (bulletHitSomething) {
returnPlayerBulletToPool(b, i);
continue;
}
}
// Update enemies - optimized
var enemiesNearPlayer = 0;
for (var i = enemies.length - 1; i >= 0; --i) {
var e = enemies[i];
e.update();
// Remove if off screen
if (e.x < -e.width / 2) {
e.destroy();
enemies.splice(i, 1);
continue;
}
// Check if enemy is near player
var isNearPlayer = Math.abs(e.x - ship.x) < 400;
if (isNearPlayer) {
enemiesNearPlayer++;
}
// Enemy shoots
if (e.shootCooldown <= 0 && e.x < 2048 && e.x > 0) {
// Limit shooting frequency based on how many enemies are near player
if (enemiesNearPlayer < 3 || Math.random() < 0.3) {
var eb = getEnemyBulletFromPool();
eb.x = e.x - e.width / 2 - 10;
eb.y = e.y;
enemyBullets.push(eb);
if (!eb.parent) {
game.addChild(eb);
}
LK.getSound('enemyShoot').play();
}
// Increase cooldown when many enemies are present
var cooldownBase = 80 + Math.floor(Math.random() * 60);
e.shootCooldown = cooldownBase * (1 + (enemies.length > 10 ? 0.5 : 0));
}
// Collide with ship - only check when close
if (!ship.invincible && isNearPlayer && Math.abs(e.y - ship.y) < 150) {
if (e.intersects(ship)) {
// Ship hit
// Create explosion at collision point
var explosion = new Explosion();
explosion.x = (ship.x + e.x) / 2;
explosion.y = (ship.y + e.y) / 2;
explosion.start(1);
game.addChild(explosion);
// Game over immediately when player is hit by enemy
gameOver = true;
LK.effects.flashObject(ship, 0xff0000, 600);
LK.effects.flashScreen(0xff0000, 400);
LK.showGameOver();
return;
}
}
}
// Update enemy bullets - batch process with reduced collision checks
var bulletsToCheck = [];
// First update all bullets and mark ones to remove (off-screen)
for (var i = enemyBullets.length - 1; i >= 0; --i) {
var eb = enemyBullets[i];
eb.update();
if (eb.x < -50) {
returnEnemyBulletToPool(eb, i);
continue;
}
// Only check collision if ship is not invincible and bullet is near ship
if (!ship.invincible && Math.abs(eb.x - ship.x) < 150 && Math.abs(eb.y - ship.y) < 150) {
bulletsToCheck.push({
index: i,
bullet: eb
});
}
}
// Now only check collisions for bullets near the ship
if (!ship.invincible) {
for (var i = 0; i < bulletsToCheck.length; i++) {
var bulletInfo = bulletsToCheck[i];
var eb = bulletInfo.bullet;
if (eb.intersects(ship)) {
// Create explosion at bullet position
var explosion = new Explosion();
explosion.x = eb.x;
explosion.y = eb.y;
explosion.start(1);
game.addChild(explosion);
LK.effects.flashObject(ship, 0xff0000, 600);
LK.effects.flashScreen(0xff0000, 400);
eb.destroy();
enemyBullets.splice(bulletInfo.index, 1);
// Game over immediately when player is hit by bullet
gameOver = true;
LK.showGameOver();
return;
break; // Exit loop after first hit
}
}
}
// Update powerups - only check collection when close
for (var i = powerups.length - 1; i >= 0; --i) {
var p = powerups[i];
p.update();
if (p.x < -50) {
p.destroy();
powerups.splice(i, 1);
continue;
}
// Collect - only check intersection when close to ship
if (Math.abs(p.x - ship.x) < 120 && Math.abs(p.y - ship.y) < 120) {
if (p.intersects(ship)) {
LK.getSound('powerup').play();
ship.powerLevel = Math.min(ship.powerLevel + 1, 3);
LK.setScore(LK.getScore() + 30);
scoreTxt.setText(LK.getScore());
p.destroy();
powerups.splice(i, 1);
}
}
}
// Spawning logic - always active in endless mode
spawnTimer--;
if (spawnTimer <= 0) {
// Spawn more enemies at higher levels
var enemiesToSpawn = 1 + Math.floor(level / 5);
for (var i = 0; i < enemiesToSpawn; i++) {
if (enemies.length < 25) {
// Cap max enemies to prevent lag
spawnEnemy();
}
}
spawnTimer = Math.max(30, 100 - level * 3);
}
// Powerup spawn
powerupTimer--;
if (powerupTimer <= 0) {
spawnPowerup();
powerupTimer = 600 + Math.floor(Math.random() * 400);
}
// Increase enemy difficulty with level
if (LK.ticks % 600 === 0) {
for (var i = 0; i < enemies.length; i++) {
// Slightly increase enemy speed based on level
if (Math.random() < 0.3) {
enemies[i].speed += 0.2;
}
}
}
// Update falling enemies
if (fallingEnemies && fallingEnemies.length > 0) {
for (var i = fallingEnemies.length - 1; i >= 0; i--) {
var fe = fallingEnemies[i];
if (fe.isFalling) {
// Add gravity effect
fe.fallSpeed += 0.3;
fe.y += fe.fallSpeed;
// Add rotation
fe.rotation += fe.fallRotation;
// Fade out
fe.fallAlpha -= 0.02;
if (fe.fallAlpha <= 0) {
fe.fallAlpha = 0;
}
fe.alpha = fe.fallAlpha;
// Remove when fallen off screen or fully faded
if (fe.y > 2732 + fe.height || fe.fallAlpha <= 0) {
fe.destroy();
fallingEnemies.splice(i, 1);
}
}
}
}
// No win condition - endless game
};
// Reset game state on restart
game.on('reset', function () {
// Return bullets to pools instead of destroying
for (var i = 0; i < playerBullets.length; ++i) {
playerBullets[i].visible = false;
playerBulletPool.push(playerBullets[i]);
}
for (var i = 0; i < enemyBullets.length; ++i) {
enemyBullets[i].visible = false;
enemyBulletPool.push(enemyBullets[i]);
}
// Destroy other objects
for (var i = 0; i < enemies.length; ++i) {
enemies[i].destroy();
}
// Destroy falling enemies
if (fallingEnemies) {
for (var i = 0; i < fallingEnemies.length; ++i) {
fallingEnemies[i].destroy();
}
}
for (var i = 0; i < powerups.length; ++i) {
powerups[i].destroy();
}
playerBullets = [];
enemies = [];
enemyBullets = [];
powerups = [];
fallingEnemies = [];
level = 1;
ship.x = 2048 / 2; // Center horizontally
ship.y = 2732 / 2;
ship.powerLevel = 1;
ship.invincible = false;
ship.invincibleTimer = 0;
LK.setScore(0);
scoreTxt.setText('0');
spawnTimer = 30;
powerupTimer = 400;
gameOver = false;
});
pufferfish disney 2d image style. In-Game asset. 2d. High contrast. No shadows
clown fish disney 2d image style. In-Game asset. 2d. High contrast. No shadows
oval blue sea buble comic 2d image style. In-Game asset. 2d. High contrast. No shadows
green seaweet comic 2d image style. In-Game asset. 2d. High contrast. No shadows
veryy quite coral reef under sea disney 2d image style. In-Game asset. 2d. High contrast. No shadows