/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Car class var Car = Container.expand(function () { var self = Container.call(this); var carSprite = self.attachAsset('car', { anchorX: 0.5, anchorY: 0.5 }); // For possible future effects self.flash = function () { LK.effects.flashObject(self, 0xffffff, 200); }; return self; }); // LaneLine class (for visual effect) var LaneLine = Container.expand(function () { var self = Container.call(this); var lineSprite = self.attachAsset('laneLine', { anchorX: 0.5, anchorY: 0.5 }); return self; }); // Note class var Note = Container.expand(function () { var self = Container.call(this); // Pick a random note asset var assetIdx = Math.floor(Math.random() * NOTE_ASSET_IDS.length); var noteSprite = self.attachAsset(NOTE_ASSET_IDS[assetIdx], { anchorX: 0.5, anchorY: 0.5 }); // Lane index (0,1,2) self.lane = 1; // For state tracking self.lastY = undefined; self.lastIntersecting = false; return self; }); // Obstacle class var Obstacle = Container.expand(function () { var self = Container.call(this); var obsSprite = self.attachAsset('obstacle', { anchorX: 0.5, anchorY: 0.5 }); // Lane index (0,1,2) self.lane = 1; // For state tracking self.lastY = undefined; self.lastIntersecting = false; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x0a0020 // Deep synthwave night }); /**** * Game Code ****/ // unique id // unique id // unique id // unique id // unique id // unique id // unique id // Sounds and music (IDs are placeholders, engine will load as used) // Neon synthwave colors // --- Game constants --- // Note class var NOTE_ASSET_IDS = ['note', // 0 'note1', // 1 'note2', // 2 'note3', // 3 'note4', // 4 'note5', // 5 'note6', // 6 'note7', // 7 'note8' // 8 ]; var NUM_LANES = 3; var LANE_WIDTH = 410; // 2048 / 3 ≈ 682, but leave margins for neon effect var ROAD_TOP = 400; var ROAD_BOTTOM = 2732 - 200; var CAR_Y = 2200; var BASE_OBSTACLE_SPEED = 22; // px per frame var BASE_NOTE_SPEED = 22; var OBSTACLE_SPEED = BASE_OBSTACLE_SPEED; var NOTE_SPEED = BASE_NOTE_SPEED; var LANE_X = [410, // left 1024, // center 1638 // right ]; // --- Music BPM and spawn sync --- var BPM = 120; // Example BPM, can be changed if music changes var BASE_BEAT_INTERVAL = Math.round(60 / BPM * 60); // frames per beat (60fps) var BEAT_INTERVAL = BASE_BEAT_INTERVAL; var lastBeatTick = 0; // --- Game state --- var car = null; var carLane = 1; // 0: left, 1: center, 2: right var obstacles = []; var notes = []; var laneLines = []; var score = 0; var scoreTxt = null; var dragSide = null; // 'left' or 'right' for touch controls // --- Background music --- LK.playMusic('neonTrack'); // --- Draw neon road lanes --- function createLaneLines() { // Remove old lines for (var i = 0; i < laneLines.length; i++) { laneLines[i].destroy(); } laneLines = []; // Draw vertical lines between lanes for (var i = 1; i < NUM_LANES; i++) { var x = (LANE_X[i - 1] + LANE_X[i]) / 2; for (var y = ROAD_TOP; y < ROAD_BOTTOM; y += 400) { var line = new LaneLine(); line.x = x; line.y = y; line.alpha = 0.25; game.addChild(line); laneLines.push(line); } } } createLaneLines(); // --- Create car --- car = new Car(); car.x = LANE_X[carLane]; car.y = CAR_Y; game.addChild(car); // --- Score display --- scoreTxt = new Text2('0', { size: 120, fill: 0xFFF200, font: "Impact" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // --- Touch controls --- // Touch/click left or right half of screen to move car function handleDown(x, y, obj) { // Only respond to touches below the top 200px (avoid menu) if (y < 200) return; // Expand touch logic: if tap is left of car center, go left; if right, go right // If tap is very close to car center (±80px), do nothing (prevents accidental moves) var carCenterX = car.x; if (x < carCenterX - 80) { dragSide = 'left'; moveCar(-1); } else if (x > carCenterX + 80) { dragSide = 'right'; moveCar(1); } else { dragSide = null; } } function handleUp(x, y, obj) { dragSide = null; } function handleMove(x, y, obj) { // Optional: swipe to move, but for now, tap only } game.down = handleDown; game.up = handleUp; game.move = handleMove; // --- Move car between lanes --- function moveCar(dir) { var newLane = carLane + dir; if (newLane < 0) newLane = 0; if (newLane > 2) newLane = 2; if (newLane !== carLane) { carLane = newLane; // Animate car to new lane tween(car, { x: LANE_X[carLane] }, { duration: 120, easing: tween.cubicOut }); } } // --- Spawn obstacles and notes in sync with music --- function spawnBeatObjects() { // Randomly decide: 1 or 2 obstacles, and 0 or 1 note var availableLanes = [0, 1, 2]; // Place 1 or 2 obstacles var numObstacles = Math.random() < 0.5 ? 1 : 2; for (var i = 0; i < numObstacles; i++) { if (availableLanes.length === 0) break; var idx = Math.floor(Math.random() * availableLanes.length); var lane = availableLanes[idx]; availableLanes.splice(idx, 1); var obs = new Obstacle(); obs.lane = lane; obs.x = LANE_X[lane]; obs.y = ROAD_TOP - 100; obs.lastY = obs.y; obs.lastIntersecting = false; obstacles.push(obs); game.addChild(obs); } // Place a note in a random free lane (if any) if (availableLanes.length > 0 && Math.random() < 0.7) { var idx = Math.floor(Math.random() * availableLanes.length); var lane = availableLanes[idx]; var note = new Note(); note.lane = lane; note.x = LANE_X[lane]; note.y = ROAD_TOP - 100; note.lastY = note.y; note.lastIntersecting = false; notes.push(note); game.addChild(note); } } // --- Main game update loop --- game.update = function () { // --- Dynamic difficulty: first 20 points easy, then increase every 20 points --- var currentScore = LK.getScore(); var difficultyLevel = 0; if (currentScore < 20) { difficultyLevel = 0; } else { difficultyLevel = Math.floor((currentScore - 20) / 20) + 1; } OBSTACLE_SPEED = BASE_OBSTACLE_SPEED + difficultyLevel * 4; NOTE_SPEED = BASE_NOTE_SPEED + difficultyLevel * 4; // Minimum beat interval: don't go below 12 frames (~0.2s at 60fps) BEAT_INTERVAL = Math.max(BASE_BEAT_INTERVAL - difficultyLevel * 8, 12); // Animate lane lines for neon effect for (var i = 0; i < laneLines.length; i++) { laneLines[i].y += OBSTACLE_SPEED; if (laneLines[i].y > ROAD_BOTTOM + 200) { laneLines[i].y = ROAD_TOP - 200; } } // Spawn obstacles/notes on beat if (LK.ticks - lastBeatTick >= BEAT_INTERVAL) { spawnBeatObjects(); lastBeatTick = LK.ticks; } // Move obstacles for (var i = obstacles.length - 1; i >= 0; i--) { var obs = obstacles[i]; obs.y += OBSTACLE_SPEED; // Off-screen removal if (obs.lastY < 2800 && obs.y >= 2800) { obs.destroy(); obstacles.splice(i, 1); continue; } // Collision detection var isIntersecting = obs.intersects(car); if (!obs.lastIntersecting && isIntersecting) { // Crash! LK.effects.flashScreen(0xff2a6d, 800); car.flash(); LK.showGameOver(); return; } obs.lastY = obs.y; obs.lastIntersecting = isIntersecting; } // Move notes for (var i = notes.length - 1; i >= 0; i--) { var note = notes[i]; note.y += NOTE_SPEED; // Off-screen removal if (note.lastY < 2800 && note.y >= 2800) { note.destroy(); notes.splice(i, 1); continue; } // Collect note var isIntersecting = note.intersects(car); if (!note.lastIntersecting && isIntersecting) { // Collect! LK.setScore(LK.getScore() + 1); scoreTxt.setText(LK.getScore()); LK.getSound('noteCollect').play(); // Neon flash LK.effects.flashObject(car, 0xfff200, 200); note.destroy(); notes.splice(i, 1); continue; } note.lastY = note.y; note.lastIntersecting = isIntersecting; } }; // --- Set initial score --- LK.setScore(0); scoreTxt.setText('0');
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Car class
var Car = Container.expand(function () {
var self = Container.call(this);
var carSprite = self.attachAsset('car', {
anchorX: 0.5,
anchorY: 0.5
});
// For possible future effects
self.flash = function () {
LK.effects.flashObject(self, 0xffffff, 200);
};
return self;
});
// LaneLine class (for visual effect)
var LaneLine = Container.expand(function () {
var self = Container.call(this);
var lineSprite = self.attachAsset('laneLine', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
// Note class
var Note = Container.expand(function () {
var self = Container.call(this);
// Pick a random note asset
var assetIdx = Math.floor(Math.random() * NOTE_ASSET_IDS.length);
var noteSprite = self.attachAsset(NOTE_ASSET_IDS[assetIdx], {
anchorX: 0.5,
anchorY: 0.5
});
// Lane index (0,1,2)
self.lane = 1;
// For state tracking
self.lastY = undefined;
self.lastIntersecting = false;
return self;
});
// Obstacle class
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obsSprite = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
// Lane index (0,1,2)
self.lane = 1;
// For state tracking
self.lastY = undefined;
self.lastIntersecting = false;
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x0a0020 // Deep synthwave night
});
/****
* Game Code
****/
// unique id
// unique id
// unique id
// unique id
// unique id
// unique id
// unique id
// Sounds and music (IDs are placeholders, engine will load as used)
// Neon synthwave colors
// --- Game constants ---
// Note class
var NOTE_ASSET_IDS = ['note',
// 0
'note1',
// 1
'note2',
// 2
'note3',
// 3
'note4',
// 4
'note5',
// 5
'note6',
// 6
'note7',
// 7
'note8' // 8
];
var NUM_LANES = 3;
var LANE_WIDTH = 410; // 2048 / 3 ≈ 682, but leave margins for neon effect
var ROAD_TOP = 400;
var ROAD_BOTTOM = 2732 - 200;
var CAR_Y = 2200;
var BASE_OBSTACLE_SPEED = 22; // px per frame
var BASE_NOTE_SPEED = 22;
var OBSTACLE_SPEED = BASE_OBSTACLE_SPEED;
var NOTE_SPEED = BASE_NOTE_SPEED;
var LANE_X = [410,
// left
1024,
// center
1638 // right
];
// --- Music BPM and spawn sync ---
var BPM = 120; // Example BPM, can be changed if music changes
var BASE_BEAT_INTERVAL = Math.round(60 / BPM * 60); // frames per beat (60fps)
var BEAT_INTERVAL = BASE_BEAT_INTERVAL;
var lastBeatTick = 0;
// --- Game state ---
var car = null;
var carLane = 1; // 0: left, 1: center, 2: right
var obstacles = [];
var notes = [];
var laneLines = [];
var score = 0;
var scoreTxt = null;
var dragSide = null; // 'left' or 'right' for touch controls
// --- Background music ---
LK.playMusic('neonTrack');
// --- Draw neon road lanes ---
function createLaneLines() {
// Remove old lines
for (var i = 0; i < laneLines.length; i++) {
laneLines[i].destroy();
}
laneLines = [];
// Draw vertical lines between lanes
for (var i = 1; i < NUM_LANES; i++) {
var x = (LANE_X[i - 1] + LANE_X[i]) / 2;
for (var y = ROAD_TOP; y < ROAD_BOTTOM; y += 400) {
var line = new LaneLine();
line.x = x;
line.y = y;
line.alpha = 0.25;
game.addChild(line);
laneLines.push(line);
}
}
}
createLaneLines();
// --- Create car ---
car = new Car();
car.x = LANE_X[carLane];
car.y = CAR_Y;
game.addChild(car);
// --- Score display ---
scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFF200,
font: "Impact"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// --- Touch controls ---
// Touch/click left or right half of screen to move car
function handleDown(x, y, obj) {
// Only respond to touches below the top 200px (avoid menu)
if (y < 200) return;
// Expand touch logic: if tap is left of car center, go left; if right, go right
// If tap is very close to car center (±80px), do nothing (prevents accidental moves)
var carCenterX = car.x;
if (x < carCenterX - 80) {
dragSide = 'left';
moveCar(-1);
} else if (x > carCenterX + 80) {
dragSide = 'right';
moveCar(1);
} else {
dragSide = null;
}
}
function handleUp(x, y, obj) {
dragSide = null;
}
function handleMove(x, y, obj) {
// Optional: swipe to move, but for now, tap only
}
game.down = handleDown;
game.up = handleUp;
game.move = handleMove;
// --- Move car between lanes ---
function moveCar(dir) {
var newLane = carLane + dir;
if (newLane < 0) newLane = 0;
if (newLane > 2) newLane = 2;
if (newLane !== carLane) {
carLane = newLane;
// Animate car to new lane
tween(car, {
x: LANE_X[carLane]
}, {
duration: 120,
easing: tween.cubicOut
});
}
}
// --- Spawn obstacles and notes in sync with music ---
function spawnBeatObjects() {
// Randomly decide: 1 or 2 obstacles, and 0 or 1 note
var availableLanes = [0, 1, 2];
// Place 1 or 2 obstacles
var numObstacles = Math.random() < 0.5 ? 1 : 2;
for (var i = 0; i < numObstacles; i++) {
if (availableLanes.length === 0) break;
var idx = Math.floor(Math.random() * availableLanes.length);
var lane = availableLanes[idx];
availableLanes.splice(idx, 1);
var obs = new Obstacle();
obs.lane = lane;
obs.x = LANE_X[lane];
obs.y = ROAD_TOP - 100;
obs.lastY = obs.y;
obs.lastIntersecting = false;
obstacles.push(obs);
game.addChild(obs);
}
// Place a note in a random free lane (if any)
if (availableLanes.length > 0 && Math.random() < 0.7) {
var idx = Math.floor(Math.random() * availableLanes.length);
var lane = availableLanes[idx];
var note = new Note();
note.lane = lane;
note.x = LANE_X[lane];
note.y = ROAD_TOP - 100;
note.lastY = note.y;
note.lastIntersecting = false;
notes.push(note);
game.addChild(note);
}
}
// --- Main game update loop ---
game.update = function () {
// --- Dynamic difficulty: first 20 points easy, then increase every 20 points ---
var currentScore = LK.getScore();
var difficultyLevel = 0;
if (currentScore < 20) {
difficultyLevel = 0;
} else {
difficultyLevel = Math.floor((currentScore - 20) / 20) + 1;
}
OBSTACLE_SPEED = BASE_OBSTACLE_SPEED + difficultyLevel * 4;
NOTE_SPEED = BASE_NOTE_SPEED + difficultyLevel * 4;
// Minimum beat interval: don't go below 12 frames (~0.2s at 60fps)
BEAT_INTERVAL = Math.max(BASE_BEAT_INTERVAL - difficultyLevel * 8, 12);
// Animate lane lines for neon effect
for (var i = 0; i < laneLines.length; i++) {
laneLines[i].y += OBSTACLE_SPEED;
if (laneLines[i].y > ROAD_BOTTOM + 200) {
laneLines[i].y = ROAD_TOP - 200;
}
}
// Spawn obstacles/notes on beat
if (LK.ticks - lastBeatTick >= BEAT_INTERVAL) {
spawnBeatObjects();
lastBeatTick = LK.ticks;
}
// Move obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
var obs = obstacles[i];
obs.y += OBSTACLE_SPEED;
// Off-screen removal
if (obs.lastY < 2800 && obs.y >= 2800) {
obs.destroy();
obstacles.splice(i, 1);
continue;
}
// Collision detection
var isIntersecting = obs.intersects(car);
if (!obs.lastIntersecting && isIntersecting) {
// Crash!
LK.effects.flashScreen(0xff2a6d, 800);
car.flash();
LK.showGameOver();
return;
}
obs.lastY = obs.y;
obs.lastIntersecting = isIntersecting;
}
// Move notes
for (var i = notes.length - 1; i >= 0; i--) {
var note = notes[i];
note.y += NOTE_SPEED;
// Off-screen removal
if (note.lastY < 2800 && note.y >= 2800) {
note.destroy();
notes.splice(i, 1);
continue;
}
// Collect note
var isIntersecting = note.intersects(car);
if (!note.lastIntersecting && isIntersecting) {
// Collect!
LK.setScore(LK.getScore() + 1);
scoreTxt.setText(LK.getScore());
LK.getSound('noteCollect').play();
// Neon flash
LK.effects.flashObject(car, 0xfff200, 200);
note.destroy();
notes.splice(i, 1);
continue;
}
note.lastY = note.y;
note.lastIntersecting = isIntersecting;
}
};
// --- Set initial score ---
LK.setScore(0);
scoreTxt.setText('0');