/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Biker = Container.expand(function () {
var self = Container.call(this);
var bikerGraphics = self.attachAsset('biker', {
anchorX: 0.5,
anchorY: 0.5
});
// State variables
self.velocity = {
x: 0,
y: 0
};
self.isJumping = false;
self.isDead = false;
self.gravity = 0.5;
self.jumpForce = -20; // Increase jump force for quicker response
self.jump = function (isHighJump) {
if (!self.isJumping && !self.isDead) {
self.velocity.y = isHighJump ? self.jumpForce * 2 : self.jumpForce;
self.isJumping = isHighJump ? true : !self.isJumping;
LK.getSound('jump').play();
}
};
self.update = function () {
if (self.isDead) {
return;
}
// Apply gravity
self.velocity.y += self.gravity;
// Update position
self.y += self.velocity.y;
// Cap falling speed
if (self.velocity.y > 15) {
self.velocity.y = 15;
}
// Tilt the biker based on vertical velocity
var targetRotation = self.velocity.y * 0.03;
bikerGraphics.rotation = targetRotation;
};
self.die = function () {
if (!self.isDead) {
self.isDead = true;
LK.getSound('crash').play();
// Animate the death
tween(bikerGraphics, {
rotation: Math.PI
}, {
duration: 800,
easing: tween.bounceOut
});
// Flash the biker red
LK.effects.flashObject(self, 0xff0000, 500);
// Game over after a short delay
LK.setTimeout(function () {
LK.showGameOver();
}, 1000);
}
};
return self;
});
var Coin = Container.expand(function () {
var self = Container.call(this);
var coinGraphics = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = coinGraphics.width;
self.height = coinGraphics.height;
self.speed = 5;
self.active = true;
// Animation
self.update = function () {
if (!self.active) {
return;
}
// Spin the coin
coinGraphics.rotation += 0.05;
self.x -= self.speed;
// Remove when off screen
if (self.x < -self.width) {
self.active = false;
self.destroy();
}
};
self.collect = function () {
if (self.active) {
self.active = false;
// Animate coin collection
tween(self, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 300,
onFinish: function onFinish() {
self.destroy();
}
});
LK.getSound('coin').play();
return true;
}
return false;
};
return self;
});
var Ground = Container.expand(function () {
var self = Container.call(this);
var groundGraphics = self.attachAsset('ground', {
anchorX: 0,
anchorY: 0
});
self.speed = 5;
self.update = function () {
self.x -= self.speed;
// Loop the ground
if (self.x <= -groundGraphics.width) {
self.x += groundGraphics.width;
}
};
return self;
});
var Jump = Container.expand(function () {
var self = Container.call(this);
var jumpGraphics = self.attachAsset('jump', {
anchorX: 0.5,
anchorY: 1.0
});
self.width = jumpGraphics.width;
self.height = jumpGraphics.height;
self.speed = 5;
self.active = true;
self.update = function () {
if (!self.active) {
return;
}
self.x -= self.speed;
// Remove when off screen
if (self.x < -self.width) {
self.active = false;
self.destroy();
}
};
return self;
});
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 1.0
});
self.width = obstacleGraphics.width;
self.height = obstacleGraphics.height;
self.speed = 5;
self.active = true;
self.update = function () {
if (!self.active) {
return;
}
self.x -= self.speed;
// Remove when off screen
if (self.x < -self.width) {
self.active = false;
self.destroy();
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB // Sky blue background
});
/****
* Game Code
****/
// Game variables
var biker;
var groundTiles = [];
var obstacles = [];
var jumps = [];
var coins = [];
var distance = 0;
var groundY = 2732 - 250; // Ground position from top
var gameSpeed = 5;
var spawnDistance = 0;
var lastObstaclePos = 0;
var gameActive = false;
var difficultyTimer = 0;
var coinsCollected = 0;
// Create score display
var scoreTxt = new Text2('Distance: 0m', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Create coin display
var coinTxt = new Text2('Coins: 0', {
size: 80,
fill: 0xFFCC00
});
coinTxt.anchor.set(0.5, 0);
coinTxt.y = 100;
LK.gui.top.addChild(coinTxt);
// Initialize high score from storage
var highScore = storage.highScore || 0;
var highScoreTxt = new Text2('Best: ' + highScore + 'm', {
size: 60,
fill: 0xFFFFFF
});
highScoreTxt.anchor.set(1.0, 0);
highScoreTxt.y = 10;
highScoreTxt.x = -10;
LK.gui.topRight.addChild(highScoreTxt);
// Create tap to start message
var startTxt = new Text2('Tap to Start', {
size: 120,
fill: 0xFFFFFF
});
startTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(startTxt);
// Initialize the game elements
function initGame() {
// Attach background image
var background = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2
});
game.addChild(background);
// Reset variables
obstacles = [];
jumps = [];
coins = [];
distance = 0;
gameSpeed = 5;
spawnDistance = 0;
lastObstaclePos = 0;
gameActive = false;
difficultyTimer = 0;
coinsCollected = 0;
// Update UI
scoreTxt.setText('Distance: 0m');
coinTxt.setText('Coins: 0');
startTxt.visible = true;
// Create ground
if (groundTiles.length === 0) {
for (var i = 0; i < 2; i++) {
var ground = new Ground();
ground.x = i * ground.width;
ground.y = groundY;
groundTiles.push(ground);
game.addChild(ground);
}
} else {
for (var i = 0; i < groundTiles.length; i++) {
groundTiles[i].x = i * groundTiles[i].width;
}
}
// Create player if not exists
if (!biker) {
biker = new Biker();
game.addChild(biker);
}
// Position player
biker.x = 2048 / 2; // Center the player horizontally
biker.y = groundY - 30;
biker.velocity = {
x: 0,
y: 0
};
biker.isJumping = false;
biker.isDead = false;
// Play background music
LK.playMusic('bgMusic', {
fade: {
start: 0,
end: 0.6,
duration: 1000
}
});
}
// Start the game
function startGame() {
if (!gameActive) {
gameActive = true;
startTxt.visible = false;
}
}
// Spawn obstacles and collectibles
function spawnObjects() {
if (!gameActive) {
return;
}
spawnDistance += gameSpeed;
// Minimum distance between objects
var minDistance = 500;
// Check if we should spawn a new object
if (spawnDistance - lastObstaclePos > minDistance) {
var rand = Math.random();
// Determine what type of object to spawn
if (rand < 0.5) {
// Spawn obstacle
var obstacle = new Obstacle();
obstacle.x = 2048 + obstacle.width;
obstacle.y = groundY;
obstacle.speed = gameSpeed;
obstacles.push(obstacle);
game.addChild(obstacle);
lastObstaclePos = spawnDistance;
} else if (rand < 0.8) {
// Spawn jump ramp
var jump = new Jump();
jump.x = 2048 + jump.width;
jump.y = groundY;
jump.speed = gameSpeed;
jumps.push(jump);
game.addChild(jump);
lastObstaclePos = spawnDistance;
// Spawn coin above the jump
var coin = new Coin();
coin.x = jump.x;
coin.y = groundY - 150 - Math.random() * 100;
coin.speed = gameSpeed;
coins.push(coin);
game.addChild(coin);
} else {
// Spawn just a coin
var coin = new Coin();
coin.x = 2048 + coin.width;
coin.y = groundY - 100 - Math.random() * 150;
coin.speed = gameSpeed;
coins.push(coin);
game.addChild(coin);
lastObstaclePos = spawnDistance;
}
}
}
// Check collision with ground
function checkGroundCollision() {
if (biker.y >= groundY - 30) {
biker.y = groundY - 30;
biker.velocity.y = 0;
biker.isJumping = false;
}
}
// Check collision with obstacles
function checkObstacleCollision() {
if (!gameActive || biker.isDead) {
return;
}
// Check obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
var obstacle = obstacles[i];
if (obstacle.active && biker.intersects(obstacle)) {
biker.die();
return;
}
}
// Check jumps (no collision, just boost)
for (var i = jumps.length - 1; i >= 0; i--) {
var jump = jumps[i];
if (jump.active && biker.intersects(jump) && !biker.isJumping) {
biker.velocity.y = biker.jumpForce * 1.2; // Extra boost from ramp
biker.isJumping = true;
LK.getSound('jump').play();
}
}
// Check coins
for (var i = coins.length - 1; i >= 0; i--) {
var coin = coins[i];
if (coin.active && biker.intersects(coin)) {
if (coin.collect()) {
coins.splice(i, 1);
coinsCollected++;
coinTxt.setText('Coins: ' + coinsCollected);
}
}
}
}
// Update game difficulty
function updateDifficulty() {
if (!gameActive) {
return;
}
difficultyTimer++;
// Increase speed every 5 seconds
if (difficultyTimer % 300 === 0) {
gameSpeed += 1.0;
// Update all object speeds
for (var i = 0; i < groundTiles.length; i++) {
groundTiles[i].speed = gameSpeed;
}
for (var i = 0; i < obstacles.length; i++) {
obstacles[i].speed = gameSpeed;
}
for (var i = 0; i < jumps.length; i++) {
jumps[i].speed = gameSpeed;
}
for (var i = 0; i < coins.length; i++) {
coins[i].speed = gameSpeed;
}
}
}
// Clean up objects that are no longer active
function cleanupObjects() {
for (var i = obstacles.length - 1; i >= 0; i--) {
if (!obstacles[i].active) {
obstacles.splice(i, 1);
}
}
for (var i = jumps.length - 1; i >= 0; i--) {
if (!jumps[i].active) {
jumps.splice(i, 1);
}
}
for (var i = coins.length - 1; i >= 0; i--) {
if (!coins[i].active) {
coins.splice(i, 1);
}
}
}
// Update score
function updateScore() {
if (gameActive && !biker.isDead) {
distance += gameSpeed / 10;
var distanceInt = Math.floor(distance);
scoreTxt.setText('Distance: ' + distanceInt + 'm');
// Update high score
if (distanceInt > highScore) {
highScore = distanceInt;
highScoreTxt.setText('Best: ' + highScore + 'm');
storage.highScore = highScore;
}
}
}
// Event handlers
game.down = function (x, y, obj) {
if (!gameActive) {
startGame();
} else if (!biker.isDead) {
biker.jump(y < 1700); // High jump if tap is in the upper 60% of the screen
}
};
// Game update function
game.update = function () {
// Initialize on first run
if (LK.ticks === 1) {
initGame();
}
// Update all game objects
if (biker) {
biker.update();
}
for (var i = 0; i < groundTiles.length; i++) {
groundTiles[i].update();
}
for (var i = 0; i < obstacles.length; i++) {
obstacles[i].update();
}
for (var i = 0; i < jumps.length; i++) {
jumps[i].update();
}
for (var i = 0; i < coins.length; i++) {
coins[i].update();
}
// Game logic
if (gameActive) {
checkGroundCollision();
checkObstacleCollision();
spawnObjects();
updateDifficulty();
updateScore();
cleanupObjects();
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Biker = Container.expand(function () {
var self = Container.call(this);
var bikerGraphics = self.attachAsset('biker', {
anchorX: 0.5,
anchorY: 0.5
});
// State variables
self.velocity = {
x: 0,
y: 0
};
self.isJumping = false;
self.isDead = false;
self.gravity = 0.5;
self.jumpForce = -20; // Increase jump force for quicker response
self.jump = function (isHighJump) {
if (!self.isJumping && !self.isDead) {
self.velocity.y = isHighJump ? self.jumpForce * 2 : self.jumpForce;
self.isJumping = isHighJump ? true : !self.isJumping;
LK.getSound('jump').play();
}
};
self.update = function () {
if (self.isDead) {
return;
}
// Apply gravity
self.velocity.y += self.gravity;
// Update position
self.y += self.velocity.y;
// Cap falling speed
if (self.velocity.y > 15) {
self.velocity.y = 15;
}
// Tilt the biker based on vertical velocity
var targetRotation = self.velocity.y * 0.03;
bikerGraphics.rotation = targetRotation;
};
self.die = function () {
if (!self.isDead) {
self.isDead = true;
LK.getSound('crash').play();
// Animate the death
tween(bikerGraphics, {
rotation: Math.PI
}, {
duration: 800,
easing: tween.bounceOut
});
// Flash the biker red
LK.effects.flashObject(self, 0xff0000, 500);
// Game over after a short delay
LK.setTimeout(function () {
LK.showGameOver();
}, 1000);
}
};
return self;
});
var Coin = Container.expand(function () {
var self = Container.call(this);
var coinGraphics = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = coinGraphics.width;
self.height = coinGraphics.height;
self.speed = 5;
self.active = true;
// Animation
self.update = function () {
if (!self.active) {
return;
}
// Spin the coin
coinGraphics.rotation += 0.05;
self.x -= self.speed;
// Remove when off screen
if (self.x < -self.width) {
self.active = false;
self.destroy();
}
};
self.collect = function () {
if (self.active) {
self.active = false;
// Animate coin collection
tween(self, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 300,
onFinish: function onFinish() {
self.destroy();
}
});
LK.getSound('coin').play();
return true;
}
return false;
};
return self;
});
var Ground = Container.expand(function () {
var self = Container.call(this);
var groundGraphics = self.attachAsset('ground', {
anchorX: 0,
anchorY: 0
});
self.speed = 5;
self.update = function () {
self.x -= self.speed;
// Loop the ground
if (self.x <= -groundGraphics.width) {
self.x += groundGraphics.width;
}
};
return self;
});
var Jump = Container.expand(function () {
var self = Container.call(this);
var jumpGraphics = self.attachAsset('jump', {
anchorX: 0.5,
anchorY: 1.0
});
self.width = jumpGraphics.width;
self.height = jumpGraphics.height;
self.speed = 5;
self.active = true;
self.update = function () {
if (!self.active) {
return;
}
self.x -= self.speed;
// Remove when off screen
if (self.x < -self.width) {
self.active = false;
self.destroy();
}
};
return self;
});
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 1.0
});
self.width = obstacleGraphics.width;
self.height = obstacleGraphics.height;
self.speed = 5;
self.active = true;
self.update = function () {
if (!self.active) {
return;
}
self.x -= self.speed;
// Remove when off screen
if (self.x < -self.width) {
self.active = false;
self.destroy();
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB // Sky blue background
});
/****
* Game Code
****/
// Game variables
var biker;
var groundTiles = [];
var obstacles = [];
var jumps = [];
var coins = [];
var distance = 0;
var groundY = 2732 - 250; // Ground position from top
var gameSpeed = 5;
var spawnDistance = 0;
var lastObstaclePos = 0;
var gameActive = false;
var difficultyTimer = 0;
var coinsCollected = 0;
// Create score display
var scoreTxt = new Text2('Distance: 0m', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Create coin display
var coinTxt = new Text2('Coins: 0', {
size: 80,
fill: 0xFFCC00
});
coinTxt.anchor.set(0.5, 0);
coinTxt.y = 100;
LK.gui.top.addChild(coinTxt);
// Initialize high score from storage
var highScore = storage.highScore || 0;
var highScoreTxt = new Text2('Best: ' + highScore + 'm', {
size: 60,
fill: 0xFFFFFF
});
highScoreTxt.anchor.set(1.0, 0);
highScoreTxt.y = 10;
highScoreTxt.x = -10;
LK.gui.topRight.addChild(highScoreTxt);
// Create tap to start message
var startTxt = new Text2('Tap to Start', {
size: 120,
fill: 0xFFFFFF
});
startTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(startTxt);
// Initialize the game elements
function initGame() {
// Attach background image
var background = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2
});
game.addChild(background);
// Reset variables
obstacles = [];
jumps = [];
coins = [];
distance = 0;
gameSpeed = 5;
spawnDistance = 0;
lastObstaclePos = 0;
gameActive = false;
difficultyTimer = 0;
coinsCollected = 0;
// Update UI
scoreTxt.setText('Distance: 0m');
coinTxt.setText('Coins: 0');
startTxt.visible = true;
// Create ground
if (groundTiles.length === 0) {
for (var i = 0; i < 2; i++) {
var ground = new Ground();
ground.x = i * ground.width;
ground.y = groundY;
groundTiles.push(ground);
game.addChild(ground);
}
} else {
for (var i = 0; i < groundTiles.length; i++) {
groundTiles[i].x = i * groundTiles[i].width;
}
}
// Create player if not exists
if (!biker) {
biker = new Biker();
game.addChild(biker);
}
// Position player
biker.x = 2048 / 2; // Center the player horizontally
biker.y = groundY - 30;
biker.velocity = {
x: 0,
y: 0
};
biker.isJumping = false;
biker.isDead = false;
// Play background music
LK.playMusic('bgMusic', {
fade: {
start: 0,
end: 0.6,
duration: 1000
}
});
}
// Start the game
function startGame() {
if (!gameActive) {
gameActive = true;
startTxt.visible = false;
}
}
// Spawn obstacles and collectibles
function spawnObjects() {
if (!gameActive) {
return;
}
spawnDistance += gameSpeed;
// Minimum distance between objects
var minDistance = 500;
// Check if we should spawn a new object
if (spawnDistance - lastObstaclePos > minDistance) {
var rand = Math.random();
// Determine what type of object to spawn
if (rand < 0.5) {
// Spawn obstacle
var obstacle = new Obstacle();
obstacle.x = 2048 + obstacle.width;
obstacle.y = groundY;
obstacle.speed = gameSpeed;
obstacles.push(obstacle);
game.addChild(obstacle);
lastObstaclePos = spawnDistance;
} else if (rand < 0.8) {
// Spawn jump ramp
var jump = new Jump();
jump.x = 2048 + jump.width;
jump.y = groundY;
jump.speed = gameSpeed;
jumps.push(jump);
game.addChild(jump);
lastObstaclePos = spawnDistance;
// Spawn coin above the jump
var coin = new Coin();
coin.x = jump.x;
coin.y = groundY - 150 - Math.random() * 100;
coin.speed = gameSpeed;
coins.push(coin);
game.addChild(coin);
} else {
// Spawn just a coin
var coin = new Coin();
coin.x = 2048 + coin.width;
coin.y = groundY - 100 - Math.random() * 150;
coin.speed = gameSpeed;
coins.push(coin);
game.addChild(coin);
lastObstaclePos = spawnDistance;
}
}
}
// Check collision with ground
function checkGroundCollision() {
if (biker.y >= groundY - 30) {
biker.y = groundY - 30;
biker.velocity.y = 0;
biker.isJumping = false;
}
}
// Check collision with obstacles
function checkObstacleCollision() {
if (!gameActive || biker.isDead) {
return;
}
// Check obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
var obstacle = obstacles[i];
if (obstacle.active && biker.intersects(obstacle)) {
biker.die();
return;
}
}
// Check jumps (no collision, just boost)
for (var i = jumps.length - 1; i >= 0; i--) {
var jump = jumps[i];
if (jump.active && biker.intersects(jump) && !biker.isJumping) {
biker.velocity.y = biker.jumpForce * 1.2; // Extra boost from ramp
biker.isJumping = true;
LK.getSound('jump').play();
}
}
// Check coins
for (var i = coins.length - 1; i >= 0; i--) {
var coin = coins[i];
if (coin.active && biker.intersects(coin)) {
if (coin.collect()) {
coins.splice(i, 1);
coinsCollected++;
coinTxt.setText('Coins: ' + coinsCollected);
}
}
}
}
// Update game difficulty
function updateDifficulty() {
if (!gameActive) {
return;
}
difficultyTimer++;
// Increase speed every 5 seconds
if (difficultyTimer % 300 === 0) {
gameSpeed += 1.0;
// Update all object speeds
for (var i = 0; i < groundTiles.length; i++) {
groundTiles[i].speed = gameSpeed;
}
for (var i = 0; i < obstacles.length; i++) {
obstacles[i].speed = gameSpeed;
}
for (var i = 0; i < jumps.length; i++) {
jumps[i].speed = gameSpeed;
}
for (var i = 0; i < coins.length; i++) {
coins[i].speed = gameSpeed;
}
}
}
// Clean up objects that are no longer active
function cleanupObjects() {
for (var i = obstacles.length - 1; i >= 0; i--) {
if (!obstacles[i].active) {
obstacles.splice(i, 1);
}
}
for (var i = jumps.length - 1; i >= 0; i--) {
if (!jumps[i].active) {
jumps.splice(i, 1);
}
}
for (var i = coins.length - 1; i >= 0; i--) {
if (!coins[i].active) {
coins.splice(i, 1);
}
}
}
// Update score
function updateScore() {
if (gameActive && !biker.isDead) {
distance += gameSpeed / 10;
var distanceInt = Math.floor(distance);
scoreTxt.setText('Distance: ' + distanceInt + 'm');
// Update high score
if (distanceInt > highScore) {
highScore = distanceInt;
highScoreTxt.setText('Best: ' + highScore + 'm');
storage.highScore = highScore;
}
}
}
// Event handlers
game.down = function (x, y, obj) {
if (!gameActive) {
startGame();
} else if (!biker.isDead) {
biker.jump(y < 1700); // High jump if tap is in the upper 60% of the screen
}
};
// Game update function
game.update = function () {
// Initialize on first run
if (LK.ticks === 1) {
initGame();
}
// Update all game objects
if (biker) {
biker.update();
}
for (var i = 0; i < groundTiles.length; i++) {
groundTiles[i].update();
}
for (var i = 0; i < obstacles.length; i++) {
obstacles[i].update();
}
for (var i = 0; i < jumps.length; i++) {
jumps[i].update();
}
for (var i = 0; i < coins.length; i++) {
coins[i].update();
}
// Game logic
if (gameActive) {
checkGroundCollision();
checkObstacleCollision();
spawnObjects();
updateDifficulty();
updateScore();
cleanupObjects();
}
};