/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// Bullet class
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletSprite = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = bulletSprite.width / 2;
self.vx = 0;
self.vy = 0;
self.lifetime = 60; // frames
self.update = function () {
self.x += self.vx;
self.y += self.vy;
self.lifetime--;
};
return self;
});
// Hero class
var Hero = Container.expand(function () {
var self = Container.call(this);
var heroSprite = self.attachAsset('hero', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = heroSprite.width / 2;
self.speed = 18; // pixels per move
self.lastShotTick = 0;
self.shootCooldown = 18; // frames between shots
self.update = function () {};
return self;
});
// Powerup class
var Powerup = Container.expand(function () {
var self = Container.call(this);
var powerupSprite = self.attachAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = powerupSprite.width / 2;
self.type = 'rapid'; // only one type for MVP
self.update = function () {};
return self;
});
// Zombie class
var Zombie = Container.expand(function () {
var self = Container.call(this);
var zombieSprite = self.attachAsset('zombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = zombieSprite.width / 2;
self.speed = 3 + Math.random() * 2; // will be increased by wave
self.target = null; // set to hero
self.update = function () {
if (!self.target) return;
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
self.x += self.speed * dx / dist;
self.y += self.speed * dy / dist;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181818
});
/****
* Game Code
****/
// Music
// Sound effects
// Powerup: blue ellipse
// Bullet: yellow box
// Zombie: green ellipse
// Hero: red box
// Game area
var GAME_W = 2048;
var GAME_H = 2732;
// State
var hero = null;
var zombies = [];
var bullets = [];
var powerups = [];
var dragNode = null;
var lastIntersecting = false;
var score = 0;
var highScore = storage.highScore || 0;
var wave = 1;
var ticksSinceStart = 0;
var zombieSpawnTick = 0;
var zombieSpawnInterval = 90; // frames
var rapidFireTicks = 0;
// Score text
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// High score text
var highScoreTxt = new Text2('Best: ' + highScore, {
size: 60,
fill: "#fff"
});
highScoreTxt.anchor.set(0.5, 0);
highScoreTxt.y = 120;
LK.gui.top.addChild(highScoreTxt);
// Powerup text
var powerupTxt = new Text2('', {
size: 70,
fill: 0x3B7DD8
});
powerupTxt.anchor.set(0.5, 0);
powerupTxt.y = 200;
LK.gui.top.addChild(powerupTxt);
// Spawn hero in center
hero = new Hero();
hero.x = GAME_W / 2;
hero.y = GAME_H / 2;
game.addChild(hero);
// Helper: spawn zombie at random edge
function spawnZombie() {
var z = new Zombie();
// Pick edge: 0=top,1=bottom,2=left,3=right
var edge = Math.floor(Math.random() * 4);
var margin = 80;
if (edge === 0) {
// top
z.x = margin + Math.random() * (GAME_W - 2 * margin);
z.y = -z.radius;
} else if (edge === 1) {
// bottom
z.x = margin + Math.random() * (GAME_W - 2 * margin);
z.y = GAME_H + z.radius;
} else if (edge === 2) {
// left
z.x = -z.radius;
z.y = margin + Math.random() * (GAME_H - 2 * margin);
} else {
// right
z.x = GAME_W + z.radius;
z.y = margin + Math.random() * (GAME_H - 2 * margin);
}
// Increase speed with wave
z.speed += (wave - 1) * 0.5;
z.target = hero;
zombies.push(z);
game.addChild(z);
}
// Helper: spawn powerup
function spawnPowerup() {
var p = new Powerup();
p.x = 200 + Math.random() * (GAME_W - 400);
p.y = 300 + Math.random() * (GAME_H - 600);
powerups.push(p);
game.addChild(p);
}
// Helper: shoot bullet towards (tx,ty)
function shootBullet(tx, ty) {
if (LK.ticks - hero.lastShotTick < (rapidFireTicks > 0 ? 6 : hero.shootCooldown)) return;
var b = new Bullet();
b.x = hero.x;
b.y = hero.y;
var dx = tx - hero.x;
var dy = ty - hero.y;
var dist = Math.sqrt(dx * dx + dy * dy);
var speed = 38;
b.vx = speed * dx / dist;
b.vy = speed * dy / dist;
bullets.push(b);
game.addChild(b);
hero.lastShotTick = LK.ticks;
LK.getSound('shoot').play();
}
// Helper: distance between two objects
function dist2(a, b) {
var dx = a.x - b.x;
var dy = a.y - b.y;
return Math.sqrt(dx * dx + dy * dy);
}
// Dragging
function handleMove(x, y, obj) {
if (dragNode) {
// Clamp hero inside game area
var r = hero.radius;
dragNode.x = Math.max(r, Math.min(GAME_W - r, x));
dragNode.y = Math.max(r, Math.min(GAME_H - r, y));
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
// Only drag if touch is on hero
if (dist2({
x: x,
y: y
}, hero) <= hero.radius * 1.2) {
dragNode = hero;
}
};
game.up = function (x, y, obj) {
dragNode = null;
// Shoot bullet towards release point if not dragging
if (dist2({
x: x,
y: y
}, hero) > hero.radius * 1.2) {
shootBullet(x, y);
}
};
// Main update loop
game.update = function () {
ticksSinceStart++;
// Increase wave every 900 ticks (~15s)
if (ticksSinceStart % 900 === 0) {
wave++;
if (zombieSpawnInterval > 30) zombieSpawnInterval -= 8;
}
// Spawn zombies
if (LK.ticks - zombieSpawnTick > zombieSpawnInterval) {
spawnZombie();
zombieSpawnTick = LK.ticks;
}
// Maybe spawn powerup
if (powerups.length === 0 && Math.random() < 0.002) {
spawnPowerup();
}
// Update hero (nothing for now)
hero.update();
// Update zombies
for (var i = zombies.length - 1; i >= 0; i--) {
var z = zombies[i];
z.update();
// Check collision with hero
if (dist2(z, hero) < z.radius + hero.radius - 10) {
LK.effects.flashScreen(0xff0000, 1000);
if (score > highScore) {
storage.highScore = score;
}
LK.showGameOver();
return;
}
}
// Update bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var b = bullets[i];
b.update();
// Remove if out of bounds or expired
if (b.x < -100 || b.x > GAME_W + 100 || b.y < -100 || b.y > GAME_H + 100 || b.lifetime <= 0) {
b.destroy();
bullets.splice(i, 1);
continue;
}
// Check collision with zombies
for (var j = zombies.length - 1; j >= 0; j--) {
var z = zombies[j];
if (dist2(b, z) < b.radius + z.radius - 10) {
// Kill zombie
LK.getSound('zombie_die').play();
score++;
scoreTxt.setText(score);
if (score > highScore) {
highScore = score;
highScoreTxt.setText('Best: ' + highScore);
storage.highScore = highScore;
}
// Flash zombie
LK.effects.flashObject(z, 0xffffff, 200);
z.destroy();
zombies.splice(j, 1);
b.destroy();
bullets.splice(i, 1);
break;
}
}
}
// Update powerups
for (var i = powerups.length - 1; i >= 0; i--) {
var p = powerups[i];
// Check collision with hero
if (dist2(p, hero) < p.radius + hero.radius - 10) {
LK.getSound('powerup').play();
rapidFireTicks = 360; // 6 seconds
powerupTxt.setText('Rapid Fire!');
LK.effects.flashObject(hero, 0x3b7dd8, 400);
p.destroy();
powerups.splice(i, 1);
}
}
// Powerup timer
if (rapidFireTicks > 0) {
rapidFireTicks--;
if (rapidFireTicks === 0) {
powerupTxt.setText('');
}
}
// Update powerup text alpha for effect
if (rapidFireTicks > 0) {
powerupTxt.alpha = 0.7 + 0.3 * Math.sin(LK.ticks / 6);
} else {
powerupTxt.alpha = 1;
}
};
// Start music
LK.playMusic('bgmusic', {
fade: {
start: 0,
end: 0.7,
duration: 1200
}
}); ===================================================================
--- original.js
+++ change.js
@@ -1,6 +1,327 @@
-/****
+/****
+* Plugins
+****/
+var tween = LK.import("@upit/tween.v1");
+var storage = LK.import("@upit/storage.v1");
+
+/****
+* Classes
+****/
+// Bullet class
+var Bullet = Container.expand(function () {
+ var self = Container.call(this);
+ var bulletSprite = self.attachAsset('bullet', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.radius = bulletSprite.width / 2;
+ self.vx = 0;
+ self.vy = 0;
+ self.lifetime = 60; // frames
+ self.update = function () {
+ self.x += self.vx;
+ self.y += self.vy;
+ self.lifetime--;
+ };
+ return self;
+});
+// Hero class
+var Hero = Container.expand(function () {
+ var self = Container.call(this);
+ var heroSprite = self.attachAsset('hero', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.radius = heroSprite.width / 2;
+ self.speed = 18; // pixels per move
+ self.lastShotTick = 0;
+ self.shootCooldown = 18; // frames between shots
+ self.update = function () {};
+ return self;
+});
+// Powerup class
+var Powerup = Container.expand(function () {
+ var self = Container.call(this);
+ var powerupSprite = self.attachAsset('powerup', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.radius = powerupSprite.width / 2;
+ self.type = 'rapid'; // only one type for MVP
+ self.update = function () {};
+ return self;
+});
+// Zombie class
+var Zombie = Container.expand(function () {
+ var self = Container.call(this);
+ var zombieSprite = self.attachAsset('zombie', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.radius = zombieSprite.width / 2;
+ self.speed = 3 + Math.random() * 2; // will be increased by wave
+ self.target = null; // set to hero
+ self.update = function () {
+ if (!self.target) return;
+ var dx = self.target.x - self.x;
+ var dy = self.target.y - self.y;
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist > 0) {
+ self.x += self.speed * dx / dist;
+ self.y += self.speed * dy / dist;
+ }
+ };
+ return self;
+});
+
+/****
* Initialize Game
-****/
+****/
var game = new LK.Game({
- backgroundColor: 0x000000
+ backgroundColor: 0x181818
+});
+
+/****
+* Game Code
+****/
+// Music
+// Sound effects
+// Powerup: blue ellipse
+// Bullet: yellow box
+// Zombie: green ellipse
+// Hero: red box
+// Game area
+var GAME_W = 2048;
+var GAME_H = 2732;
+// State
+var hero = null;
+var zombies = [];
+var bullets = [];
+var powerups = [];
+var dragNode = null;
+var lastIntersecting = false;
+var score = 0;
+var highScore = storage.highScore || 0;
+var wave = 1;
+var ticksSinceStart = 0;
+var zombieSpawnTick = 0;
+var zombieSpawnInterval = 90; // frames
+var rapidFireTicks = 0;
+// Score text
+var scoreTxt = new Text2('0', {
+ size: 120,
+ fill: "#fff"
+});
+scoreTxt.anchor.set(0.5, 0);
+LK.gui.top.addChild(scoreTxt);
+// High score text
+var highScoreTxt = new Text2('Best: ' + highScore, {
+ size: 60,
+ fill: "#fff"
+});
+highScoreTxt.anchor.set(0.5, 0);
+highScoreTxt.y = 120;
+LK.gui.top.addChild(highScoreTxt);
+// Powerup text
+var powerupTxt = new Text2('', {
+ size: 70,
+ fill: 0x3B7DD8
+});
+powerupTxt.anchor.set(0.5, 0);
+powerupTxt.y = 200;
+LK.gui.top.addChild(powerupTxt);
+// Spawn hero in center
+hero = new Hero();
+hero.x = GAME_W / 2;
+hero.y = GAME_H / 2;
+game.addChild(hero);
+// Helper: spawn zombie at random edge
+function spawnZombie() {
+ var z = new Zombie();
+ // Pick edge: 0=top,1=bottom,2=left,3=right
+ var edge = Math.floor(Math.random() * 4);
+ var margin = 80;
+ if (edge === 0) {
+ // top
+ z.x = margin + Math.random() * (GAME_W - 2 * margin);
+ z.y = -z.radius;
+ } else if (edge === 1) {
+ // bottom
+ z.x = margin + Math.random() * (GAME_W - 2 * margin);
+ z.y = GAME_H + z.radius;
+ } else if (edge === 2) {
+ // left
+ z.x = -z.radius;
+ z.y = margin + Math.random() * (GAME_H - 2 * margin);
+ } else {
+ // right
+ z.x = GAME_W + z.radius;
+ z.y = margin + Math.random() * (GAME_H - 2 * margin);
+ }
+ // Increase speed with wave
+ z.speed += (wave - 1) * 0.5;
+ z.target = hero;
+ zombies.push(z);
+ game.addChild(z);
+}
+// Helper: spawn powerup
+function spawnPowerup() {
+ var p = new Powerup();
+ p.x = 200 + Math.random() * (GAME_W - 400);
+ p.y = 300 + Math.random() * (GAME_H - 600);
+ powerups.push(p);
+ game.addChild(p);
+}
+// Helper: shoot bullet towards (tx,ty)
+function shootBullet(tx, ty) {
+ if (LK.ticks - hero.lastShotTick < (rapidFireTicks > 0 ? 6 : hero.shootCooldown)) return;
+ var b = new Bullet();
+ b.x = hero.x;
+ b.y = hero.y;
+ var dx = tx - hero.x;
+ var dy = ty - hero.y;
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ var speed = 38;
+ b.vx = speed * dx / dist;
+ b.vy = speed * dy / dist;
+ bullets.push(b);
+ game.addChild(b);
+ hero.lastShotTick = LK.ticks;
+ LK.getSound('shoot').play();
+}
+// Helper: distance between two objects
+function dist2(a, b) {
+ var dx = a.x - b.x;
+ var dy = a.y - b.y;
+ return Math.sqrt(dx * dx + dy * dy);
+}
+// Dragging
+function handleMove(x, y, obj) {
+ if (dragNode) {
+ // Clamp hero inside game area
+ var r = hero.radius;
+ dragNode.x = Math.max(r, Math.min(GAME_W - r, x));
+ dragNode.y = Math.max(r, Math.min(GAME_H - r, y));
+ }
+}
+game.move = handleMove;
+game.down = function (x, y, obj) {
+ // Only drag if touch is on hero
+ if (dist2({
+ x: x,
+ y: y
+ }, hero) <= hero.radius * 1.2) {
+ dragNode = hero;
+ }
+};
+game.up = function (x, y, obj) {
+ dragNode = null;
+ // Shoot bullet towards release point if not dragging
+ if (dist2({
+ x: x,
+ y: y
+ }, hero) > hero.radius * 1.2) {
+ shootBullet(x, y);
+ }
+};
+// Main update loop
+game.update = function () {
+ ticksSinceStart++;
+ // Increase wave every 900 ticks (~15s)
+ if (ticksSinceStart % 900 === 0) {
+ wave++;
+ if (zombieSpawnInterval > 30) zombieSpawnInterval -= 8;
+ }
+ // Spawn zombies
+ if (LK.ticks - zombieSpawnTick > zombieSpawnInterval) {
+ spawnZombie();
+ zombieSpawnTick = LK.ticks;
+ }
+ // Maybe spawn powerup
+ if (powerups.length === 0 && Math.random() < 0.002) {
+ spawnPowerup();
+ }
+ // Update hero (nothing for now)
+ hero.update();
+ // Update zombies
+ for (var i = zombies.length - 1; i >= 0; i--) {
+ var z = zombies[i];
+ z.update();
+ // Check collision with hero
+ if (dist2(z, hero) < z.radius + hero.radius - 10) {
+ LK.effects.flashScreen(0xff0000, 1000);
+ if (score > highScore) {
+ storage.highScore = score;
+ }
+ LK.showGameOver();
+ return;
+ }
+ }
+ // Update bullets
+ for (var i = bullets.length - 1; i >= 0; i--) {
+ var b = bullets[i];
+ b.update();
+ // Remove if out of bounds or expired
+ if (b.x < -100 || b.x > GAME_W + 100 || b.y < -100 || b.y > GAME_H + 100 || b.lifetime <= 0) {
+ b.destroy();
+ bullets.splice(i, 1);
+ continue;
+ }
+ // Check collision with zombies
+ for (var j = zombies.length - 1; j >= 0; j--) {
+ var z = zombies[j];
+ if (dist2(b, z) < b.radius + z.radius - 10) {
+ // Kill zombie
+ LK.getSound('zombie_die').play();
+ score++;
+ scoreTxt.setText(score);
+ if (score > highScore) {
+ highScore = score;
+ highScoreTxt.setText('Best: ' + highScore);
+ storage.highScore = highScore;
+ }
+ // Flash zombie
+ LK.effects.flashObject(z, 0xffffff, 200);
+ z.destroy();
+ zombies.splice(j, 1);
+ b.destroy();
+ bullets.splice(i, 1);
+ break;
+ }
+ }
+ }
+ // Update powerups
+ for (var i = powerups.length - 1; i >= 0; i--) {
+ var p = powerups[i];
+ // Check collision with hero
+ if (dist2(p, hero) < p.radius + hero.radius - 10) {
+ LK.getSound('powerup').play();
+ rapidFireTicks = 360; // 6 seconds
+ powerupTxt.setText('Rapid Fire!');
+ LK.effects.flashObject(hero, 0x3b7dd8, 400);
+ p.destroy();
+ powerups.splice(i, 1);
+ }
+ }
+ // Powerup timer
+ if (rapidFireTicks > 0) {
+ rapidFireTicks--;
+ if (rapidFireTicks === 0) {
+ powerupTxt.setText('');
+ }
+ }
+ // Update powerup text alpha for effect
+ if (rapidFireTicks > 0) {
+ powerupTxt.alpha = 0.7 + 0.3 * Math.sin(LK.ticks / 6);
+ } else {
+ powerupTxt.alpha = 1;
+ }
+};
+// Start music
+LK.playMusic('bgmusic', {
+ fade: {
+ start: 0,
+ end: 0.7,
+ duration: 1200
+ }
});
\ No newline at end of file
zombie. In-Game asset. 2d. High contrast. No shadows
bullet. In-Game asset. 2d. High contrast. No shadows
power up. In-Game asset. 2d. High contrast. No shadows
pistol. In-Game asset. 2d. High contrast. No shadows
crawler zombie. In-Game asset. 2d. High contrast. No shadows
runner zombie. In-Game asset. 2d. High contrast. No shadows
tank zombie. In-Game asset. 2d. High contrast. No shadows
man. In-Game asset. 2d. High contrast. No shadows