User prompt
Don't add random colors make the it look better ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Better graphics
User prompt
When game over the text overlaps fix it
User prompt
Make the coins spawn on the ground and make the obstacles come faster after 100m
User prompt
Make the obstacles be on the ground
User prompt
Make the obstacles y level same as the player's y level
User prompt
Make the obstacles come to towards the player ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Make player on the ground
Code edit (1 edits merged)
Please save this source code
User prompt
Endless Dash
Initial prompt
Make a endless runner game
/**** * 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();
};