/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Obstacle = Container.expand(function (lane, distance) { var self = Container.call(this); var graphics = self.attachAsset('obstacle', { anchorX: 0.5, anchorY: 1 }); self.lane = lane; self.distance = distance; self.update = function () { self.x = 100 + self.distance / raceDistance * 1800; }; return self; }); var Runner = Container.expand(function (color, lane) { var self = Container.call(this); var graphics = self.attachAsset(color, { anchorX: 0.5, anchorY: 1 }); self.speed = 0; self.maxSpeed = self.isPlayer ? 14 : 12; // Higher max speed for player self.lane = lane; self.distance = 0; self.isJumping = false; self.jumpHeight = 0; self.isPlayer = false; self.update = function () { if (gameState !== 'racing') return; // Handle jumping - graphics position updated by jumpHeight if (self.isJumping) { graphics.y = -self.jumpHeight; } // Move forward self.distance += self.speed; self.x = 100 + self.distance / raceDistance * 1800; // Check for obstacles if (!self.isJumping && self.jumpHeight === 0) { for (var i = 0; i < obstacles.length; i++) { var obs = obstacles[i]; if (obs.lane === self.lane && Math.abs(self.distance - obs.distance) < 50) { // Hit obstacle self.speed *= 0.3; break; } } } }; self.jump = function () { if (!self.isJumping && self.jumpHeight === 0) { self.isJumping = true; self.jumpHeight = 0; LK.getSound('jump').play(); // Use tween to animate jump - go up then down over 0.5 seconds total tween(self, { jumpHeight: 100 }, { duration: 250, easing: tween.easeOut, onFinish: function onFinish() { // After reaching peak, tween back down tween(self, { jumpHeight: 0 }, { duration: 250, easing: tween.easeIn, onFinish: function onFinish() { self.isJumping = false; } }); } }); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87CEEB }); /**** * Game Code ****/ // Game variables var gameState = 'tutorial'; // tutorial, ready, racing, finished var currentRound = 1; var raceDistance = 2000; var runners = []; var obstacles = []; var playerRunner; var tapCount = 0; var lastTapTime = 0; var raceStartTime = 0; // UI Elements var roundText = new Text2('Round 1', { size: 80, fill: 0x000000 }); roundText.anchor.set(0.5, 0); LK.gui.top.addChild(roundText); roundText.y = 100; var speedText = new Text2('Speed: 0', { size: 60, fill: 0x000000 }); speedText.anchor.set(0, 0); LK.gui.left.addChild(speedText); speedText.x = 20; speedText.y = -300; var instructionText = new Text2('TAP TO SPRINT!', { size: 100, fill: 0xFF0000 }); instructionText.anchor.set(0.5, 0.5); LK.gui.center.addChild(instructionText); instructionText.x = 0; instructionText.y = 200; // Tutorial text explaining controls var tutorialText = new Text2('TAP SCREEN TO RUN FASTER\nTAP UPPER AREA TO JUMP OVER OBSTACLES', { size: 60, fill: 0x000000 }); tutorialText.anchor.set(0.5, 0.5); LK.gui.center.addChild(tutorialText); tutorialText.x = 0; tutorialText.y = -100; // Continue text var continueText = new Text2('TAP TO CONTINUE', { size: 50, fill: 0x666666 }); continueText.anchor.set(0.5, 0.5); LK.gui.center.addChild(continueText); continueText.x = 0; continueText.y = 400; var positionText = new Text2('Position: 5/5', { size: 60, fill: 0x000000 }); positionText.anchor.set(1, 0); LK.gui.topRight.addChild(positionText); positionText.x = -20; positionText.y = 150; // Create stadium background var stadium = game.addChild(LK.getAsset('stadium', { anchorX: 0, anchorY: 0 })); stadium.x = 0; stadium.y = 0; // Create track var trackY = 1500; var track = game.addChild(LK.getAsset('track', { anchorX: 0, anchorY: 0.5 })); track.x = 0; track.y = trackY; // Create lane lines for (var i = 1; i < 5; i++) { var lane = game.addChild(LK.getAsset('lane', { anchorX: 0, anchorY: 0.5 })); lane.x = 0; lane.y = trackY - 150 + i * 60; } // Create finish line var finishLine = game.addChild(LK.getAsset('finish', { anchorX: 0.5, anchorY: 0.5 })); finishLine.x = 1950; finishLine.y = trackY; function initializeRace() { // Clear previous runners and obstacles for (var i = 0; i < runners.length; i++) { runners[i].destroy(); } for (var j = 0; j < obstacles.length; j++) { obstacles[j].destroy(); } runners = []; obstacles = []; // Create runners var colors = ['player', 'ai1', 'ai2', 'ai3', 'ai4']; for (var k = 0; k < 5; k++) { var runner = new Runner(colors[k], k); runner.x = 100; runner.y = trackY - 120 + k * 60; if (k === 0) { runner.isPlayer = true; runner.maxSpeed = 14; // Set higher max speed for player playerRunner = runner; } else { // Set random base speed for each AI runner that will persist this round runner.baseSpeed = (3 + Math.random() * 2) * 1.22; // Random base speed between 3.66-6.1 (1.22x faster) runner.maxSpeed = (runner.baseSpeed + 1 + Math.random() * 1) * 1.22; // Max speed based on base speed (1.22x faster) } runners.push(runner); game.addChild(runner); } // Create obstacles for rounds 2+ if (currentRound >= 2) { var obstacleCount = Math.min(currentRound, 5); var usedLanes = []; for (var l = 0; l < obstacleCount; l++) { var obsLane; // Keep trying random lanes until we find one that's not used do { obsLane = Math.floor(Math.random() * 5); } while (usedLanes.indexOf(obsLane) !== -1); usedLanes.push(obsLane); var obsDistance = 800 + Math.random() * 800; var obstacle = new Obstacle(obsLane, obsDistance); obstacle.y = trackY - 120 + obsLane * 60; obstacles.push(obstacle); game.addChild(obstacle); } } if (currentRound === 1) { gameState = 'tutorial'; tutorialText.visible = true; continueText.visible = true; instructionText.visible = false; } else { gameState = 'ready'; tutorialText.visible = false; continueText.visible = false; instructionText.visible = true; } tapCount = 0; roundText.setText('Round ' + currentRound); } function startRace() { gameState = 'racing'; raceStartTime = LK.ticks; instructionText.visible = false; } function updatePlayerSpeed() { var currentTime = LK.ticks; var timeSinceLastTap = currentTime - lastTapTime; if (timeSinceLastTap > 30) { // Decrease speed if not tapping playerRunner.speed *= 0.95; } if (playerRunner.speed > playerRunner.maxSpeed) { playerRunner.speed = playerRunner.maxSpeed; } speedText.setText('Speed: ' + Math.floor(playerRunner.speed)); } function updateAI() { for (var i = 1; i < runners.length; i++) { var aiRunner = runners[i]; // Use consistent base speed for this round with small random variations var roundBonus = currentRound * 0.2; var targetSpeed = (aiRunner.baseSpeed + roundBonus) * 1.22; // Add small random variation (±10%) to make movement feel natural var variation = targetSpeed * (0.8 + Math.random() * 0.2); aiRunner.speed = variation; // Cap speed at the runner's max speed if (aiRunner.speed > aiRunner.maxSpeed) { aiRunner.speed = aiRunner.maxSpeed; } // AI obstacle avoidance for (var j = 0; j < obstacles.length; j++) { var obs = obstacles[j]; if (obs.lane === aiRunner.lane && Math.abs(aiRunner.distance - obs.distance) < 150 && Math.random() < 0.7) { aiRunner.jump(); break; } } } } function updatePositions() { var sortedRunners = runners.slice().sort(function (a, b) { return b.distance - a.distance; }); var playerPosition = sortedRunners.indexOf(playerRunner) + 1; positionText.setText('Position: ' + playerPosition + '/5'); } function checkRaceEnd() { var finishedRunners = []; for (var i = 0; i < runners.length; i++) { if (runners[i].distance >= raceDistance) { finishedRunners.push(runners[i]); } } if (finishedRunners.length > 0) { gameState = 'finished'; // Sort by distance to determine winner finishedRunners.sort(function (a, b) { return b.distance - a.distance; }); if (finishedRunners[0] === playerRunner) { // Player won currentRound++; LK.setScore(currentRound - 1); LK.setTimeout(function () { initializeRace(); }, 2000); } else { // Player lost LK.setTimeout(function () { LK.showGameOver(); }, 2000); } } } // Touch controls game.down = function (x, y, obj) { if (gameState === 'tutorial') { // Move from tutorial to ready state gameState = 'ready'; tutorialText.visible = false; continueText.visible = false; instructionText.visible = true; } if (gameState === 'ready') { startRace(); } if (gameState === 'racing') { tapCount++; lastTapTime = LK.ticks; // Increase player speed playerRunner.speed += 0.8; // Jump if tapping in upper screen area if (y < 1200) { playerRunner.jump(); } LK.getSound('tap').play(); } }; // Game update loop game.update = function () { if (gameState === 'racing') { updatePlayerSpeed(); updateAI(); updatePositions(); checkRaceEnd(); } }; // Initialize first race initializeRace();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Obstacle = Container.expand(function (lane, distance) {
var self = Container.call(this);
var graphics = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 1
});
self.lane = lane;
self.distance = distance;
self.update = function () {
self.x = 100 + self.distance / raceDistance * 1800;
};
return self;
});
var Runner = Container.expand(function (color, lane) {
var self = Container.call(this);
var graphics = self.attachAsset(color, {
anchorX: 0.5,
anchorY: 1
});
self.speed = 0;
self.maxSpeed = self.isPlayer ? 14 : 12; // Higher max speed for player
self.lane = lane;
self.distance = 0;
self.isJumping = false;
self.jumpHeight = 0;
self.isPlayer = false;
self.update = function () {
if (gameState !== 'racing') return;
// Handle jumping - graphics position updated by jumpHeight
if (self.isJumping) {
graphics.y = -self.jumpHeight;
}
// Move forward
self.distance += self.speed;
self.x = 100 + self.distance / raceDistance * 1800;
// Check for obstacles
if (!self.isJumping && self.jumpHeight === 0) {
for (var i = 0; i < obstacles.length; i++) {
var obs = obstacles[i];
if (obs.lane === self.lane && Math.abs(self.distance - obs.distance) < 50) {
// Hit obstacle
self.speed *= 0.3;
break;
}
}
}
};
self.jump = function () {
if (!self.isJumping && self.jumpHeight === 0) {
self.isJumping = true;
self.jumpHeight = 0;
LK.getSound('jump').play();
// Use tween to animate jump - go up then down over 0.5 seconds total
tween(self, {
jumpHeight: 100
}, {
duration: 250,
easing: tween.easeOut,
onFinish: function onFinish() {
// After reaching peak, tween back down
tween(self, {
jumpHeight: 0
}, {
duration: 250,
easing: tween.easeIn,
onFinish: function onFinish() {
self.isJumping = false;
}
});
}
});
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
// Game variables
var gameState = 'tutorial'; // tutorial, ready, racing, finished
var currentRound = 1;
var raceDistance = 2000;
var runners = [];
var obstacles = [];
var playerRunner;
var tapCount = 0;
var lastTapTime = 0;
var raceStartTime = 0;
// UI Elements
var roundText = new Text2('Round 1', {
size: 80,
fill: 0x000000
});
roundText.anchor.set(0.5, 0);
LK.gui.top.addChild(roundText);
roundText.y = 100;
var speedText = new Text2('Speed: 0', {
size: 60,
fill: 0x000000
});
speedText.anchor.set(0, 0);
LK.gui.left.addChild(speedText);
speedText.x = 20;
speedText.y = -300;
var instructionText = new Text2('TAP TO SPRINT!', {
size: 100,
fill: 0xFF0000
});
instructionText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(instructionText);
instructionText.x = 0;
instructionText.y = 200;
// Tutorial text explaining controls
var tutorialText = new Text2('TAP SCREEN TO RUN FASTER\nTAP UPPER AREA TO JUMP OVER OBSTACLES', {
size: 60,
fill: 0x000000
});
tutorialText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(tutorialText);
tutorialText.x = 0;
tutorialText.y = -100;
// Continue text
var continueText = new Text2('TAP TO CONTINUE', {
size: 50,
fill: 0x666666
});
continueText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(continueText);
continueText.x = 0;
continueText.y = 400;
var positionText = new Text2('Position: 5/5', {
size: 60,
fill: 0x000000
});
positionText.anchor.set(1, 0);
LK.gui.topRight.addChild(positionText);
positionText.x = -20;
positionText.y = 150;
// Create stadium background
var stadium = game.addChild(LK.getAsset('stadium', {
anchorX: 0,
anchorY: 0
}));
stadium.x = 0;
stadium.y = 0;
// Create track
var trackY = 1500;
var track = game.addChild(LK.getAsset('track', {
anchorX: 0,
anchorY: 0.5
}));
track.x = 0;
track.y = trackY;
// Create lane lines
for (var i = 1; i < 5; i++) {
var lane = game.addChild(LK.getAsset('lane', {
anchorX: 0,
anchorY: 0.5
}));
lane.x = 0;
lane.y = trackY - 150 + i * 60;
}
// Create finish line
var finishLine = game.addChild(LK.getAsset('finish', {
anchorX: 0.5,
anchorY: 0.5
}));
finishLine.x = 1950;
finishLine.y = trackY;
function initializeRace() {
// Clear previous runners and obstacles
for (var i = 0; i < runners.length; i++) {
runners[i].destroy();
}
for (var j = 0; j < obstacles.length; j++) {
obstacles[j].destroy();
}
runners = [];
obstacles = [];
// Create runners
var colors = ['player', 'ai1', 'ai2', 'ai3', 'ai4'];
for (var k = 0; k < 5; k++) {
var runner = new Runner(colors[k], k);
runner.x = 100;
runner.y = trackY - 120 + k * 60;
if (k === 0) {
runner.isPlayer = true;
runner.maxSpeed = 14; // Set higher max speed for player
playerRunner = runner;
} else {
// Set random base speed for each AI runner that will persist this round
runner.baseSpeed = (3 + Math.random() * 2) * 1.22; // Random base speed between 3.66-6.1 (1.22x faster)
runner.maxSpeed = (runner.baseSpeed + 1 + Math.random() * 1) * 1.22; // Max speed based on base speed (1.22x faster)
}
runners.push(runner);
game.addChild(runner);
}
// Create obstacles for rounds 2+
if (currentRound >= 2) {
var obstacleCount = Math.min(currentRound, 5);
var usedLanes = [];
for (var l = 0; l < obstacleCount; l++) {
var obsLane;
// Keep trying random lanes until we find one that's not used
do {
obsLane = Math.floor(Math.random() * 5);
} while (usedLanes.indexOf(obsLane) !== -1);
usedLanes.push(obsLane);
var obsDistance = 800 + Math.random() * 800;
var obstacle = new Obstacle(obsLane, obsDistance);
obstacle.y = trackY - 120 + obsLane * 60;
obstacles.push(obstacle);
game.addChild(obstacle);
}
}
if (currentRound === 1) {
gameState = 'tutorial';
tutorialText.visible = true;
continueText.visible = true;
instructionText.visible = false;
} else {
gameState = 'ready';
tutorialText.visible = false;
continueText.visible = false;
instructionText.visible = true;
}
tapCount = 0;
roundText.setText('Round ' + currentRound);
}
function startRace() {
gameState = 'racing';
raceStartTime = LK.ticks;
instructionText.visible = false;
}
function updatePlayerSpeed() {
var currentTime = LK.ticks;
var timeSinceLastTap = currentTime - lastTapTime;
if (timeSinceLastTap > 30) {
// Decrease speed if not tapping
playerRunner.speed *= 0.95;
}
if (playerRunner.speed > playerRunner.maxSpeed) {
playerRunner.speed = playerRunner.maxSpeed;
}
speedText.setText('Speed: ' + Math.floor(playerRunner.speed));
}
function updateAI() {
for (var i = 1; i < runners.length; i++) {
var aiRunner = runners[i];
// Use consistent base speed for this round with small random variations
var roundBonus = currentRound * 0.2;
var targetSpeed = (aiRunner.baseSpeed + roundBonus) * 1.22;
// Add small random variation (±10%) to make movement feel natural
var variation = targetSpeed * (0.8 + Math.random() * 0.2);
aiRunner.speed = variation;
// Cap speed at the runner's max speed
if (aiRunner.speed > aiRunner.maxSpeed) {
aiRunner.speed = aiRunner.maxSpeed;
}
// AI obstacle avoidance
for (var j = 0; j < obstacles.length; j++) {
var obs = obstacles[j];
if (obs.lane === aiRunner.lane && Math.abs(aiRunner.distance - obs.distance) < 150 && Math.random() < 0.7) {
aiRunner.jump();
break;
}
}
}
}
function updatePositions() {
var sortedRunners = runners.slice().sort(function (a, b) {
return b.distance - a.distance;
});
var playerPosition = sortedRunners.indexOf(playerRunner) + 1;
positionText.setText('Position: ' + playerPosition + '/5');
}
function checkRaceEnd() {
var finishedRunners = [];
for (var i = 0; i < runners.length; i++) {
if (runners[i].distance >= raceDistance) {
finishedRunners.push(runners[i]);
}
}
if (finishedRunners.length > 0) {
gameState = 'finished';
// Sort by distance to determine winner
finishedRunners.sort(function (a, b) {
return b.distance - a.distance;
});
if (finishedRunners[0] === playerRunner) {
// Player won
currentRound++;
LK.setScore(currentRound - 1);
LK.setTimeout(function () {
initializeRace();
}, 2000);
} else {
// Player lost
LK.setTimeout(function () {
LK.showGameOver();
}, 2000);
}
}
}
// Touch controls
game.down = function (x, y, obj) {
if (gameState === 'tutorial') {
// Move from tutorial to ready state
gameState = 'ready';
tutorialText.visible = false;
continueText.visible = false;
instructionText.visible = true;
}
if (gameState === 'ready') {
startRace();
}
if (gameState === 'racing') {
tapCount++;
lastTapTime = LK.ticks;
// Increase player speed
playerRunner.speed += 0.8;
// Jump if tapping in upper screen area
if (y < 1200) {
playerRunner.jump();
}
LK.getSound('tap').play();
}
};
// Game update loop
game.update = function () {
if (gameState === 'racing') {
updatePlayerSpeed();
updateAI();
updatePositions();
checkRaceEnd();
}
};
// Initialize first race
initializeRace();
runner with a red shirt. In-Game asset. 2d. High contrast. No shadows
runner with a green shirt. In-Game asset. 2d. High contrast. No shadows
runner with a yellow shirt. In-Game asset. 2d. High contrast. No shadows
runner with a blue shirt. In-Game asset. 2d. High contrast. No shadows
runner with a black shirt with number 7. In-Game asset. 2d. High contrast. No shadows
running obstacle. In-Game asset. 2d. High contrast. No shadows
running finish line. In-Game asset. 2d. High contrast. No shadows
horizontal white lines for running. In-Game asset. 2d. High contrast. No shadows
straight running lines. In-Game asset. 2d. High contrast. No shadows