/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0
});
/****
* Classes
****/
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.collected = false;
// Add light pulsing effect to make the coin shine
function startPulseEffect() {
tween(coinGraphics, {
scaleX: 1.2,
scaleY: 1.2,
rotation: Math.PI * 0.25,
alpha: 1.0
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(coinGraphics, {
scaleX: 0.9,
scaleY: 0.9,
rotation: -Math.PI * 0.25,
alpha: 0.8
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: startPulseEffect
});
}
});
}
self.setup = function (lane) {
self.y = GROUND_Y - self.height / 2; // Position coins on the ground
self.lane = lane;
self.speed = gameSpeed;
// Add spinning animation for coin
tween(coinGraphics, {
rotation: Math.PI * 2
}, {
duration: 1500,
easing: tween.linear,
onFinish: function onFinish() {
coinGraphics.rotation = 0;
if (self.parent) {
// Only restart if still in scene
self.setup(lane);
}
}
});
// Start the pulsing shine effect
startPulseEffect();
};
self.collect = function () {
if (self.collected) return;
self.collected = true;
LK.getSound('collect').play();
// Scale up and fade out effect with sparkle
tween.stop(coinGraphics); // Stop any ongoing tweens
// First make it flash bright
tween(coinGraphics, {
tint: 0xFFFFFF,
scaleX: 1.5,
scaleY: 1.5,
rotation: Math.PI
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
// Then zoom up and fade out
tween(self, {
scaleX: 2.5,
scaleY: 2.5,
alpha: 0,
y: self.y - 100
}, {
duration: 300,
easing: tween.bounceOut,
onFinish: function onFinish() {
self.destroy();
}
});
}
});
};
self.update = function () {
if (self.collected) return true;
// No need to manually update x as it's handled by tween
// Check if coin is off screen
if (self.x < -self.width) {
self.destroy();
return true; // Return true if destroyed
}
return false; // Not destroyed
};
return self;
});
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var type = "high"; // Default type
var graphicsAsset;
// Add subtle rotation animation to make obstacles more dynamic
function addObstacleEffect() {
// For high obstacles, add a more interesting wobble effect
if (type === "high") {
tween(graphicsAsset, {
rotation: 0.08,
scaleX: 1.03,
alpha: 0.95
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(graphicsAsset, {
rotation: -0.08,
scaleX: 0.97,
alpha: 1.0
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: addObstacleEffect
});
}
});
}
// For low obstacles, add a more dynamic bounce effect
else if (type === "low") {
tween(graphicsAsset, {
scaleY: 1.08,
scaleX: 0.95,
y: graphicsAsset.y - 5
}, {
duration: 400,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(graphicsAsset, {
scaleY: 0.97,
scaleX: 1.03,
y: graphicsAsset.y + 5
}, {
duration: 400,
easing: tween.easeInOut,
onFinish: addObstacleEffect
});
}
});
}
}
self.setup = function (obstacleType, lane) {
type = obstacleType;
if (self.children.length > 0) {
self.removeChildren();
}
if (type === "high") {
graphicsAsset = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (type === "low") {
graphicsAsset = self.attachAsset('lowObstacle', {
anchorX: 0.5,
anchorY: 0.5
});
}
self.width = graphicsAsset.width;
self.height = graphicsAsset.height;
// Calculate correct Y position based on type and lane - position all on ground
if (type === "high") {
self.y = GROUND_Y - self.height / 2; // High obstacles on the ground
} else if (type === "low") {
self.y = GROUND_Y - self.height / 2; // Low obstacles on the ground
}
self.lane = lane;
self.type = type;
self.speed = gameSpeed;
// Add appear animation when obstacle is created
graphicsAsset.scaleX = 0.1;
graphicsAsset.scaleY = 0.1;
graphicsAsset.alpha = 0.5;
graphicsAsset.rotation = Math.PI * 0.25;
tween(graphicsAsset, {
scaleX: 1.1,
scaleY: 1.1,
alpha: 1.0,
rotation: 0
}, {
duration: 300,
easing: tween.elasticOut,
onFinish: function onFinish() {
// Add slight bounce back
tween(graphicsAsset, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 150,
easing: tween.easeOut,
onFinish: addObstacleEffect
});
}
});
};
self.update = function () {
// No need to manually update x as it's handled by tween
// Check if obstacle is off screen
if (self.x < -self.width) {
tween.stop(graphicsAsset); // Stop any ongoing animations
self.destroy();
return true; // Return true if destroyed
}
return false; // Not destroyed
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
var playerJumpGraphics = LK.getAsset('playerJump', {
anchorX: 0.5,
anchorY: 0.5,
visible: false
});
var playerSlideGraphics = LK.getAsset('playerSlide', {
anchorX: 0.5,
anchorY: 0.5,
visible: false
});
self.addChild(playerJumpGraphics);
self.addChild(playerSlideGraphics);
self.width = playerGraphics.width;
self.height = playerGraphics.height;
self.lane = 1; // 0: top, 1: middle, 2: bottom
self.isJumping = false;
self.isSliding = false;
self.isDead = false;
self.jump = function () {
if (self.isJumping || self.isDead) return;
self.isJumping = true;
LK.getSound('jump').play();
// Show jump sprite, hide others
playerGraphics.visible = false;
playerSlideGraphics.visible = false;
playerJumpGraphics.visible = true;
// Add a small rotation effect for more dynamic jump
playerJumpGraphics.rotation = -0.2;
var startY = self.y;
var jumpHeight = 250;
// First stretch vertically to prepare for jump
tween(playerJumpGraphics, {
scaleY: 0.8,
scaleX: 1.2
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
// Then perform the actual jump
tween(self, {
y: startY - jumpHeight,
rotation: 0.2
}, {
duration: 350,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
y: startY,
rotation: 0
}, {
duration: 350,
easing: tween.easeIn,
onFinish: function onFinish() {
self.isJumping = false;
// Show normal sprite, hide others
playerGraphics.visible = true;
playerJumpGraphics.visible = false;
playerSlideGraphics.visible = false;
// Reset any scale changes
playerJumpGraphics.scaleX = 1;
playerJumpGraphics.scaleY = 1;
playerJumpGraphics.rotation = 0;
}
});
}
});
}
});
};
self.slide = function () {
if (self.isSliding || self.isDead) return;
self.isSliding = true;
// Show slide sprite, hide others
playerGraphics.visible = false;
playerJumpGraphics.visible = false;
playerSlideGraphics.visible = true;
// Add slide animation - stretch horizontally
tween(playerSlideGraphics, {
scaleX: 1.3,
scaleY: 0.7
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
// Hold slide position
tween(playerSlideGraphics, {
scaleX: 1.2,
scaleY: 0.8
}, {
duration: 200,
easing: tween.linear
});
}
});
LK.setTimeout(function () {
// End slide with slight bounce back to normal
tween(playerSlideGraphics, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
self.isSliding = false;
// Show normal sprite, hide others
playerGraphics.visible = true;
playerJumpGraphics.visible = false;
playerSlideGraphics.visible = false;
}
});
}, 600);
};
self.changeLane = function (direction) {
if (self.isDead) return;
// Direction: -1 for up, 1 for down
var newLane = self.lane + direction;
if (newLane < 0 || newLane > 2) return;
self.lane = newLane;
var targetY = lanePositions[self.lane];
tween(self, {
y: targetY
}, {
duration: 200,
easing: tween.easeOut
});
};
self.die = function () {
if (self.isDead) return;
self.isDead = true;
LK.getSound('hit').play();
// Flash the player red
LK.effects.flashObject(self, 0xff0000, 500);
// Create dramatic death effect with multiple stages
// First - impact effect
tween(self, {
scaleX: 1.3,
scaleY: 0.7,
rotation: 0.2
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
// Second - spin and fade
tween(self, {
rotation: Math.PI * 2,
alpha: 0.6,
scaleX: 0.7,
scaleY: 0.7,
y: self.y + 50
}, {
duration: 600,
easing: tween.easeIn,
onFinish: function onFinish() {
// Final bounce
tween(self, {
y: self.y + 100,
alpha: 0.3,
rotation: Math.PI * 3,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 250,
easing: tween.bounceOut
});
}
});
}
});
// Screen flash and game over with slight delay
LK.effects.flashScreen(0xff0000, 300);
LK.setTimeout(function () {
LK.showGameOver();
}, 1000);
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Game constants
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var lanePositions = [GAME_HEIGHT * 0.35, GAME_HEIGHT * 0.5, GAME_HEIGHT * 0.65];
var GROUND_Y = GAME_HEIGHT * 0.8;
var gameSpeed = 10;
var gameSpeedIncrement = 0.1;
var obstacleFrequency = 100; // Frames between obstacle generation
var coinFrequency = 150; // Frames between coin generation
var score = 0;
var distance = 0;
var isGameRunning = false;
// Game elements
var player;
var obstacles = [];
var coins = [];
var ground;
var ceiling;
var lastObstacleFrame = 0;
var lastCoinFrame = 0;
// GUI elements
var scoreTxt;
var distanceTxt;
var highScoreTxt;
// Game setup
function setupGame() {
// Add background first so it's behind everything
var background = game.addChild(LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
}));
// Create ground
ground = game.addChild(LK.getAsset('ground', {
anchorX: 0,
anchorY: 0,
x: 0,
y: GROUND_Y
}));
// Create ceiling
ceiling = game.addChild(LK.getAsset('ceiling', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
}));
// Create player
player = new Player();
player.x = GAME_WIDTH * 0.2;
player.y = GROUND_Y - player.height / 2; // Position player on the ground
game.addChild(player);
// Setup score text with shadow effect
scoreTxt = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 5
});
scoreTxt.anchor.set(0, 0);
scoreTxt.x = 50;
scoreTxt.y = 50;
LK.gui.top.addChild(scoreTxt);
// Setup distance text with shadow effect
distanceTxt = new Text2('Distance: 0m', {
size: 80,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 5
});
distanceTxt.anchor.set(0, 0);
distanceTxt.x = 50;
distanceTxt.y = 150;
LK.gui.top.addChild(distanceTxt);
// Setup high score text with shadow effect
highScoreTxt = new Text2('High Score: ' + storage.highScore, {
size: 80,
fill: 0xFFD700,
stroke: 0x000000,
strokeThickness: 5
});
highScoreTxt.anchor.set(1, 0);
highScoreTxt.x = GAME_WIDTH - 50;
highScoreTxt.y = 50;
LK.gui.top.addChild(highScoreTxt);
// Reset game state
score = 0;
distance = 0;
gameSpeed = 10;
isGameRunning = true;
// Start background music
LK.playMusic('gameMusic');
}
// Create a new obstacle
function createObstacle() {
var obstacle = new Obstacle();
var lane = Math.floor(Math.random() * 3); // Random lane
var type = Math.random() > 0.5 ? "high" : "low"; // Random type
obstacle.setup(type, lane);
obstacle.x = GAME_WIDTH + obstacle.width;
game.addChild(obstacle);
// Animate the obstacle coming towards the player
var targetX = -obstacle.width; // Final position off the left side of the screen
var duration = (GAME_WIDTH + obstacle.width * 2) / gameSpeed * 1000 / 60; // Duration based on game speed
tween(obstacle, {
x: targetX
}, {
duration: duration,
easing: tween.linear
});
obstacles.push(obstacle);
lastObstacleFrame = LK.ticks;
}
// Create a new coin
function createCoin() {
var coin = new Coin();
var lane = 0; // Lane doesn't matter as coins are on ground now, but we keep the parameter for consistency
coin.setup(lane);
coin.x = GAME_WIDTH + coin.width;
game.addChild(coin);
// Animate the coin coming towards the player
var targetX = -coin.width; // Final position off the left side of the screen
var duration = (GAME_WIDTH + coin.width * 2) / gameSpeed * 1000 / 60; // Duration based on game speed
tween(coin, {
x: targetX
}, {
duration: duration,
easing: tween.linear
});
coins.push(coin);
lastCoinFrame = LK.ticks;
}
// Check for collisions
function checkCollisions() {
// Check obstacle collisions
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
if (player.intersects(obstacle)) {
// Check if player can pass:
// - If jumping over high obstacle
// - If sliding under low obstacle
var canPass = false;
if (obstacle.type === "high" && player.isJumping) {
canPass = true;
} else if (obstacle.type === "low" && player.isSliding) {
canPass = true;
} else if (player.lane !== obstacle.lane && obstacle.lane !== 0) {
// Only consider lane difference if obstacle is not on ground level
canPass = true;
}
if (!canPass) {
player.die();
isGameRunning = false;
// Update high score
if (score > storage.highScore) {
storage.highScore = score;
highScoreTxt.setText('High Score: ' + storage.highScore);
}
}
}
}
// Check coin collisions
for (var j = 0; j < coins.length; j++) {
var coin = coins[j];
if (!coin.collected && player.intersects(coin)) {
coin.collect();
score += 10;
scoreTxt.setText('Score: ' + score);
}
}
}
// Update game function
function updateGame() {
if (!isGameRunning) return;
// Update distance and increase difficulty
distance += Math.floor(gameSpeed / 10);
distanceTxt.setText('Distance: ' + distance + 'm');
// Increase game speed
var oldSpeed = gameSpeed;
// Speed increases faster after 100m
if (distance > 100) {
gameSpeed += gameSpeedIncrement * 0.03;
} else {
gameSpeed += gameSpeedIncrement * 0.01;
}
// If game speed changed significantly, update existing tweens
if (gameSpeed > oldSpeed * 1.1) {
// Update obstacle speeds
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
// Stop current tween
tween.stop(obstacle, {
x: true
});
// Create new tween with updated speed
var remainingDistance = obstacle.x + obstacle.width;
var duration = remainingDistance / gameSpeed * 1000 / 60;
tween(obstacle, {
x: -obstacle.width
}, {
duration: duration,
easing: tween.linear
});
}
// Update coin speeds
for (var j = 0; j < coins.length; j++) {
var coin = coins[j];
if (!coin.collected) {
// Stop current tween
tween.stop(coin, {
x: true
});
// Create new tween with updated speed
var coinRemainingDistance = coin.x + coin.width;
var coinDuration = coinRemainingDistance / gameSpeed * 1000 / 60;
tween(coin, {
x: -coin.width
}, {
duration: coinDuration,
easing: tween.linear
});
}
}
}
// Generate obstacles
if (LK.ticks - lastObstacleFrame > obstacleFrequency) {
createObstacle();
// Obstacles appear more frequently as the game progresses
// After 100m, obstacles come much faster
if (distance > 100) {
obstacleFrequency = Math.max(30, 60 - Math.floor(distance / 200));
} else {
obstacleFrequency = Math.max(50, 100 - Math.floor(distance / 500));
}
}
// Generate coins
if (LK.ticks - lastCoinFrame > coinFrequency) {
createCoin();
}
// Update obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
if (obstacles[i].update()) {
obstacles.splice(i, 1);
}
}
// Update coins
for (var j = coins.length - 1; j >= 0; j--) {
if (coins[j].update()) {
coins.splice(j, 1);
}
}
// Check for collisions
checkCollisions();
}
// Handle swipe gestures
var startX = 0;
var startY = 0;
var isDragging = false;
var swipeThreshold = 50; // Minimum distance for a swipe
function handleDown(x, y, obj) {
startX = x;
startY = y;
isDragging = true;
}
function handleMove(x, y, obj) {
if (!isDragging || !isGameRunning) return;
var deltaX = x - startX;
var deltaY = y - startY;
var absX = Math.abs(deltaX);
var absY = Math.abs(deltaY);
// Only process significant movements
if (absX < swipeThreshold && absY < swipeThreshold) return;
// Determine swipe direction
if (absY > absX) {
// Vertical swipe
if (deltaY < 0) {
// Swipe up - jump
player.jump();
} else {
// Swipe down - slide
player.slide();
}
} else {
// Horizontal swipe
if (deltaY < -swipeThreshold) {
// Swipe up left/right - change lane up
player.changeLane(-1);
} else if (deltaY > swipeThreshold) {
// Swipe down left/right - change lane down
player.changeLane(1);
}
}
// Reset for next swipe
startX = x;
startY = y;
}
function handleUp(x, y, obj) {
isDragging = false;
}
// Set up event handlers
game.down = handleDown;
game.move = handleMove;
game.up = handleUp;
// Game update function
game.update = function () {
// Initialize game on first update
if (!player) {
setupGame();
}
// Update game state
updateGame();
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0
});
/****
* Classes
****/
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.collected = false;
// Add light pulsing effect to make the coin shine
function startPulseEffect() {
tween(coinGraphics, {
scaleX: 1.2,
scaleY: 1.2,
rotation: Math.PI * 0.25,
alpha: 1.0
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(coinGraphics, {
scaleX: 0.9,
scaleY: 0.9,
rotation: -Math.PI * 0.25,
alpha: 0.8
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: startPulseEffect
});
}
});
}
self.setup = function (lane) {
self.y = GROUND_Y - self.height / 2; // Position coins on the ground
self.lane = lane;
self.speed = gameSpeed;
// Add spinning animation for coin
tween(coinGraphics, {
rotation: Math.PI * 2
}, {
duration: 1500,
easing: tween.linear,
onFinish: function onFinish() {
coinGraphics.rotation = 0;
if (self.parent) {
// Only restart if still in scene
self.setup(lane);
}
}
});
// Start the pulsing shine effect
startPulseEffect();
};
self.collect = function () {
if (self.collected) return;
self.collected = true;
LK.getSound('collect').play();
// Scale up and fade out effect with sparkle
tween.stop(coinGraphics); // Stop any ongoing tweens
// First make it flash bright
tween(coinGraphics, {
tint: 0xFFFFFF,
scaleX: 1.5,
scaleY: 1.5,
rotation: Math.PI
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
// Then zoom up and fade out
tween(self, {
scaleX: 2.5,
scaleY: 2.5,
alpha: 0,
y: self.y - 100
}, {
duration: 300,
easing: tween.bounceOut,
onFinish: function onFinish() {
self.destroy();
}
});
}
});
};
self.update = function () {
if (self.collected) return true;
// No need to manually update x as it's handled by tween
// Check if coin is off screen
if (self.x < -self.width) {
self.destroy();
return true; // Return true if destroyed
}
return false; // Not destroyed
};
return self;
});
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var type = "high"; // Default type
var graphicsAsset;
// Add subtle rotation animation to make obstacles more dynamic
function addObstacleEffect() {
// For high obstacles, add a more interesting wobble effect
if (type === "high") {
tween(graphicsAsset, {
rotation: 0.08,
scaleX: 1.03,
alpha: 0.95
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(graphicsAsset, {
rotation: -0.08,
scaleX: 0.97,
alpha: 1.0
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: addObstacleEffect
});
}
});
}
// For low obstacles, add a more dynamic bounce effect
else if (type === "low") {
tween(graphicsAsset, {
scaleY: 1.08,
scaleX: 0.95,
y: graphicsAsset.y - 5
}, {
duration: 400,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(graphicsAsset, {
scaleY: 0.97,
scaleX: 1.03,
y: graphicsAsset.y + 5
}, {
duration: 400,
easing: tween.easeInOut,
onFinish: addObstacleEffect
});
}
});
}
}
self.setup = function (obstacleType, lane) {
type = obstacleType;
if (self.children.length > 0) {
self.removeChildren();
}
if (type === "high") {
graphicsAsset = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (type === "low") {
graphicsAsset = self.attachAsset('lowObstacle', {
anchorX: 0.5,
anchorY: 0.5
});
}
self.width = graphicsAsset.width;
self.height = graphicsAsset.height;
// Calculate correct Y position based on type and lane - position all on ground
if (type === "high") {
self.y = GROUND_Y - self.height / 2; // High obstacles on the ground
} else if (type === "low") {
self.y = GROUND_Y - self.height / 2; // Low obstacles on the ground
}
self.lane = lane;
self.type = type;
self.speed = gameSpeed;
// Add appear animation when obstacle is created
graphicsAsset.scaleX = 0.1;
graphicsAsset.scaleY = 0.1;
graphicsAsset.alpha = 0.5;
graphicsAsset.rotation = Math.PI * 0.25;
tween(graphicsAsset, {
scaleX: 1.1,
scaleY: 1.1,
alpha: 1.0,
rotation: 0
}, {
duration: 300,
easing: tween.elasticOut,
onFinish: function onFinish() {
// Add slight bounce back
tween(graphicsAsset, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 150,
easing: tween.easeOut,
onFinish: addObstacleEffect
});
}
});
};
self.update = function () {
// No need to manually update x as it's handled by tween
// Check if obstacle is off screen
if (self.x < -self.width) {
tween.stop(graphicsAsset); // Stop any ongoing animations
self.destroy();
return true; // Return true if destroyed
}
return false; // Not destroyed
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
var playerJumpGraphics = LK.getAsset('playerJump', {
anchorX: 0.5,
anchorY: 0.5,
visible: false
});
var playerSlideGraphics = LK.getAsset('playerSlide', {
anchorX: 0.5,
anchorY: 0.5,
visible: false
});
self.addChild(playerJumpGraphics);
self.addChild(playerSlideGraphics);
self.width = playerGraphics.width;
self.height = playerGraphics.height;
self.lane = 1; // 0: top, 1: middle, 2: bottom
self.isJumping = false;
self.isSliding = false;
self.isDead = false;
self.jump = function () {
if (self.isJumping || self.isDead) return;
self.isJumping = true;
LK.getSound('jump').play();
// Show jump sprite, hide others
playerGraphics.visible = false;
playerSlideGraphics.visible = false;
playerJumpGraphics.visible = true;
// Add a small rotation effect for more dynamic jump
playerJumpGraphics.rotation = -0.2;
var startY = self.y;
var jumpHeight = 250;
// First stretch vertically to prepare for jump
tween(playerJumpGraphics, {
scaleY: 0.8,
scaleX: 1.2
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
// Then perform the actual jump
tween(self, {
y: startY - jumpHeight,
rotation: 0.2
}, {
duration: 350,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
y: startY,
rotation: 0
}, {
duration: 350,
easing: tween.easeIn,
onFinish: function onFinish() {
self.isJumping = false;
// Show normal sprite, hide others
playerGraphics.visible = true;
playerJumpGraphics.visible = false;
playerSlideGraphics.visible = false;
// Reset any scale changes
playerJumpGraphics.scaleX = 1;
playerJumpGraphics.scaleY = 1;
playerJumpGraphics.rotation = 0;
}
});
}
});
}
});
};
self.slide = function () {
if (self.isSliding || self.isDead) return;
self.isSliding = true;
// Show slide sprite, hide others
playerGraphics.visible = false;
playerJumpGraphics.visible = false;
playerSlideGraphics.visible = true;
// Add slide animation - stretch horizontally
tween(playerSlideGraphics, {
scaleX: 1.3,
scaleY: 0.7
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
// Hold slide position
tween(playerSlideGraphics, {
scaleX: 1.2,
scaleY: 0.8
}, {
duration: 200,
easing: tween.linear
});
}
});
LK.setTimeout(function () {
// End slide with slight bounce back to normal
tween(playerSlideGraphics, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
self.isSliding = false;
// Show normal sprite, hide others
playerGraphics.visible = true;
playerJumpGraphics.visible = false;
playerSlideGraphics.visible = false;
}
});
}, 600);
};
self.changeLane = function (direction) {
if (self.isDead) return;
// Direction: -1 for up, 1 for down
var newLane = self.lane + direction;
if (newLane < 0 || newLane > 2) return;
self.lane = newLane;
var targetY = lanePositions[self.lane];
tween(self, {
y: targetY
}, {
duration: 200,
easing: tween.easeOut
});
};
self.die = function () {
if (self.isDead) return;
self.isDead = true;
LK.getSound('hit').play();
// Flash the player red
LK.effects.flashObject(self, 0xff0000, 500);
// Create dramatic death effect with multiple stages
// First - impact effect
tween(self, {
scaleX: 1.3,
scaleY: 0.7,
rotation: 0.2
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
// Second - spin and fade
tween(self, {
rotation: Math.PI * 2,
alpha: 0.6,
scaleX: 0.7,
scaleY: 0.7,
y: self.y + 50
}, {
duration: 600,
easing: tween.easeIn,
onFinish: function onFinish() {
// Final bounce
tween(self, {
y: self.y + 100,
alpha: 0.3,
rotation: Math.PI * 3,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 250,
easing: tween.bounceOut
});
}
});
}
});
// Screen flash and game over with slight delay
LK.effects.flashScreen(0xff0000, 300);
LK.setTimeout(function () {
LK.showGameOver();
}, 1000);
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Game constants
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var lanePositions = [GAME_HEIGHT * 0.35, GAME_HEIGHT * 0.5, GAME_HEIGHT * 0.65];
var GROUND_Y = GAME_HEIGHT * 0.8;
var gameSpeed = 10;
var gameSpeedIncrement = 0.1;
var obstacleFrequency = 100; // Frames between obstacle generation
var coinFrequency = 150; // Frames between coin generation
var score = 0;
var distance = 0;
var isGameRunning = false;
// Game elements
var player;
var obstacles = [];
var coins = [];
var ground;
var ceiling;
var lastObstacleFrame = 0;
var lastCoinFrame = 0;
// GUI elements
var scoreTxt;
var distanceTxt;
var highScoreTxt;
// Game setup
function setupGame() {
// Add background first so it's behind everything
var background = game.addChild(LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
}));
// Create ground
ground = game.addChild(LK.getAsset('ground', {
anchorX: 0,
anchorY: 0,
x: 0,
y: GROUND_Y
}));
// Create ceiling
ceiling = game.addChild(LK.getAsset('ceiling', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
}));
// Create player
player = new Player();
player.x = GAME_WIDTH * 0.2;
player.y = GROUND_Y - player.height / 2; // Position player on the ground
game.addChild(player);
// Setup score text with shadow effect
scoreTxt = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 5
});
scoreTxt.anchor.set(0, 0);
scoreTxt.x = 50;
scoreTxt.y = 50;
LK.gui.top.addChild(scoreTxt);
// Setup distance text with shadow effect
distanceTxt = new Text2('Distance: 0m', {
size: 80,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 5
});
distanceTxt.anchor.set(0, 0);
distanceTxt.x = 50;
distanceTxt.y = 150;
LK.gui.top.addChild(distanceTxt);
// Setup high score text with shadow effect
highScoreTxt = new Text2('High Score: ' + storage.highScore, {
size: 80,
fill: 0xFFD700,
stroke: 0x000000,
strokeThickness: 5
});
highScoreTxt.anchor.set(1, 0);
highScoreTxt.x = GAME_WIDTH - 50;
highScoreTxt.y = 50;
LK.gui.top.addChild(highScoreTxt);
// Reset game state
score = 0;
distance = 0;
gameSpeed = 10;
isGameRunning = true;
// Start background music
LK.playMusic('gameMusic');
}
// Create a new obstacle
function createObstacle() {
var obstacle = new Obstacle();
var lane = Math.floor(Math.random() * 3); // Random lane
var type = Math.random() > 0.5 ? "high" : "low"; // Random type
obstacle.setup(type, lane);
obstacle.x = GAME_WIDTH + obstacle.width;
game.addChild(obstacle);
// Animate the obstacle coming towards the player
var targetX = -obstacle.width; // Final position off the left side of the screen
var duration = (GAME_WIDTH + obstacle.width * 2) / gameSpeed * 1000 / 60; // Duration based on game speed
tween(obstacle, {
x: targetX
}, {
duration: duration,
easing: tween.linear
});
obstacles.push(obstacle);
lastObstacleFrame = LK.ticks;
}
// Create a new coin
function createCoin() {
var coin = new Coin();
var lane = 0; // Lane doesn't matter as coins are on ground now, but we keep the parameter for consistency
coin.setup(lane);
coin.x = GAME_WIDTH + coin.width;
game.addChild(coin);
// Animate the coin coming towards the player
var targetX = -coin.width; // Final position off the left side of the screen
var duration = (GAME_WIDTH + coin.width * 2) / gameSpeed * 1000 / 60; // Duration based on game speed
tween(coin, {
x: targetX
}, {
duration: duration,
easing: tween.linear
});
coins.push(coin);
lastCoinFrame = LK.ticks;
}
// Check for collisions
function checkCollisions() {
// Check obstacle collisions
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
if (player.intersects(obstacle)) {
// Check if player can pass:
// - If jumping over high obstacle
// - If sliding under low obstacle
var canPass = false;
if (obstacle.type === "high" && player.isJumping) {
canPass = true;
} else if (obstacle.type === "low" && player.isSliding) {
canPass = true;
} else if (player.lane !== obstacle.lane && obstacle.lane !== 0) {
// Only consider lane difference if obstacle is not on ground level
canPass = true;
}
if (!canPass) {
player.die();
isGameRunning = false;
// Update high score
if (score > storage.highScore) {
storage.highScore = score;
highScoreTxt.setText('High Score: ' + storage.highScore);
}
}
}
}
// Check coin collisions
for (var j = 0; j < coins.length; j++) {
var coin = coins[j];
if (!coin.collected && player.intersects(coin)) {
coin.collect();
score += 10;
scoreTxt.setText('Score: ' + score);
}
}
}
// Update game function
function updateGame() {
if (!isGameRunning) return;
// Update distance and increase difficulty
distance += Math.floor(gameSpeed / 10);
distanceTxt.setText('Distance: ' + distance + 'm');
// Increase game speed
var oldSpeed = gameSpeed;
// Speed increases faster after 100m
if (distance > 100) {
gameSpeed += gameSpeedIncrement * 0.03;
} else {
gameSpeed += gameSpeedIncrement * 0.01;
}
// If game speed changed significantly, update existing tweens
if (gameSpeed > oldSpeed * 1.1) {
// Update obstacle speeds
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
// Stop current tween
tween.stop(obstacle, {
x: true
});
// Create new tween with updated speed
var remainingDistance = obstacle.x + obstacle.width;
var duration = remainingDistance / gameSpeed * 1000 / 60;
tween(obstacle, {
x: -obstacle.width
}, {
duration: duration,
easing: tween.linear
});
}
// Update coin speeds
for (var j = 0; j < coins.length; j++) {
var coin = coins[j];
if (!coin.collected) {
// Stop current tween
tween.stop(coin, {
x: true
});
// Create new tween with updated speed
var coinRemainingDistance = coin.x + coin.width;
var coinDuration = coinRemainingDistance / gameSpeed * 1000 / 60;
tween(coin, {
x: -coin.width
}, {
duration: coinDuration,
easing: tween.linear
});
}
}
}
// Generate obstacles
if (LK.ticks - lastObstacleFrame > obstacleFrequency) {
createObstacle();
// Obstacles appear more frequently as the game progresses
// After 100m, obstacles come much faster
if (distance > 100) {
obstacleFrequency = Math.max(30, 60 - Math.floor(distance / 200));
} else {
obstacleFrequency = Math.max(50, 100 - Math.floor(distance / 500));
}
}
// Generate coins
if (LK.ticks - lastCoinFrame > coinFrequency) {
createCoin();
}
// Update obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
if (obstacles[i].update()) {
obstacles.splice(i, 1);
}
}
// Update coins
for (var j = coins.length - 1; j >= 0; j--) {
if (coins[j].update()) {
coins.splice(j, 1);
}
}
// Check for collisions
checkCollisions();
}
// Handle swipe gestures
var startX = 0;
var startY = 0;
var isDragging = false;
var swipeThreshold = 50; // Minimum distance for a swipe
function handleDown(x, y, obj) {
startX = x;
startY = y;
isDragging = true;
}
function handleMove(x, y, obj) {
if (!isDragging || !isGameRunning) return;
var deltaX = x - startX;
var deltaY = y - startY;
var absX = Math.abs(deltaX);
var absY = Math.abs(deltaY);
// Only process significant movements
if (absX < swipeThreshold && absY < swipeThreshold) return;
// Determine swipe direction
if (absY > absX) {
// Vertical swipe
if (deltaY < 0) {
// Swipe up - jump
player.jump();
} else {
// Swipe down - slide
player.slide();
}
} else {
// Horizontal swipe
if (deltaY < -swipeThreshold) {
// Swipe up left/right - change lane up
player.changeLane(-1);
} else if (deltaY > swipeThreshold) {
// Swipe down left/right - change lane down
player.changeLane(1);
}
}
// Reset for next swipe
startX = x;
startY = y;
}
function handleUp(x, y, obj) {
isDragging = false;
}
// Set up event handlers
game.down = handleDown;
game.move = handleMove;
game.up = handleUp;
// Game update function
game.update = function () {
// Initialize game on first update
if (!player) {
setupGame();
}
// Update game state
updateGame();
};