/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Obstacle var Obstacle = Container.expand(function () { var self = Container.call(this); var obsAsset = self.attachAsset('obstacle', { anchorX: 0.5, anchorY: 0.5 }); self.width = obsAsset.width; self.height = obsAsset.height; self.speed = 0; self.lane = 1; self.update = function () { self.y += self.speed; }; return self; }); // Player Car var PlayerCar = Container.expand(function () { var self = Container.call(this); var carAsset = self.attachAsset('car', { anchorX: 0.5, anchorY: 0.5 }); self.width = carAsset.width; self.height = carAsset.height; self.lane = 1; // 0: left, 1: center, 2: right self.invincible = false; self.invincibleTimer = 0; // Flash effect for invincibility self.setInvincible = function (duration) { self.invincible = true; self.invincibleTimer = LK.ticks + Math.floor(duration * 60 / 1000); tween(self, { alpha: 0.5 }, { duration: 100, easing: tween.linear, onFinish: function onFinish() { tween(self, { alpha: 1 }, { duration: 100, easing: tween.linear }); } }); }; self.update = function () { if (self.invincible && LK.ticks > self.invincibleTimer) { self.invincible = false; self.alpha = 1; } }; return self; }); // PowerUp var PowerUp = Container.expand(function () { var self = Container.call(this); var puAsset = self.attachAsset('powerup', { anchorX: 0.5, anchorY: 0.5 }); self.width = puAsset.width; self.height = puAsset.height; self.speed = 0; self.lane = 1; self.update = function () { self.y += self.speed; }; return self; }); // Rival Car var RivalCar = Container.expand(function () { var self = Container.call(this); var rivalAsset = self.attachAsset('rival', { anchorX: 0.5, anchorY: 0.5 }); self.width = rivalAsset.width; self.height = rivalAsset.height; self.speed = 0; self.lane = 1; self.update = function () { self.y += self.speed; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x222222 }); /**** * Game Code ****/ // Lane positions (3 lanes) // Car (player) // Rival car // Obstacle (barrier) // Power-up (star) // Road lane var laneCount = 3; var laneWidth = 400; var laneX = [2048 / 2 - laneWidth, // left 2048 / 2, // center 2048 / 2 + laneWidth // right ]; // Draw road lanes (visual only) for (var i = 0; i < laneCount + 1; i++) { var laneLine = LK.getAsset('lane', { anchorX: 0.5, anchorY: 0, x: 2048 / 2 - laneWidth * 1.5 + i * laneWidth, y: 0, scaleY: 7 }); laneLine.alpha = 0.2; game.addChild(laneLine); } // Player car var player = new PlayerCar(); player.x = laneX[1]; player.y = 2732 - 400; player.lane = 1; game.addChild(player); // Score var score = 0; var scoreTxt = new Text2('0', { size: 120, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Power-up timer text var powerupTxt = new Text2('', { size: 70, fill: 0xFFE082 }); powerupTxt.anchor.set(0.5, 0); LK.gui.top.addChild(powerupTxt); powerupTxt.y = 130; // Game state var rivals = []; var obstacles = []; var powerups = []; var gameSpeed = 18; // pixels per frame var spawnTimer = 0; var powerupActive = false; var powerupEndTick = 0; var dragNode = null; var lastTouchX = 0; // Helper: Clamp lane function clampLane(lane) { if (lane < 0) return 0; if (lane > laneCount - 1) return laneCount - 1; return lane; } // Helper: Move player to x function movePlayerToX(x) { // Find closest lane var minDist = 99999; var bestLane = 1; for (var i = 0; i < laneCount; i++) { var dist = Math.abs(x - laneX[i]); if (dist < minDist) { minDist = dist; bestLane = i; } } player.lane = bestLane; // Tween to lane center tween.stop(player, { x: true }); tween(player, { x: laneX[bestLane] }, { duration: 120, easing: tween.cubicOut }); } // Touch/drag controls game.down = function (x, y, obj) { // Only allow drag if touch is on player car or below it if (y >= player.y - player.height / 2 && y <= player.y + player.height / 2) { dragNode = player; lastTouchX = x; } }; game.move = function (x, y, obj) { if (dragNode === player) { // Move car horizontally, snap to nearest lane movePlayerToX(x); lastTouchX = x; } }; game.up = function (x, y, obj) { dragNode = null; }; // Spawning logic function spawnRival() { var rival = new RivalCar(); rival.lane = Math.floor(Math.random() * laneCount); rival.x = laneX[rival.lane]; rival.y = -rival.height; rival.speed = gameSpeed + Math.random() * 4; rival.lastY = rival.y; rivals.push(rival); game.addChild(rival); } function spawnObstacle() { var obs = new Obstacle(); obs.lane = Math.floor(Math.random() * laneCount); obs.x = laneX[obs.lane]; obs.y = -obs.height; obs.speed = gameSpeed; obstacles.push(obs); game.addChild(obs); } function spawnPowerUp() { var pu = new PowerUp(); pu.lane = Math.floor(Math.random() * laneCount); pu.x = laneX[pu.lane]; pu.y = -pu.height; pu.speed = gameSpeed; powerups.push(pu); game.addChild(pu); } // Main update loop game.update = function () { // Increase score over time if (LK.ticks % 6 === 0) { score += 1; scoreTxt.setText(score); // Win condition: reach 500 points if (score >= 500) { LK.showYouWin(); return; } } // Power-up timer if (powerupActive) { var ticksLeft = powerupEndTick - LK.ticks; if (ticksLeft > 0) { powerupTxt.setText('Invincible: ' + Math.ceil(ticksLeft / 60) + 's'); } else { powerupActive = false; player.invincible = false; player.alpha = 1; powerupTxt.setText(''); } } // Player update (invincibility) player.update(); // Spawn rivals, obstacles, powerups if (spawnTimer <= 0) { var r = Math.random(); if (r < 0.45) { spawnRival(); } else if (r < 0.8) { spawnObstacle(); } else { spawnPowerUp(); } spawnTimer = 36 + Math.floor(Math.random() * 30); // ~0.6s } else { spawnTimer--; } // Update rivals for (var i = rivals.length - 1; i >= 0; i--) { var rival = rivals[i]; if (rival.lastY === undefined) rival.lastY = rival.y; // Track lastY for overtaking rival.update(); // Remove if off screen if (rival.y > 2732 + rival.height) { rival.destroy(); rivals.splice(i, 1); continue; } // Collision with player if (rival.intersects(player)) { if (!player.invincible) { LK.effects.flashScreen(0xff0000, 800); LK.showGameOver(); return; } else { // Remove rival rival.destroy(); rivals.splice(i, 1); continue; } } // Overtake detection: player passes rival (player.y < rival.y, and was not before) if (rival.lastY < player.y && rival.y >= player.y) { score += 10; scoreTxt.setText(score); } rival.lastY = rival.y; } // Update obstacles for (var i = obstacles.length - 1; i >= 0; i--) { var obs = obstacles[i]; obs.update(); if (obs.y > 2732 + obs.height) { obs.destroy(); obstacles.splice(i, 1); continue; } if (obs.intersects(player)) { if (!player.invincible) { LK.effects.flashScreen(0xff0000, 800); LK.showGameOver(); return; } else { obs.destroy(); obstacles.splice(i, 1); continue; } } } // Update powerups for (var i = powerups.length - 1; i >= 0; i--) { var pu = powerups[i]; pu.update(); if (pu.y > 2732 + pu.height) { pu.destroy(); powerups.splice(i, 1); continue; } if (pu.intersects(player)) { // Activate invincibility for 3 seconds powerupActive = true; powerupEndTick = LK.ticks + 180; player.setInvincible(3000); powerupTxt.setText('Invincible: 3s'); // Flash player car yellow for feedback LK.effects.flashObject(player, 0xffeb3b, 600); // Play power-up sound LK.getSound('shoot').play(); pu.destroy(); powerups.splice(i, 1); continue; } } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Obstacle
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obsAsset = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = obsAsset.width;
self.height = obsAsset.height;
self.speed = 0;
self.lane = 1;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Player Car
var PlayerCar = Container.expand(function () {
var self = Container.call(this);
var carAsset = self.attachAsset('car', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = carAsset.width;
self.height = carAsset.height;
self.lane = 1; // 0: left, 1: center, 2: right
self.invincible = false;
self.invincibleTimer = 0;
// Flash effect for invincibility
self.setInvincible = function (duration) {
self.invincible = true;
self.invincibleTimer = LK.ticks + Math.floor(duration * 60 / 1000);
tween(self, {
alpha: 0.5
}, {
duration: 100,
easing: tween.linear,
onFinish: function onFinish() {
tween(self, {
alpha: 1
}, {
duration: 100,
easing: tween.linear
});
}
});
};
self.update = function () {
if (self.invincible && LK.ticks > self.invincibleTimer) {
self.invincible = false;
self.alpha = 1;
}
};
return self;
});
// PowerUp
var PowerUp = Container.expand(function () {
var self = Container.call(this);
var puAsset = self.attachAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = puAsset.width;
self.height = puAsset.height;
self.speed = 0;
self.lane = 1;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Rival Car
var RivalCar = Container.expand(function () {
var self = Container.call(this);
var rivalAsset = self.attachAsset('rival', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = rivalAsset.width;
self.height = rivalAsset.height;
self.speed = 0;
self.lane = 1;
self.update = function () {
self.y += self.speed;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// Lane positions (3 lanes)
// Car (player)
// Rival car
// Obstacle (barrier)
// Power-up (star)
// Road lane
var laneCount = 3;
var laneWidth = 400;
var laneX = [2048 / 2 - laneWidth,
// left
2048 / 2,
// center
2048 / 2 + laneWidth // right
];
// Draw road lanes (visual only)
for (var i = 0; i < laneCount + 1; i++) {
var laneLine = LK.getAsset('lane', {
anchorX: 0.5,
anchorY: 0,
x: 2048 / 2 - laneWidth * 1.5 + i * laneWidth,
y: 0,
scaleY: 7
});
laneLine.alpha = 0.2;
game.addChild(laneLine);
}
// Player car
var player = new PlayerCar();
player.x = laneX[1];
player.y = 2732 - 400;
player.lane = 1;
game.addChild(player);
// Score
var score = 0;
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Power-up timer text
var powerupTxt = new Text2('', {
size: 70,
fill: 0xFFE082
});
powerupTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(powerupTxt);
powerupTxt.y = 130;
// Game state
var rivals = [];
var obstacles = [];
var powerups = [];
var gameSpeed = 18; // pixels per frame
var spawnTimer = 0;
var powerupActive = false;
var powerupEndTick = 0;
var dragNode = null;
var lastTouchX = 0;
// Helper: Clamp lane
function clampLane(lane) {
if (lane < 0) return 0;
if (lane > laneCount - 1) return laneCount - 1;
return lane;
}
// Helper: Move player to x
function movePlayerToX(x) {
// Find closest lane
var minDist = 99999;
var bestLane = 1;
for (var i = 0; i < laneCount; i++) {
var dist = Math.abs(x - laneX[i]);
if (dist < minDist) {
minDist = dist;
bestLane = i;
}
}
player.lane = bestLane;
// Tween to lane center
tween.stop(player, {
x: true
});
tween(player, {
x: laneX[bestLane]
}, {
duration: 120,
easing: tween.cubicOut
});
}
// Touch/drag controls
game.down = function (x, y, obj) {
// Only allow drag if touch is on player car or below it
if (y >= player.y - player.height / 2 && y <= player.y + player.height / 2) {
dragNode = player;
lastTouchX = x;
}
};
game.move = function (x, y, obj) {
if (dragNode === player) {
// Move car horizontally, snap to nearest lane
movePlayerToX(x);
lastTouchX = x;
}
};
game.up = function (x, y, obj) {
dragNode = null;
};
// Spawning logic
function spawnRival() {
var rival = new RivalCar();
rival.lane = Math.floor(Math.random() * laneCount);
rival.x = laneX[rival.lane];
rival.y = -rival.height;
rival.speed = gameSpeed + Math.random() * 4;
rival.lastY = rival.y;
rivals.push(rival);
game.addChild(rival);
}
function spawnObstacle() {
var obs = new Obstacle();
obs.lane = Math.floor(Math.random() * laneCount);
obs.x = laneX[obs.lane];
obs.y = -obs.height;
obs.speed = gameSpeed;
obstacles.push(obs);
game.addChild(obs);
}
function spawnPowerUp() {
var pu = new PowerUp();
pu.lane = Math.floor(Math.random() * laneCount);
pu.x = laneX[pu.lane];
pu.y = -pu.height;
pu.speed = gameSpeed;
powerups.push(pu);
game.addChild(pu);
}
// Main update loop
game.update = function () {
// Increase score over time
if (LK.ticks % 6 === 0) {
score += 1;
scoreTxt.setText(score);
// Win condition: reach 500 points
if (score >= 500) {
LK.showYouWin();
return;
}
}
// Power-up timer
if (powerupActive) {
var ticksLeft = powerupEndTick - LK.ticks;
if (ticksLeft > 0) {
powerupTxt.setText('Invincible: ' + Math.ceil(ticksLeft / 60) + 's');
} else {
powerupActive = false;
player.invincible = false;
player.alpha = 1;
powerupTxt.setText('');
}
}
// Player update (invincibility)
player.update();
// Spawn rivals, obstacles, powerups
if (spawnTimer <= 0) {
var r = Math.random();
if (r < 0.45) {
spawnRival();
} else if (r < 0.8) {
spawnObstacle();
} else {
spawnPowerUp();
}
spawnTimer = 36 + Math.floor(Math.random() * 30); // ~0.6s
} else {
spawnTimer--;
}
// Update rivals
for (var i = rivals.length - 1; i >= 0; i--) {
var rival = rivals[i];
if (rival.lastY === undefined) rival.lastY = rival.y; // Track lastY for overtaking
rival.update();
// Remove if off screen
if (rival.y > 2732 + rival.height) {
rival.destroy();
rivals.splice(i, 1);
continue;
}
// Collision with player
if (rival.intersects(player)) {
if (!player.invincible) {
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
return;
} else {
// Remove rival
rival.destroy();
rivals.splice(i, 1);
continue;
}
}
// Overtake detection: player passes rival (player.y < rival.y, and was not before)
if (rival.lastY < player.y && rival.y >= player.y) {
score += 10;
scoreTxt.setText(score);
}
rival.lastY = rival.y;
}
// Update obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
var obs = obstacles[i];
obs.update();
if (obs.y > 2732 + obs.height) {
obs.destroy();
obstacles.splice(i, 1);
continue;
}
if (obs.intersects(player)) {
if (!player.invincible) {
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
return;
} else {
obs.destroy();
obstacles.splice(i, 1);
continue;
}
}
}
// Update powerups
for (var i = powerups.length - 1; i >= 0; i--) {
var pu = powerups[i];
pu.update();
if (pu.y > 2732 + pu.height) {
pu.destroy();
powerups.splice(i, 1);
continue;
}
if (pu.intersects(player)) {
// Activate invincibility for 3 seconds
powerupActive = true;
powerupEndTick = LK.ticks + 180;
player.setInvincible(3000);
powerupTxt.setText('Invincible: 3s');
// Flash player car yellow for feedback
LK.effects.flashObject(player, 0xffeb3b, 600);
// Play power-up sound
LK.getSound('shoot').play();
pu.destroy();
powerups.splice(i, 1);
continue;
}
}
};