/**** * 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();
};