/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Enemy Car var EnemyCar = Container.expand(function () { var self = Container.call(this); var car = self.attachAsset('enemyCar', { anchorX: 0.5, anchorY: 0.5 }); self.width = car.width; self.height = car.height; self.speed = 0; self.lane = 0; self.update = function () { self.y += self.speed; }; return self; }); // Lane Mark var LaneMark = Container.expand(function () { var self = Container.call(this); var mark = self.attachAsset('laneMark', { anchorX: 0.5, anchorY: 0.5 }); self.width = mark.width; self.height = mark.height; self.speed = 0; self.update = function () { self.y += self.speed; }; return self; }); // Obstacle (barrier) var Obstacle = Container.expand(function () { var self = Container.call(this); var obs = self.attachAsset('obstacle', { anchorX: 0.5, anchorY: 0.5 }); self.width = obs.width; self.height = obs.height; self.speed = 0; self.update = function () { self.y += self.speed; }; return self; }); // Player Car var PlayerCar = Container.expand(function () { var self = Container.call(this); var car = self.attachAsset('playerCar', { anchorX: 0.5, anchorY: 0.5 }); self.width = car.width; self.height = car.height; // For near-miss detection self.lastNearMissTick = 0; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x222831 }); /**** * Game Code ****/ // --- Road Lane Marks --- LK.playMusic('sound'); // Obstacle (barrier) // Enemy car // Car (player) // --- Game Constants --- var GAME_WIDTH = 2048; var GAME_HEIGHT = 2732; var ROAD_WIDTH = 1200; var ROAD_LEFT = (GAME_WIDTH - ROAD_WIDTH) / 2; var ROAD_RIGHT = ROAD_LEFT + ROAD_WIDTH; var LANE_COUNT = 4; var LANE_WIDTH = ROAD_WIDTH / LANE_COUNT; var PLAYER_START_LANE = 1; var PLAYER_Y = GAME_HEIGHT - 500; var ENEMY_MIN_SPEED = 18; var ENEMY_MAX_SPEED = 32; var OBSTACLE_SPEED = 24; var LANE_MARK_SPEED = 28; var LANE_MARK_SPACING = 320; var NEAR_MISS_DIST = 120; var NEAR_MISS_SCORE = 5; // --- Game State --- var playerCar; var enemyCars = []; var obstacles = []; var laneMarks = []; var dragNode = null; var dragOffsetX = 0; var score = 0; var scoreTxt; var distance = 0; var distanceTxt; var lastEnemySpawnTick = 0; var lastObstacleSpawnTick = 0; var lastNearMissTick = 0; var speedMultiplier = 1; var gameOver = false; // --- GUI --- scoreTxt = new Text2('0', { size: 120, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); distanceTxt = new Text2('0 m', { size: 80, fill: "#fff" }); distanceTxt.anchor.set(0.5, 0); LK.gui.top.addChild(distanceTxt); distanceTxt.y = 130; // --- Road Lane Marks --- function spawnLaneMarks() { for (var lane = 0; lane < LANE_COUNT + 1; lane++) { var x = ROAD_LEFT + lane * LANE_WIDTH; for (var y = -LANE_MARK_SPACING; y < GAME_HEIGHT + LANE_MARK_SPACING; y += LANE_MARK_SPACING * 2) { var mark = new LaneMark(); mark.x = x; mark.y = y; mark.speed = LANE_MARK_SPEED; laneMarks.push(mark); game.addChild(mark); } } } spawnLaneMarks(); // --- Player Car --- playerCar = new PlayerCar(); playerCar.x = ROAD_LEFT + (PLAYER_START_LANE + 0.5) * LANE_WIDTH; playerCar.y = PLAYER_Y; game.addChild(playerCar); // --- Helper: Get nearest lane center for a given x --- function getNearestLaneCenter(x) { var lane = Math.floor((x - ROAD_LEFT) / LANE_WIDTH); if (lane < 0) lane = 0; if (lane >= LANE_COUNT) lane = LANE_COUNT - 1; return ROAD_LEFT + (lane + 0.5) * LANE_WIDTH; } // --- Helper: Clamp player within road --- function clampPlayerX(x) { var half = playerCar.width / 2; var minX = ROAD_LEFT + half; var maxX = ROAD_RIGHT - half; if (x < minX) x = minX; if (x > maxX) x = maxX; return x; } // --- Touch/Drag Controls --- game.down = function (x, y, obj) { // Only start drag if touch is on player car var local = playerCar.toLocal(game.toGlobal({ x: x, y: y })); if (local.x > -playerCar.width / 2 && local.x < playerCar.width / 2 && local.y > -playerCar.height / 2 && local.y < playerCar.height / 2) { dragNode = playerCar; dragOffsetX = playerCar.x - x; } }; game.move = function (x, y, obj) { if (dragNode) { var newX = clampPlayerX(x + dragOffsetX); playerCar.x = newX; } }; game.up = function (x, y, obj) { dragNode = null; }; // --- Enemy Car Spawning --- function spawnEnemyCar() { // Pick a random lane var lane = Math.floor(Math.random() * LANE_COUNT); var x = ROAD_LEFT + (lane + 0.5) * LANE_WIDTH; var y = -200; var car = new EnemyCar(); car.x = x; car.y = y; car.lane = lane; car.speed = ENEMY_MIN_SPEED + Math.random() * (ENEMY_MAX_SPEED - ENEMY_MIN_SPEED) * speedMultiplier; enemyCars.push(car); game.addChild(car); } // --- Obstacle Spawning --- function spawnObstacle() { // Pick a random lane var lane = Math.floor(Math.random() * LANE_COUNT); var x = ROAD_LEFT + (lane + 0.5) * LANE_WIDTH; var y = -100; var obs = new Obstacle(); obs.x = x; obs.y = y; obs.speed = OBSTACLE_SPEED * speedMultiplier; obstacles.push(obs); game.addChild(obs); } // --- Main Game Loop --- game.update = function () { if (gameOver) return; // --- Lane Marks --- for (var i = laneMarks.length - 1; i >= 0; i--) { var mark = laneMarks[i]; mark.y += mark.speed * speedMultiplier; if (mark.y > GAME_HEIGHT + LANE_MARK_SPACING) { mark.y -= GAME_HEIGHT + LANE_MARK_SPACING * 2; } } // --- Enemy Cars --- for (var i = enemyCars.length - 1; i >= 0; i--) { var car = enemyCars[i]; car.y += car.speed * speedMultiplier; // Remove if off screen if (car.y > GAME_HEIGHT + 400) { car.destroy(); enemyCars.splice(i, 1); continue; } // Collision with player if (car.intersects(playerCar)) { LK.getSound('21334535').play(); LK.effects.flashScreen(0xff0000, 800); gameOver = true; LK.showGameOver(); return; } // Near-miss detection (side-by-side, close y, but not colliding) var dx = Math.abs(car.x - playerCar.x); var dy = Math.abs(car.y - playerCar.y); if (dx > playerCar.width * 0.7 && dy < NEAR_MISS_DIST && LK.ticks - playerCar.lastNearMissTick > 30) { score += NEAR_MISS_SCORE; playerCar.lastNearMissTick = LK.ticks; LK.effects.flashObject(playerCar, 0x00ff00, 200); } } // --- Obstacles --- for (var i = obstacles.length - 1; i >= 0; i--) { var obs = obstacles[i]; obs.y += obs.speed * speedMultiplier; if (obs.y > GAME_HEIGHT + 200) { obs.destroy(); obstacles.splice(i, 1); continue; } if (obs.intersects(playerCar)) { LK.getSound('21334535').play(); LK.effects.flashScreen(0xff0000, 800); gameOver = true; LK.showGameOver(); return; } } // --- Spawning Enemies --- if (LK.ticks - lastEnemySpawnTick > Math.max(32 - speedMultiplier * 8, 12)) { spawnEnemyCar(); lastEnemySpawnTick = LK.ticks; } // --- Spawning Obstacles --- if (LK.ticks - lastObstacleSpawnTick > 120 + Math.floor(Math.random() * 80)) { if (Math.random() < 0.35) { spawnObstacle(); } lastObstacleSpawnTick = LK.ticks; } // --- Score & Distance --- distance += 1.5 * speedMultiplier; score += Math.floor(0.08 * speedMultiplier); scoreTxt.setText(score); distanceTxt.setText(Math.floor(distance) + " m"); // --- Difficulty Scaling --- if (LK.ticks % 180 == 0 && speedMultiplier < 2.5) { speedMultiplier += 0.04; } }; // --- Reset on Game Over --- game.on('destroy', function () { // Clean up arrays for (var i = 0; i < enemyCars.length; i++) enemyCars[i].destroy(); for (var i = 0; i < obstacles.length; i++) obstacles[i].destroy(); for (var i = 0; i < laneMarks.length; i++) laneMarks[i].destroy(); enemyCars = []; obstacles = []; laneMarks = []; score = 0; distance = 0; speedMultiplier = 1; gameOver = false; // Recreate lane marks and player spawnLaneMarks(); playerCar.x = ROAD_LEFT + (PLAYER_START_LANE + 0.5) * LANE_WIDTH; playerCar.y = PLAYER_Y; scoreTxt.setText('0'); distanceTxt.setText('0 m'); });
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Enemy Car
var EnemyCar = Container.expand(function () {
var self = Container.call(this);
var car = self.attachAsset('enemyCar', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = car.width;
self.height = car.height;
self.speed = 0;
self.lane = 0;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Lane Mark
var LaneMark = Container.expand(function () {
var self = Container.call(this);
var mark = self.attachAsset('laneMark', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = mark.width;
self.height = mark.height;
self.speed = 0;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Obstacle (barrier)
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obs = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = obs.width;
self.height = obs.height;
self.speed = 0;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Player Car
var PlayerCar = Container.expand(function () {
var self = Container.call(this);
var car = self.attachAsset('playerCar', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = car.width;
self.height = car.height;
// For near-miss detection
self.lastNearMissTick = 0;
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222831
});
/****
* Game Code
****/
// --- Road Lane Marks ---
LK.playMusic('sound');
// Obstacle (barrier)
// Enemy car
// Car (player)
// --- Game Constants ---
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var ROAD_WIDTH = 1200;
var ROAD_LEFT = (GAME_WIDTH - ROAD_WIDTH) / 2;
var ROAD_RIGHT = ROAD_LEFT + ROAD_WIDTH;
var LANE_COUNT = 4;
var LANE_WIDTH = ROAD_WIDTH / LANE_COUNT;
var PLAYER_START_LANE = 1;
var PLAYER_Y = GAME_HEIGHT - 500;
var ENEMY_MIN_SPEED = 18;
var ENEMY_MAX_SPEED = 32;
var OBSTACLE_SPEED = 24;
var LANE_MARK_SPEED = 28;
var LANE_MARK_SPACING = 320;
var NEAR_MISS_DIST = 120;
var NEAR_MISS_SCORE = 5;
// --- Game State ---
var playerCar;
var enemyCars = [];
var obstacles = [];
var laneMarks = [];
var dragNode = null;
var dragOffsetX = 0;
var score = 0;
var scoreTxt;
var distance = 0;
var distanceTxt;
var lastEnemySpawnTick = 0;
var lastObstacleSpawnTick = 0;
var lastNearMissTick = 0;
var speedMultiplier = 1;
var gameOver = false;
// --- GUI ---
scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
distanceTxt = new Text2('0 m', {
size: 80,
fill: "#fff"
});
distanceTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(distanceTxt);
distanceTxt.y = 130;
// --- Road Lane Marks ---
function spawnLaneMarks() {
for (var lane = 0; lane < LANE_COUNT + 1; lane++) {
var x = ROAD_LEFT + lane * LANE_WIDTH;
for (var y = -LANE_MARK_SPACING; y < GAME_HEIGHT + LANE_MARK_SPACING; y += LANE_MARK_SPACING * 2) {
var mark = new LaneMark();
mark.x = x;
mark.y = y;
mark.speed = LANE_MARK_SPEED;
laneMarks.push(mark);
game.addChild(mark);
}
}
}
spawnLaneMarks();
// --- Player Car ---
playerCar = new PlayerCar();
playerCar.x = ROAD_LEFT + (PLAYER_START_LANE + 0.5) * LANE_WIDTH;
playerCar.y = PLAYER_Y;
game.addChild(playerCar);
// --- Helper: Get nearest lane center for a given x ---
function getNearestLaneCenter(x) {
var lane = Math.floor((x - ROAD_LEFT) / LANE_WIDTH);
if (lane < 0) lane = 0;
if (lane >= LANE_COUNT) lane = LANE_COUNT - 1;
return ROAD_LEFT + (lane + 0.5) * LANE_WIDTH;
}
// --- Helper: Clamp player within road ---
function clampPlayerX(x) {
var half = playerCar.width / 2;
var minX = ROAD_LEFT + half;
var maxX = ROAD_RIGHT - half;
if (x < minX) x = minX;
if (x > maxX) x = maxX;
return x;
}
// --- Touch/Drag Controls ---
game.down = function (x, y, obj) {
// Only start drag if touch is on player car
var local = playerCar.toLocal(game.toGlobal({
x: x,
y: y
}));
if (local.x > -playerCar.width / 2 && local.x < playerCar.width / 2 && local.y > -playerCar.height / 2 && local.y < playerCar.height / 2) {
dragNode = playerCar;
dragOffsetX = playerCar.x - x;
}
};
game.move = function (x, y, obj) {
if (dragNode) {
var newX = clampPlayerX(x + dragOffsetX);
playerCar.x = newX;
}
};
game.up = function (x, y, obj) {
dragNode = null;
};
// --- Enemy Car Spawning ---
function spawnEnemyCar() {
// Pick a random lane
var lane = Math.floor(Math.random() * LANE_COUNT);
var x = ROAD_LEFT + (lane + 0.5) * LANE_WIDTH;
var y = -200;
var car = new EnemyCar();
car.x = x;
car.y = y;
car.lane = lane;
car.speed = ENEMY_MIN_SPEED + Math.random() * (ENEMY_MAX_SPEED - ENEMY_MIN_SPEED) * speedMultiplier;
enemyCars.push(car);
game.addChild(car);
}
// --- Obstacle Spawning ---
function spawnObstacle() {
// Pick a random lane
var lane = Math.floor(Math.random() * LANE_COUNT);
var x = ROAD_LEFT + (lane + 0.5) * LANE_WIDTH;
var y = -100;
var obs = new Obstacle();
obs.x = x;
obs.y = y;
obs.speed = OBSTACLE_SPEED * speedMultiplier;
obstacles.push(obs);
game.addChild(obs);
}
// --- Main Game Loop ---
game.update = function () {
if (gameOver) return;
// --- Lane Marks ---
for (var i = laneMarks.length - 1; i >= 0; i--) {
var mark = laneMarks[i];
mark.y += mark.speed * speedMultiplier;
if (mark.y > GAME_HEIGHT + LANE_MARK_SPACING) {
mark.y -= GAME_HEIGHT + LANE_MARK_SPACING * 2;
}
}
// --- Enemy Cars ---
for (var i = enemyCars.length - 1; i >= 0; i--) {
var car = enemyCars[i];
car.y += car.speed * speedMultiplier;
// Remove if off screen
if (car.y > GAME_HEIGHT + 400) {
car.destroy();
enemyCars.splice(i, 1);
continue;
}
// Collision with player
if (car.intersects(playerCar)) {
LK.getSound('21334535').play();
LK.effects.flashScreen(0xff0000, 800);
gameOver = true;
LK.showGameOver();
return;
}
// Near-miss detection (side-by-side, close y, but not colliding)
var dx = Math.abs(car.x - playerCar.x);
var dy = Math.abs(car.y - playerCar.y);
if (dx > playerCar.width * 0.7 && dy < NEAR_MISS_DIST && LK.ticks - playerCar.lastNearMissTick > 30) {
score += NEAR_MISS_SCORE;
playerCar.lastNearMissTick = LK.ticks;
LK.effects.flashObject(playerCar, 0x00ff00, 200);
}
}
// --- Obstacles ---
for (var i = obstacles.length - 1; i >= 0; i--) {
var obs = obstacles[i];
obs.y += obs.speed * speedMultiplier;
if (obs.y > GAME_HEIGHT + 200) {
obs.destroy();
obstacles.splice(i, 1);
continue;
}
if (obs.intersects(playerCar)) {
LK.getSound('21334535').play();
LK.effects.flashScreen(0xff0000, 800);
gameOver = true;
LK.showGameOver();
return;
}
}
// --- Spawning Enemies ---
if (LK.ticks - lastEnemySpawnTick > Math.max(32 - speedMultiplier * 8, 12)) {
spawnEnemyCar();
lastEnemySpawnTick = LK.ticks;
}
// --- Spawning Obstacles ---
if (LK.ticks - lastObstacleSpawnTick > 120 + Math.floor(Math.random() * 80)) {
if (Math.random() < 0.35) {
spawnObstacle();
}
lastObstacleSpawnTick = LK.ticks;
}
// --- Score & Distance ---
distance += 1.5 * speedMultiplier;
score += Math.floor(0.08 * speedMultiplier);
scoreTxt.setText(score);
distanceTxt.setText(Math.floor(distance) + " m");
// --- Difficulty Scaling ---
if (LK.ticks % 180 == 0 && speedMultiplier < 2.5) {
speedMultiplier += 0.04;
}
};
// --- Reset on Game Over ---
game.on('destroy', function () {
// Clean up arrays
for (var i = 0; i < enemyCars.length; i++) enemyCars[i].destroy();
for (var i = 0; i < obstacles.length; i++) obstacles[i].destroy();
for (var i = 0; i < laneMarks.length; i++) laneMarks[i].destroy();
enemyCars = [];
obstacles = [];
laneMarks = [];
score = 0;
distance = 0;
speedMultiplier = 1;
gameOver = false;
// Recreate lane marks and player
spawnLaneMarks();
playerCar.x = ROAD_LEFT + (PLAYER_START_LANE + 0.5) * LANE_WIDTH;
playerCar.y = PLAYER_Y;
scoreTxt.setText('0');
distanceTxt.setText('0 m');
});