/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highscore: 0,
coins: 0
});
/****
* Classes
****/
var Coin = Container.expand(function () {
var self = Container.call(this);
var coinGraphics = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 12;
self.active = true;
self.collected = false;
// Coin animation
function animateCoin() {
tween(coinGraphics, {
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(coinGraphics, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: animateCoin
});
}
});
}
animateCoin();
self.collect = function () {
if (!self.collected) {
self.collected = true;
LK.getSound('coin_collect').play();
tween(self, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
self.active = false;
self.destroy();
}
});
// Update coins
coins++;
coinsText.setText("Coins: " + coins);
// Save coins
storage.coins = coins;
}
};
self.update = function () {
if (!self.active) {
return;
}
self.x -= self.speed;
// Remove if off screen
if (self.x < -coinGraphics.width) {
self.active = false;
self.destroy();
}
};
return self;
});
var Ground = Container.expand(function () {
var self = Container.call(this);
var groundGraphics = self.attachAsset('ground', {
anchorX: 0,
anchorY: 0
});
self.speed = 12;
self.update = function () {
self.x -= self.speed;
// Loop ground
if (self.x <= -groundGraphics.width) {
self.x = 0;
}
};
return self;
});
var GroundDecoration = Container.expand(function () {
var self = Container.call(this);
var decorationGraphics = self.attachAsset('ground_decoration', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 12;
self.active = true;
// Random height
decorationGraphics.height = 10 + Math.random() * 20;
// Random color tint
var colors = [0x7f8c8d, 0x95a5a6, 0xbdc3c7];
decorationGraphics.tint = colors[Math.floor(Math.random() * colors.length)];
self.update = function () {
if (!self.active) {
return;
}
self.x -= self.speed;
// Remove if off screen
if (self.x < -decorationGraphics.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.speed = 12;
self.active = true;
self.update = function () {
if (!self.active) {
return;
}
self.x -= self.speed;
// Remove if off screen
if (self.x < -obstacleGraphics.width) {
self.active = false;
self.destroy();
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocity = 0;
self.gravity = 1.5;
self.jumpPower = -25;
self.grounded = false;
self.dead = false;
self.jump = function () {
if (self.grounded && !self.dead) {
self.velocity = self.jumpPower;
self.grounded = false;
LK.getSound('jump').play();
// Jump animation
tween(self, {
rotation: -Math.PI * 2
}, {
duration: 500,
easing: tween.linear,
onFinish: function onFinish() {
self.rotation = 0;
}
});
}
};
self.die = function () {
if (!self.dead) {
self.dead = true;
LK.getSound('death').play();
LK.effects.flashObject(self, 0xff0000, 500);
// Death animation
tween(self, {
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
LK.showGameOver();
}
});
}
};
self.update = function () {
if (self.dead) {
return;
}
// Apply gravity
self.velocity += self.gravity;
self.y += self.velocity;
// Check if on ground
if (self.y >= groundY - playerGraphics.height / 2) {
self.y = groundY - playerGraphics.height / 2;
self.velocity = 0;
self.grounded = true;
}
// Die if player falls below ground (into a pit)
if (self.y > groundY + 300) {
self.die();
}
};
return self;
});
var Spike = Container.expand(function () {
var self = Container.call(this);
var spikeGraphics = self.attachAsset('spike', {
anchorX: 0.5,
anchorY: 1.0
});
self.speed = 12;
self.active = true;
// Rotate to make it look like a spike
spikeGraphics.rotation = Math.PI / 4;
self.update = function () {
if (!self.active) {
return;
}
self.x -= self.speed;
// Remove if off screen
if (self.x < -spikeGraphics.width) {
self.active = false;
self.destroy();
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x34495e
});
/****
* Game Code
****/
// Game variables
var player;
var obstacles = [];
var spikes = [];
var coins = [];
var decorations = [];
var grounds = [];
var groundY = 2300;
var score = 0;
var distance = 0;
var gameStarted = false;
var gamePaused = false;
var lastObstacleTime = 0;
var obstacleSpawnRate = 120; // frames between obstacle spawns
var coinCollected = storage.coins || 0;
var coins = [];
var highscore = storage.highscore || 0;
// Background
var background = game.addChild(LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
}));
// Create UI elements
var scoreText = new Text2("Score: 0", {
size: 80,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
scoreText.y = 100;
var coinsText = new Text2("Coins: " + coins, {
size: 80,
fill: 0xF1C40F
});
coinsText.anchor.set(0.5, 0);
LK.gui.top.addChild(coinsText);
coinsText.y = 200;
var highscoreText = new Text2("Best: " + highscore, {
size: 80,
fill: 0x2ECC71
});
highscoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(highscoreText);
highscoreText.y = 300;
var startText = new Text2("Tap to Start", {
size: 120,
fill: 0xFFFFFF
});
startText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(startText);
// Initialize grounds (double to create seamless loop)
for (var i = 0; i < 2; i++) {
var ground = new Ground();
ground.x = i * 2048;
ground.y = groundY;
grounds.push(ground);
game.addChild(ground);
}
// Initialize player
player = new Player();
player.x = 400;
player.y = groundY - 50;
game.addChild(player);
// Start game
function startGame() {
if (!gameStarted) {
gameStarted = true;
LK.gui.center.removeChild(startText);
LK.playMusic('bgmusic');
// Start with some decorations
for (var i = 0; i < 15; i++) {
spawnDecoration(200 + i * 200);
}
}
}
// Spawn obstacle
function spawnObstacle() {
var obstacleType = Math.random();
if (obstacleType < 0.6) {
// Regular obstacle
var obstacle = new Obstacle();
obstacle.x = 2148; // Just off screen to the right
obstacle.y = groundY;
obstacles.push(obstacle);
game.addChild(obstacle);
} else {
// Spike
var spike = new Spike();
spike.x = 2148; // Just off screen to the right
spike.y = groundY;
spikes.push(spike);
game.addChild(spike);
}
// Maybe spawn a coin
if (Math.random() < 0.3) {
spawnCoin();
}
}
// Spawn coin
function spawnCoin() {
var coin = new Coin();
coin.x = 2148 + Math.random() * 200; // Slightly offset from obstacles
coin.y = groundY - 150 - Math.random() * 200; // Above ground at varying heights
coins.push(coin);
game.addChild(coin);
}
// Spawn decorations
function spawnDecoration(xPos) {
var decoration = new GroundDecoration();
decoration.x = xPos || 2148 + Math.random() * 100;
decoration.y = groundY - Math.random() * 10;
decorations.push(decoration);
game.addChild(decoration);
}
// Input handling
game.down = function (x, y, obj) {
if (!gameStarted) {
startGame();
} else {
player.jump();
}
};
// Main game update loop
game.update = function () {
if (!gameStarted) {
return;
}
// Update score
if (!player.dead) {
distance += 1;
score = Math.floor(distance / 5);
scoreText.setText("Score: " + score);
// Update high score if needed
if (score > highscore) {
highscore = score;
highscoreText.setText("Best: " + highscore);
storage.highscore = highscore;
}
// Increase difficulty
obstacleSpawnRate = Math.max(60, 120 - Math.floor(score / 100) * 10);
}
// Spawn obstacles
if (LK.ticks - lastObstacleTime > obstacleSpawnRate) {
spawnObstacle();
lastObstacleTime = LK.ticks;
}
// Spawn decorations randomly
if (Math.random() < 0.05) {
spawnDecoration();
}
// Update obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
var obstacle = obstacles[i];
// Check collision with player
if (!player.dead && player.intersects(obstacle)) {
player.die();
}
// Remove if not active anymore
if (!obstacle.active) {
obstacles.splice(i, 1);
}
}
// Update spikes
for (var i = spikes.length - 1; i >= 0; i--) {
var spike = spikes[i];
// Check collision with player
if (!player.dead && player.intersects(spike)) {
player.die();
}
// Remove if not active anymore
if (!spike.active) {
spikes.splice(i, 1);
}
}
// Update coins
for (var i = coins.length - 1; i >= 0; i--) {
var coin = coins[i];
// Check collision with player
if (!player.dead && !coin.collected && player.intersects(coin)) {
coin.collect();
}
// Remove if not active anymore
if (!coin.active) {
coins.splice(i, 1);
}
}
// Update decorations
for (var i = decorations.length - 1; i >= 0; i--) {
if (!decorations[i].active) {
decorations.splice(i, 1);
}
}
// Increase game speed based on score
var baseSpeed = 12;
var speedMultiplier = 1 + Math.min(1, score / 1000);
var gameSpeed = baseSpeed * speedMultiplier;
// Update game speed for all moving objects
for (var i = 0; i < obstacles.length; i++) {
obstacles[i].speed = gameSpeed;
}
for (var i = 0; i < spikes.length; i++) {
spikes[i].speed = gameSpeed;
}
for (var i = 0; i < coins.length; i++) {
coins[i].speed = gameSpeed;
}
for (var i = 0; i < decorations.length; i++) {
decorations[i].speed = gameSpeed;
}
for (var i = 0; i < grounds.length; i++) {
grounds[i].speed = gameSpeed;
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highscore: 0,
coins: 0
});
/****
* Classes
****/
var Coin = Container.expand(function () {
var self = Container.call(this);
var coinGraphics = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 12;
self.active = true;
self.collected = false;
// Coin animation
function animateCoin() {
tween(coinGraphics, {
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(coinGraphics, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: animateCoin
});
}
});
}
animateCoin();
self.collect = function () {
if (!self.collected) {
self.collected = true;
LK.getSound('coin_collect').play();
tween(self, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
self.active = false;
self.destroy();
}
});
// Update coins
coins++;
coinsText.setText("Coins: " + coins);
// Save coins
storage.coins = coins;
}
};
self.update = function () {
if (!self.active) {
return;
}
self.x -= self.speed;
// Remove if off screen
if (self.x < -coinGraphics.width) {
self.active = false;
self.destroy();
}
};
return self;
});
var Ground = Container.expand(function () {
var self = Container.call(this);
var groundGraphics = self.attachAsset('ground', {
anchorX: 0,
anchorY: 0
});
self.speed = 12;
self.update = function () {
self.x -= self.speed;
// Loop ground
if (self.x <= -groundGraphics.width) {
self.x = 0;
}
};
return self;
});
var GroundDecoration = Container.expand(function () {
var self = Container.call(this);
var decorationGraphics = self.attachAsset('ground_decoration', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 12;
self.active = true;
// Random height
decorationGraphics.height = 10 + Math.random() * 20;
// Random color tint
var colors = [0x7f8c8d, 0x95a5a6, 0xbdc3c7];
decorationGraphics.tint = colors[Math.floor(Math.random() * colors.length)];
self.update = function () {
if (!self.active) {
return;
}
self.x -= self.speed;
// Remove if off screen
if (self.x < -decorationGraphics.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.speed = 12;
self.active = true;
self.update = function () {
if (!self.active) {
return;
}
self.x -= self.speed;
// Remove if off screen
if (self.x < -obstacleGraphics.width) {
self.active = false;
self.destroy();
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocity = 0;
self.gravity = 1.5;
self.jumpPower = -25;
self.grounded = false;
self.dead = false;
self.jump = function () {
if (self.grounded && !self.dead) {
self.velocity = self.jumpPower;
self.grounded = false;
LK.getSound('jump').play();
// Jump animation
tween(self, {
rotation: -Math.PI * 2
}, {
duration: 500,
easing: tween.linear,
onFinish: function onFinish() {
self.rotation = 0;
}
});
}
};
self.die = function () {
if (!self.dead) {
self.dead = true;
LK.getSound('death').play();
LK.effects.flashObject(self, 0xff0000, 500);
// Death animation
tween(self, {
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
LK.showGameOver();
}
});
}
};
self.update = function () {
if (self.dead) {
return;
}
// Apply gravity
self.velocity += self.gravity;
self.y += self.velocity;
// Check if on ground
if (self.y >= groundY - playerGraphics.height / 2) {
self.y = groundY - playerGraphics.height / 2;
self.velocity = 0;
self.grounded = true;
}
// Die if player falls below ground (into a pit)
if (self.y > groundY + 300) {
self.die();
}
};
return self;
});
var Spike = Container.expand(function () {
var self = Container.call(this);
var spikeGraphics = self.attachAsset('spike', {
anchorX: 0.5,
anchorY: 1.0
});
self.speed = 12;
self.active = true;
// Rotate to make it look like a spike
spikeGraphics.rotation = Math.PI / 4;
self.update = function () {
if (!self.active) {
return;
}
self.x -= self.speed;
// Remove if off screen
if (self.x < -spikeGraphics.width) {
self.active = false;
self.destroy();
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x34495e
});
/****
* Game Code
****/
// Game variables
var player;
var obstacles = [];
var spikes = [];
var coins = [];
var decorations = [];
var grounds = [];
var groundY = 2300;
var score = 0;
var distance = 0;
var gameStarted = false;
var gamePaused = false;
var lastObstacleTime = 0;
var obstacleSpawnRate = 120; // frames between obstacle spawns
var coinCollected = storage.coins || 0;
var coins = [];
var highscore = storage.highscore || 0;
// Background
var background = game.addChild(LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
}));
// Create UI elements
var scoreText = new Text2("Score: 0", {
size: 80,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
scoreText.y = 100;
var coinsText = new Text2("Coins: " + coins, {
size: 80,
fill: 0xF1C40F
});
coinsText.anchor.set(0.5, 0);
LK.gui.top.addChild(coinsText);
coinsText.y = 200;
var highscoreText = new Text2("Best: " + highscore, {
size: 80,
fill: 0x2ECC71
});
highscoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(highscoreText);
highscoreText.y = 300;
var startText = new Text2("Tap to Start", {
size: 120,
fill: 0xFFFFFF
});
startText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(startText);
// Initialize grounds (double to create seamless loop)
for (var i = 0; i < 2; i++) {
var ground = new Ground();
ground.x = i * 2048;
ground.y = groundY;
grounds.push(ground);
game.addChild(ground);
}
// Initialize player
player = new Player();
player.x = 400;
player.y = groundY - 50;
game.addChild(player);
// Start game
function startGame() {
if (!gameStarted) {
gameStarted = true;
LK.gui.center.removeChild(startText);
LK.playMusic('bgmusic');
// Start with some decorations
for (var i = 0; i < 15; i++) {
spawnDecoration(200 + i * 200);
}
}
}
// Spawn obstacle
function spawnObstacle() {
var obstacleType = Math.random();
if (obstacleType < 0.6) {
// Regular obstacle
var obstacle = new Obstacle();
obstacle.x = 2148; // Just off screen to the right
obstacle.y = groundY;
obstacles.push(obstacle);
game.addChild(obstacle);
} else {
// Spike
var spike = new Spike();
spike.x = 2148; // Just off screen to the right
spike.y = groundY;
spikes.push(spike);
game.addChild(spike);
}
// Maybe spawn a coin
if (Math.random() < 0.3) {
spawnCoin();
}
}
// Spawn coin
function spawnCoin() {
var coin = new Coin();
coin.x = 2148 + Math.random() * 200; // Slightly offset from obstacles
coin.y = groundY - 150 - Math.random() * 200; // Above ground at varying heights
coins.push(coin);
game.addChild(coin);
}
// Spawn decorations
function spawnDecoration(xPos) {
var decoration = new GroundDecoration();
decoration.x = xPos || 2148 + Math.random() * 100;
decoration.y = groundY - Math.random() * 10;
decorations.push(decoration);
game.addChild(decoration);
}
// Input handling
game.down = function (x, y, obj) {
if (!gameStarted) {
startGame();
} else {
player.jump();
}
};
// Main game update loop
game.update = function () {
if (!gameStarted) {
return;
}
// Update score
if (!player.dead) {
distance += 1;
score = Math.floor(distance / 5);
scoreText.setText("Score: " + score);
// Update high score if needed
if (score > highscore) {
highscore = score;
highscoreText.setText("Best: " + highscore);
storage.highscore = highscore;
}
// Increase difficulty
obstacleSpawnRate = Math.max(60, 120 - Math.floor(score / 100) * 10);
}
// Spawn obstacles
if (LK.ticks - lastObstacleTime > obstacleSpawnRate) {
spawnObstacle();
lastObstacleTime = LK.ticks;
}
// Spawn decorations randomly
if (Math.random() < 0.05) {
spawnDecoration();
}
// Update obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
var obstacle = obstacles[i];
// Check collision with player
if (!player.dead && player.intersects(obstacle)) {
player.die();
}
// Remove if not active anymore
if (!obstacle.active) {
obstacles.splice(i, 1);
}
}
// Update spikes
for (var i = spikes.length - 1; i >= 0; i--) {
var spike = spikes[i];
// Check collision with player
if (!player.dead && player.intersects(spike)) {
player.die();
}
// Remove if not active anymore
if (!spike.active) {
spikes.splice(i, 1);
}
}
// Update coins
for (var i = coins.length - 1; i >= 0; i--) {
var coin = coins[i];
// Check collision with player
if (!player.dead && !coin.collected && player.intersects(coin)) {
coin.collect();
}
// Remove if not active anymore
if (!coin.active) {
coins.splice(i, 1);
}
}
// Update decorations
for (var i = decorations.length - 1; i >= 0; i--) {
if (!decorations[i].active) {
decorations.splice(i, 1);
}
}
// Increase game speed based on score
var baseSpeed = 12;
var speedMultiplier = 1 + Math.min(1, score / 1000);
var gameSpeed = baseSpeed * speedMultiplier;
// Update game speed for all moving objects
for (var i = 0; i < obstacles.length; i++) {
obstacles[i].speed = gameSpeed;
}
for (var i = 0; i < spikes.length; i++) {
spikes[i].speed = gameSpeed;
}
for (var i = 0; i < coins.length; i++) {
coins[i].speed = gameSpeed;
}
for (var i = 0; i < decorations.length; i++) {
decorations[i].speed = gameSpeed;
}
for (var i = 0; i < grounds.length; i++) {
grounds[i].speed = gameSpeed;
}
};