/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Character = Container.expand(function () {
var self = Container.call(this);
var characterGraphics = self.attachAsset('character', {
anchorX: 0.5,
anchorY: 1.0
});
// Character properties
self.baseY = 2400; // Ground level
self.targetX = 1024; // Center of screen
self.moveSpeed = 8;
self.update = function () {
// Continuous movement while holding
if (isHolding) {
self.x += holdDirection * self.moveSpeed;
}
// Keep character within screen bounds
self.x = Math.max(200, Math.min(1848, self.x));
};
return self;
});
var Coin = Container.expand(function () {
var self = Container.call(this);
var coinGraphics = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
// Scale coin to simulate 3D perspective
self.baseScale = 0.4;
self.scale.set(self.baseScale);
// Add floating animation
self.floatOffset = Math.random() * Math.PI * 2;
self.baseY = 0;
self.update = function () {
// Move toward camera
self.y += gameSpeed;
// Simulate 3D perspective
var distanceFromCamera = (2732 - self.y) / 2732;
var perspectiveScale = self.baseScale + (1 - self.baseScale) * (1 - distanceFromCamera);
self.scale.set(perspectiveScale);
// Floating animation
self.y = self.baseY + Math.sin(LK.ticks * 0.1 + self.floatOffset) * 10;
// Coin spinning animation for classic coin effect
coinGraphics.rotation += 0.08;
// Scale pulsing to make coin more noticeable
var pulseScale = 1 + Math.sin(LK.ticks * 0.15) * 0.1;
coinGraphics.scaleX = pulseScale;
// Adjust hit area
self.hitWidth = 80 * perspectiveScale;
self.hitHeight = 80 * perspectiveScale;
};
return self;
});
var MovingObstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 1.0
});
// Scale obstacle to simulate 3D perspective
self.baseScale = 0.2;
self.scale.set(self.baseScale);
self.moveDirection = Math.random() < 0.5 ? -1 : 1; // Random direction
self.moveSpeed = 1 + Math.random() * 2; // Random speed
self.update = function () {
// Move toward camera (increase Y and scale)
self.y += gameSpeed;
// Side-to-side movement
self.x += self.moveDirection * self.moveSpeed;
// Bounce off screen edges
if (self.x < 200 || self.x > 1848) {
self.moveDirection *= -1;
}
// Simulate 3D perspective by scaling based on distance
var distanceFromCamera = (2732 - self.y) / 2732;
var perspectiveScale = self.baseScale + (1 - self.baseScale) * (1 - distanceFromCamera);
self.scale.set(perspectiveScale);
// Adjust apparent width based on perspective
self.hitWidth = 100 * perspectiveScale;
self.hitHeight = 133 * perspectiveScale;
};
return self;
});
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 1.0
});
// Scale obstacle to simulate 3D perspective
self.baseScale = 0.2;
self.scale.set(self.baseScale);
self.update = function () {
// Move toward camera (increase Y and scale)
self.y += gameSpeed;
// Simulate 3D perspective by scaling based on distance
var distanceFromCamera = (2732 - self.y) / 2732;
var perspectiveScale = self.baseScale + (1 - self.baseScale) * (1 - distanceFromCamera);
self.scale.set(perspectiveScale);
// Adjust apparent width based on perspective
self.hitWidth = 100 * perspectiveScale;
self.hitHeight = 133 * perspectiveScale;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
// Game variables
var character;
var obstacles = [];
var coins = [];
var gameSpeed = 6;
var spawnTimer = 0;
var coinTimer = 0;
var movingObstacleTimer = 0;
var cameraY = 0;
var gameRunning = true;
// UI elements
var scoreTxt = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var speedTxt = new Text2('Speed: 1.0x', {
size: 60,
fill: 0xFFFFFF
});
speedTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(speedTxt);
// Create ground strips for perspective effect
var groundStrips = [];
for (var i = 0; i < 8; i++) {
var ground = game.addChild(LK.getAsset('ground', {
anchorX: 0.5,
anchorY: 0.5
}));
ground.x = 1024;
ground.y = 2400 + i * 300;
ground.baseY = ground.y;
ground.alpha = 0.3;
groundStrips.push(ground);
}
// Create character
character = game.addChild(new Character());
character.x = 1024;
character.y = character.baseY;
// Touch controls - hold to move
var isHolding = false;
var holdDirection = 0; // -1 for left, 1 for right
game.down = function (x, y, obj) {
if (!gameRunning) return;
isHolding = true;
if (x < 1024) {
// Left side - move left
holdDirection = -1;
} else {
// Right side - move right
holdDirection = 1;
}
};
game.up = function (x, y, obj) {
isHolding = false;
holdDirection = 0;
};
// Spawn obstacles
function spawnObstacle() {
var obstacle = new Obstacle();
// Better X distribution to avoid clustering
var lanes = [300, 600, 900, 1200, 1500, 1800]; // Defined lanes
var laneIndex = Math.floor(Math.random() * lanes.length);
obstacle.x = lanes[laneIndex] + (Math.random() - 0.5) * 100; // Add slight variation
obstacle.y = -200 - Math.random() * 300; // Vary spawn depth for natural spacing
obstacle.baseY = obstacle.y;
obstacles.push(obstacle);
game.addChild(obstacle);
}
// Spawn moving obstacles
function spawnMovingObstacle() {
var obstacle = new MovingObstacle();
var lanes = [300, 600, 900, 1200, 1500, 1800];
var laneIndex = Math.floor(Math.random() * lanes.length);
obstacle.x = lanes[laneIndex];
obstacle.y = -200 - Math.random() * 400;
obstacle.baseY = obstacle.y;
obstacles.push(obstacle);
game.addChild(obstacle);
}
// Spawn coins
function spawnCoin() {
var coin = new Coin();
// Better distribution across the screen with some spacing from edges
coin.x = 250 + Math.random() * 1548; // Wider spread
coin.y = -100 - Math.random() * 200; // Vary spawn depth for natural spacing
coin.baseY = coin.y;
coins.push(coin);
game.addChild(coin);
}
// Collision detection function
function checkCollision(obj1, obj2) {
var dx = obj1.x - obj2.x;
var dy = obj1.y - obj2.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var minDistance = (obj1.hitWidth || 120) / 2 + (obj2.hitWidth || 150) / 2;
return distance < minDistance;
}
// Game update loop
game.update = function () {
if (!gameRunning) return;
// Increase speed over time
gameSpeed += 0.002;
speedTxt.setText('Speed: ' + (gameSpeed / 6).toFixed(1) + 'x');
// Update ground for scrolling effect
for (var i = 0; i < groundStrips.length; i++) {
var ground = groundStrips[i];
ground.y += gameSpeed;
if (ground.y > 2932) {
ground.y = -200;
}
// Perspective scaling for ground
var distanceFromCamera = (2732 - ground.y) / 2732;
var perspectiveScale = 0.5 + 0.5 * (1 - distanceFromCamera);
ground.scaleY = perspectiveScale;
}
// Spawn obstacles
spawnTimer++;
if (spawnTimer > 30 - gameSpeed * 3) {
// Spawn multiple obstacles per wave
var obstaclesToSpawn = 1 + Math.floor(Math.random() * 3); // 1-3 obstacles per wave
for (var o = 0; o < obstaclesToSpawn; o++) {
spawnObstacle();
}
spawnTimer = 0;
}
// Spawn moving obstacles
movingObstacleTimer++;
if (movingObstacleTimer > 80 - gameSpeed * 2) {
if (Math.random() < 0.4) {
// 40% chance to spawn moving obstacle
spawnMovingObstacle();
}
movingObstacleTimer = 0;
}
// Spawn coins
coinTimer++;
var coinSpawnRate = 45 - Math.floor(LK.getScore() / 50); // Spawn faster as score increases
coinSpawnRate = Math.max(20, coinSpawnRate); // Minimum spawn rate
if (coinTimer > coinSpawnRate) {
// Spawn multiple coins in a wave
var coinsToSpawn = 2 + Math.floor(Math.random() * 3); // 2-4 coins per wave
// Bonus coins for high scores
if (LK.getScore() > 200 && Math.random() < 0.3) {
coinsToSpawn += 2; // Extra coins for high scores
}
for (var k = 0; k < coinsToSpawn; k++) {
spawnCoin();
}
coinTimer = 0;
}
// Update and check obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
var obstacle = obstacles[i];
// Remove obstacles that are off screen
if (obstacle.y > 2832) {
obstacle.destroy();
obstacles.splice(i, 1);
continue;
}
// Check collision with character
if (obstacle.y > 2000 && obstacle.y < 2600) {
if (checkCollision(character, obstacle)) {
// Game over
gameRunning = false;
LK.getSound('hit').play();
LK.effects.flashScreen(0xFF0000, 500);
LK.setTimeout(function () {
LK.showGameOver();
}, 500);
break;
}
}
}
// Update and check coins
for (var j = coins.length - 1; j >= 0; j--) {
var coin = coins[j];
// Remove coins that are off screen
if (coin.y > 2832) {
coin.destroy();
coins.splice(j, 1);
continue;
}
// Check coin collection
if (coin.y > 2000 && coin.y < 2600) {
if (checkCollision(character, coin)) {
// Coin collected
LK.setScore(LK.getScore() + 10);
scoreTxt.setText('Score: ' + LK.getScore());
LK.getSound('collect').play();
// Coin collection visual effect
tween(coin, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 200,
onFinish: function onFinish() {
coin.destroy();
}
});
coins.splice(j, 1);
}
}
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Character = Container.expand(function () {
var self = Container.call(this);
var characterGraphics = self.attachAsset('character', {
anchorX: 0.5,
anchorY: 1.0
});
// Character properties
self.baseY = 2400; // Ground level
self.targetX = 1024; // Center of screen
self.moveSpeed = 8;
self.update = function () {
// Continuous movement while holding
if (isHolding) {
self.x += holdDirection * self.moveSpeed;
}
// Keep character within screen bounds
self.x = Math.max(200, Math.min(1848, self.x));
};
return self;
});
var Coin = Container.expand(function () {
var self = Container.call(this);
var coinGraphics = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
// Scale coin to simulate 3D perspective
self.baseScale = 0.4;
self.scale.set(self.baseScale);
// Add floating animation
self.floatOffset = Math.random() * Math.PI * 2;
self.baseY = 0;
self.update = function () {
// Move toward camera
self.y += gameSpeed;
// Simulate 3D perspective
var distanceFromCamera = (2732 - self.y) / 2732;
var perspectiveScale = self.baseScale + (1 - self.baseScale) * (1 - distanceFromCamera);
self.scale.set(perspectiveScale);
// Floating animation
self.y = self.baseY + Math.sin(LK.ticks * 0.1 + self.floatOffset) * 10;
// Coin spinning animation for classic coin effect
coinGraphics.rotation += 0.08;
// Scale pulsing to make coin more noticeable
var pulseScale = 1 + Math.sin(LK.ticks * 0.15) * 0.1;
coinGraphics.scaleX = pulseScale;
// Adjust hit area
self.hitWidth = 80 * perspectiveScale;
self.hitHeight = 80 * perspectiveScale;
};
return self;
});
var MovingObstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 1.0
});
// Scale obstacle to simulate 3D perspective
self.baseScale = 0.2;
self.scale.set(self.baseScale);
self.moveDirection = Math.random() < 0.5 ? -1 : 1; // Random direction
self.moveSpeed = 1 + Math.random() * 2; // Random speed
self.update = function () {
// Move toward camera (increase Y and scale)
self.y += gameSpeed;
// Side-to-side movement
self.x += self.moveDirection * self.moveSpeed;
// Bounce off screen edges
if (self.x < 200 || self.x > 1848) {
self.moveDirection *= -1;
}
// Simulate 3D perspective by scaling based on distance
var distanceFromCamera = (2732 - self.y) / 2732;
var perspectiveScale = self.baseScale + (1 - self.baseScale) * (1 - distanceFromCamera);
self.scale.set(perspectiveScale);
// Adjust apparent width based on perspective
self.hitWidth = 100 * perspectiveScale;
self.hitHeight = 133 * perspectiveScale;
};
return self;
});
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 1.0
});
// Scale obstacle to simulate 3D perspective
self.baseScale = 0.2;
self.scale.set(self.baseScale);
self.update = function () {
// Move toward camera (increase Y and scale)
self.y += gameSpeed;
// Simulate 3D perspective by scaling based on distance
var distanceFromCamera = (2732 - self.y) / 2732;
var perspectiveScale = self.baseScale + (1 - self.baseScale) * (1 - distanceFromCamera);
self.scale.set(perspectiveScale);
// Adjust apparent width based on perspective
self.hitWidth = 100 * perspectiveScale;
self.hitHeight = 133 * perspectiveScale;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
// Game variables
var character;
var obstacles = [];
var coins = [];
var gameSpeed = 6;
var spawnTimer = 0;
var coinTimer = 0;
var movingObstacleTimer = 0;
var cameraY = 0;
var gameRunning = true;
// UI elements
var scoreTxt = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var speedTxt = new Text2('Speed: 1.0x', {
size: 60,
fill: 0xFFFFFF
});
speedTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(speedTxt);
// Create ground strips for perspective effect
var groundStrips = [];
for (var i = 0; i < 8; i++) {
var ground = game.addChild(LK.getAsset('ground', {
anchorX: 0.5,
anchorY: 0.5
}));
ground.x = 1024;
ground.y = 2400 + i * 300;
ground.baseY = ground.y;
ground.alpha = 0.3;
groundStrips.push(ground);
}
// Create character
character = game.addChild(new Character());
character.x = 1024;
character.y = character.baseY;
// Touch controls - hold to move
var isHolding = false;
var holdDirection = 0; // -1 for left, 1 for right
game.down = function (x, y, obj) {
if (!gameRunning) return;
isHolding = true;
if (x < 1024) {
// Left side - move left
holdDirection = -1;
} else {
// Right side - move right
holdDirection = 1;
}
};
game.up = function (x, y, obj) {
isHolding = false;
holdDirection = 0;
};
// Spawn obstacles
function spawnObstacle() {
var obstacle = new Obstacle();
// Better X distribution to avoid clustering
var lanes = [300, 600, 900, 1200, 1500, 1800]; // Defined lanes
var laneIndex = Math.floor(Math.random() * lanes.length);
obstacle.x = lanes[laneIndex] + (Math.random() - 0.5) * 100; // Add slight variation
obstacle.y = -200 - Math.random() * 300; // Vary spawn depth for natural spacing
obstacle.baseY = obstacle.y;
obstacles.push(obstacle);
game.addChild(obstacle);
}
// Spawn moving obstacles
function spawnMovingObstacle() {
var obstacle = new MovingObstacle();
var lanes = [300, 600, 900, 1200, 1500, 1800];
var laneIndex = Math.floor(Math.random() * lanes.length);
obstacle.x = lanes[laneIndex];
obstacle.y = -200 - Math.random() * 400;
obstacle.baseY = obstacle.y;
obstacles.push(obstacle);
game.addChild(obstacle);
}
// Spawn coins
function spawnCoin() {
var coin = new Coin();
// Better distribution across the screen with some spacing from edges
coin.x = 250 + Math.random() * 1548; // Wider spread
coin.y = -100 - Math.random() * 200; // Vary spawn depth for natural spacing
coin.baseY = coin.y;
coins.push(coin);
game.addChild(coin);
}
// Collision detection function
function checkCollision(obj1, obj2) {
var dx = obj1.x - obj2.x;
var dy = obj1.y - obj2.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var minDistance = (obj1.hitWidth || 120) / 2 + (obj2.hitWidth || 150) / 2;
return distance < minDistance;
}
// Game update loop
game.update = function () {
if (!gameRunning) return;
// Increase speed over time
gameSpeed += 0.002;
speedTxt.setText('Speed: ' + (gameSpeed / 6).toFixed(1) + 'x');
// Update ground for scrolling effect
for (var i = 0; i < groundStrips.length; i++) {
var ground = groundStrips[i];
ground.y += gameSpeed;
if (ground.y > 2932) {
ground.y = -200;
}
// Perspective scaling for ground
var distanceFromCamera = (2732 - ground.y) / 2732;
var perspectiveScale = 0.5 + 0.5 * (1 - distanceFromCamera);
ground.scaleY = perspectiveScale;
}
// Spawn obstacles
spawnTimer++;
if (spawnTimer > 30 - gameSpeed * 3) {
// Spawn multiple obstacles per wave
var obstaclesToSpawn = 1 + Math.floor(Math.random() * 3); // 1-3 obstacles per wave
for (var o = 0; o < obstaclesToSpawn; o++) {
spawnObstacle();
}
spawnTimer = 0;
}
// Spawn moving obstacles
movingObstacleTimer++;
if (movingObstacleTimer > 80 - gameSpeed * 2) {
if (Math.random() < 0.4) {
// 40% chance to spawn moving obstacle
spawnMovingObstacle();
}
movingObstacleTimer = 0;
}
// Spawn coins
coinTimer++;
var coinSpawnRate = 45 - Math.floor(LK.getScore() / 50); // Spawn faster as score increases
coinSpawnRate = Math.max(20, coinSpawnRate); // Minimum spawn rate
if (coinTimer > coinSpawnRate) {
// Spawn multiple coins in a wave
var coinsToSpawn = 2 + Math.floor(Math.random() * 3); // 2-4 coins per wave
// Bonus coins for high scores
if (LK.getScore() > 200 && Math.random() < 0.3) {
coinsToSpawn += 2; // Extra coins for high scores
}
for (var k = 0; k < coinsToSpawn; k++) {
spawnCoin();
}
coinTimer = 0;
}
// Update and check obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
var obstacle = obstacles[i];
// Remove obstacles that are off screen
if (obstacle.y > 2832) {
obstacle.destroy();
obstacles.splice(i, 1);
continue;
}
// Check collision with character
if (obstacle.y > 2000 && obstacle.y < 2600) {
if (checkCollision(character, obstacle)) {
// Game over
gameRunning = false;
LK.getSound('hit').play();
LK.effects.flashScreen(0xFF0000, 500);
LK.setTimeout(function () {
LK.showGameOver();
}, 500);
break;
}
}
}
// Update and check coins
for (var j = coins.length - 1; j >= 0; j--) {
var coin = coins[j];
// Remove coins that are off screen
if (coin.y > 2832) {
coin.destroy();
coins.splice(j, 1);
continue;
}
// Check coin collection
if (coin.y > 2000 && coin.y < 2600) {
if (checkCollision(character, coin)) {
// Coin collected
LK.setScore(LK.getScore() + 10);
scoreTxt.setText('Score: ' + LK.getScore());
LK.getSound('collect').play();
// Coin collection visual effect
tween(coin, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 200,
onFinish: function onFinish() {
coin.destroy();
}
});
coins.splice(j, 1);
}
}
}
};