User prompt
remove the image from the back button
User prompt
remove the assets from, restart, how to play and start buttons and add a plain text
User prompt
make the restart button bigger
User prompt
remove the title of the game
User prompt
add the title of the game as asset
User prompt
remove the texts of "start game", "how to play", and "restart". Only the assets will shown
User prompt
the assets won't shown
User prompt
add assets for each of these three buttons
User prompt
change the images of the "start", "how to play", and restart buttons
User prompt
the assets of the game shown over the game over screen please fix it
User prompt
remake the game over screen
User prompt
Please fix the bug: 'ReferenceError: jumpIndicator is not defined' in or related to this line: 'if (jumpIndicator) {' Line Number: 256
User prompt
remove the jump mechanic and indicator entirely
User prompt
add a coin counter at the left top of the screen with a cool animation ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
the indicator will starts immediately when i started to hold the screen
User prompt
the more you hold the more runner will jump but it will be a limit to it
User prompt
remove the customize option instead add the indicator as asset
User prompt
No let me change the asset of the indicator
User prompt
I want to change the indicator image however i want to
User prompt
add a jump indicator next to runner to show the player how much the runner will jump
User prompt
Please fix the bug: 'GameState is not defined' in or related to this line: 'var currentState = GameState.MENU;' Line Number: 360
User prompt
add a "start game" and "how to play" screen on every game launch
User prompt
Please fix the bug: 'TypeError: Cannot read properties of undefined (reading 'then')' in or related to this line: 'tween(coinIcon, {' Line Number: 416 ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'TypeError: coins.push is not a function' in or related to this line: 'coins.push(coin);' Line Number: 296
User prompt
add a coin system
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Coin
var Coin = Container.expand(function () {
var self = Container.call(this);
var coinGfx = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
// Lane: 0,1,2
self.lane = 1;
// For collision detection
self.isCoin = true;
return self;
});
// UI Elements class for start menu and instructions
var GameUI = Container.expand(function () {
var self = Container.call(this);
// Create title text
self.title = new Text2('ENDLESS RUNNER', {
size: 150,
fill: 0xFFFFFF
});
self.title.anchor.set(0.5, 0.5);
self.title.x = 2048 / 2;
self.title.y = 800;
self.addChild(self.title);
// Create start button
self.startBtn = new Container();
var startBtnBg = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 1.2
});
startBtnBg.tint = 0x22CC44;
self.startBtn.addChild(startBtnBg);
var startText = new Text2('START GAME', {
size: 100,
fill: 0xFFFFFF
});
startText.anchor.set(0.5, 0.5);
self.startBtn.addChild(startText);
self.startBtn.x = 2048 / 2;
self.startBtn.y = 1200;
self.startBtn.interactive = true;
self.addChild(self.startBtn);
// Create how to play button
self.instructionsBtn = new Container();
var instBtnBg = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 1.2
});
instBtnBg.tint = 0x2288FF;
self.instructionsBtn.addChild(instBtnBg);
var instText = new Text2('HOW TO PLAY', {
size: 100,
fill: 0xFFFFFF
});
instText.anchor.set(0.5, 0.5);
self.instructionsBtn.addChild(instText);
self.instructionsBtn.x = 2048 / 2;
self.instructionsBtn.y = 1400;
self.instructionsBtn.interactive = true;
self.addChild(self.instructionsBtn);
// Create instructions screen (initially hidden)
self.instructions = new Container();
self.instructions.visible = false;
var instTitle = new Text2('HOW TO PLAY', {
size: 120,
fill: 0xFFFFFF
});
instTitle.anchor.set(0.5, 0);
instTitle.x = 2048 / 2;
instTitle.y = 600;
self.instructions.addChild(instTitle);
var instText1 = new Text2('• SWIPE LEFT/RIGHT to change lanes', {
size: 80,
fill: 0xFFFFFF
});
instText1.anchor.set(0.5, 0);
instText1.x = 2048 / 2;
instText1.y = 800;
self.instructions.addChild(instText1);
var instText2 = new Text2('• TAP to jump over obstacles', {
size: 80,
fill: 0xFFFFFF
});
instText2.anchor.set(0.5, 0);
instText2.x = 2048 / 2;
instText2.y = 900;
self.instructions.addChild(instText2);
var instText3 = new Text2('• HOLD TAP LONGER for higher jumps', {
size: 80,
fill: 0xFFFFFF
});
instText3.anchor.set(0.5, 0);
instText3.x = 2048 / 2;
instText3.y = 1000;
self.instructions.addChild(instText3);
var instText3b = new Text2('• Watch the green bar for jump power', {
size: 80,
fill: 0xFFFFFF
});
instText3b.anchor.set(0.5, 0);
instText3b.x = 2048 / 2;
instText3b.y = 1100;
self.instructions.addChild(instText3b);
var instText4 = new Text2('• Collect coins for bonus points', {
size: 80,
fill: 0xFFFFFF
});
instText4.anchor.set(0.5, 0);
instText4.x = 2048 / 2;
instText4.y = 1200;
self.instructions.addChild(instText4);
// Back button for instructions
self.backBtn = new Container();
var backBtnBg = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 1
});
backBtnBg.tint = 0xFF6644;
self.backBtn.addChild(backBtnBg);
var backText = new Text2('BACK', {
size: 80,
fill: 0xFFFFFF
});
backText.anchor.set(0.5, 0.5);
self.backBtn.addChild(backText);
self.backBtn.x = 2048 / 2;
self.backBtn.y = 1400;
self.backBtn.interactive = true;
self.instructions.addChild(self.backBtn);
self.addChild(self.instructions);
// Button press handlers
self.startBtn.down = function () {
tween(self.startBtn, {
scaleX: 0.95,
scaleY: 0.95
}, {
duration: 100
});
};
self.startBtn.up = function () {
tween(self.startBtn, {
scaleX: 1,
scaleY: 1
}, {
duration: 100,
onFinish: function onFinish() {
if (typeof self.onStartClick === 'function') {
self.onStartClick();
}
}
});
};
self.instructionsBtn.down = function () {
tween(self.instructionsBtn, {
scaleX: 0.95,
scaleY: 0.95
}, {
duration: 100
});
};
self.instructionsBtn.up = function () {
tween(self.instructionsBtn, {
scaleX: 1,
scaleY: 1
}, {
duration: 100,
onFinish: function onFinish() {
if (typeof self.onInstructionsClick === 'function') {
self.onInstructionsClick();
}
}
});
};
self.backBtn.down = function () {
tween(self.backBtn, {
scaleX: 0.95,
scaleY: 0.95
}, {
duration: 100
});
};
self.backBtn.up = function () {
tween(self.backBtn, {
scaleX: 1,
scaleY: 1
}, {
duration: 100,
onFinish: function onFinish() {
if (typeof self.onBackClick === 'function') {
self.onBackClick();
}
}
});
};
// Set show/hide methods
self.showMenu = function () {
self.visible = true;
self.title.visible = true;
self.startBtn.visible = true;
self.instructionsBtn.visible = true;
self.instructions.visible = false;
};
self.showInstructions = function () {
self.visible = true;
self.title.visible = false;
self.startBtn.visible = false;
self.instructionsBtn.visible = false;
self.instructions.visible = true;
};
self.hide = function () {
self.visible = false;
};
return self;
});
// JumpIndicator for showing jump power
var JumpIndicator = Container.expand(function () {
var self = Container.call(this);
// Create the background bar
self.bgBar = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.15
});
self.bgBar.tint = 0x333333;
self.addChild(self.bgBar);
// Create the fill bar (inner progress)
self.fillBar = LK.getAsset('obstacle', {
anchorX: 0,
anchorY: 0.5,
scaleX: 0,
scaleY: 0.12
});
self.fillBar.tint = 0x22DD88;
self.fillBar.x = -self.bgBar.width / 2;
self.addChild(self.fillBar);
// Initialize as hidden
self.visible = false;
// Update the progress (0-1)
self.updateProgress = function (progress) {
progress = Math.max(0, Math.min(1, progress));
self.fillBar.scaleX = progress * 0.8;
};
// Show the indicator
self.show = function () {
self.visible = true;
self.updateProgress(0);
// Make it appear with animation
self.alpha = 0;
tween(self, {
alpha: 1
}, {
duration: 200,
easing: tween.easeOut
});
};
// Hide the indicator
self.hide = function () {
tween(self, {
alpha: 0
}, {
duration: 200,
easing: tween.easeIn,
onFinish: function onFinish() {
self.visible = false;
}
});
};
return self;
});
// Obstacle
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obsGfx = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
// Lane: 0,1,2
self.lane = 1;
// For collision detection
self.isObstacle = true;
return self;
});
// Runner (player character)
var Runner = Container.expand(function () {
var self = Container.call(this);
var runnerGfx = self.attachAsset('runner', {
anchorX: 0.5,
anchorY: 0.5
});
// Current lane: 0 (left), 1 (center), 2 (right)
self.lane = 1;
// --- JUMP SYSTEM ---
self.isJumping = false;
self.jumpTime = 0;
self.jumpDuration = 32; // frames (about 0.53s at 60fps)
self.jumpHeight = 260; // px, how high the jump arc is
self.jump = function (customDuration) {
if (self.isJumping) return;
self.isJumping = true;
self.jumpTime = 0;
if (typeof customDuration === "number" && customDuration > 0) {
self.jumpDuration = customDuration;
} else {
self.jumpDuration = 32; // default
}
};
self.moveToLane = function (lane, duration) {
self.lane = lane;
var targetX = laneToX(lane);
tween(self, {
x: targetX
}, {
duration: duration || 120,
easing: tween.cubicOut,
onFinish: function onFinish() {
// Update jump indicator position when lane changes
if (jumpIndicator) {
tween(jumpIndicator, {
x: targetX + 160
}, {
duration: 100,
easing: tween.easeOut
});
}
}
});
};
// Called every frame by game.update
self.update = function () {
if (self.isJumping) {
self.jumpTime++;
var t = self.jumpTime / self.jumpDuration;
var arc = -4 * self.jumpHeight * (t - 0.5) * (t - 0.5) + self.jumpHeight;
self.z = arc; // z is not rendered, but we use it for logic
self.yJumpOffset = -arc;
self.y = 2732 - 400 + self.yJumpOffset;
// Update jump indicator position to follow runner during jump
if (jumpIndicator && jumpIndicator.visible) {
jumpIndicator.y = self.y;
}
if (self.jumpTime >= self.jumpDuration) {
self.isJumping = false;
self.jumpTime = 0;
self.yJumpOffset = 0;
self.y = 2732 - 400;
self.jumpDuration = 32; // reset to default after jump
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// --- ROAD BACKGROUND ---
// Character: Red box
// Obstacle: Gray box
// Coin: Yellow ellipse
var roadBg1 = LK.getAsset('road', {
anchorX: 0.5,
anchorY: 0
});
roadBg1.x = 2048 / 2;
roadBg1.y = 0;
game.addChild(roadBg1);
var roadBg2 = LK.getAsset('road', {
anchorX: 0.5,
anchorY: 0
});
roadBg2.x = 2048 / 2;
roadBg2.y = -2732;
game.addChild(roadBg2);
// --- LANE SYSTEM ---
var NUM_LANES = 3;
var LANE_WIDTH = 2048 / NUM_LANES; // Use full width
var LANE_START_X = LANE_WIDTH / 2; // Center of first lane at left edge
function laneToX(lane) {
// lane: 0 (left), 1 (center), 2 (right)
return LANE_START_X + lane * LANE_WIDTH;
}
// Game state manager
var GameState = {
MENU: 0,
INSTRUCTIONS: 1,
PLAYING: 2
};
// --- GAMEPLAY VARIABLES ---
var runner;
var obstacles = [];
var coinObjects = []; // Array for coin instances
var speed = 18; // Initial speed (pixels per frame)
var speedIncrease = 0.012; // Speed up per tick
var spawnTimer = 0;
var coinSpawnTimer = 0;
var minObstacleGap = 420; // Minimum vertical gap between obstacles
var lastObstacleY = 0;
var isGameOver = false;
// Coin system variables
var coinCombo = 0;
var coinComboTimer = 0;
var coinComboTimeout = 60; // frames (1 second) to maintain combo
var coinComboText = null;
// Game state management
var currentState = GameState.MENU;
var gameUI = new GameUI();
game.addChild(gameUI);
// Setup UI event handlers
gameUI.onStartClick = function () {
currentState = GameState.PLAYING;
gameUI.hide();
resetGame();
// Show score and coin counter
scoreTxt.visible = true;
coinCounter.visible = true;
};
gameUI.onInstructionsClick = function () {
currentState = GameState.INSTRUCTIONS;
gameUI.showInstructions();
};
gameUI.onBackClick = function () {
currentState = GameState.MENU;
gameUI.showMenu();
};
// Initial setup
gameUI.showMenu();
// --- SCORE AND COINS ---
var score = 0;
var coins = 0; // Count of collected coins
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Hide score initially
scoreTxt.visible = false;
// Coin counter display
var coinCounter = new Container();
var coinIcon = LK.getAsset('coin', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
coinIcon.x = 60;
coinCounter.addChild(coinIcon);
var coinsTxt = new Text2('0', {
size: 100,
fill: 0xFFCC00
});
coinsTxt.anchor.set(0, 0.5);
coinsTxt.x = coinIcon.x + 80;
coinsTxt.y = coinIcon.y;
coinCounter.addChild(coinsTxt);
// Position the coin counter at the top right
coinCounter.x = 150;
coinCounter.y = 120;
LK.gui.topRight.addChild(coinCounter);
// Hide coin counter initially
coinCounter.visible = false;
// Function to reset game state
function resetGame() {
// Clean up arrays
for (var i = 0; i < obstacles.length; i++) {
obstacles[i].destroy();
}
for (var j = 0; j < coinObjects.length; j++) {
coinObjects[j].destroy();
}
obstacles = [];
coinObjects = [];
isGameOver = false;
speed = 18;
score = 0;
coins = 0;
coinCombo = 0;
coinComboTimer = 0;
if (coinComboText) {
coinComboText.destroy();
coinComboText = null;
}
LK.setScore(0);
scoreTxt.setText('0');
coinsTxt.setText('0');
runner.x = laneToX(1);
runner.lane = 1;
runner.y = 2732 - 400;
runner.isJumping = false;
// Reset jump indicator position and hide it
jumpIndicator.x = runner.x + 160;
jumpIndicator.y = runner.y;
jumpIndicator.hide();
jumpIndicator.updateProgress(0);
spawnTimer = 0;
coinSpawnTimer = 0;
roadBg1.y = 0;
roadBg2.y = -2732;
}
// --- INIT RUNNER ---
runner = new Runner();
runner.y = 2732 - 400;
runner.x = laneToX(1);
runner.lane = 1;
game.addChild(runner);
// --- INIT JUMP INDICATOR ---
var jumpIndicator = new JumpIndicator();
jumpIndicator.x = laneToX(1) + 160; // Position to the right of runner
jumpIndicator.y = 2732 - 400; // Same Y as runner
game.addChild(jumpIndicator);
// --- INPUT HANDLING ---
var dragStartX = null;
var dragStartLane = null;
var dragActive = false;
// For jump hold logic
var jumpHoldActive = false;
var jumpHoldStartTick = 0;
var jumpHoldMaxDuration = 48; // max frames to extend jump (0.8s)
var jumpHoldMinDuration = 24; // min frames to allow (0.4s)
var jumpHoldCurrentDuration = 0;
function handleMove(x, y, obj) {
if (currentState !== GameState.PLAYING) return;
if (dragActive && dragStartX !== null) {
var dx = x - dragStartX;
if (Math.abs(dx) > 120) {
// Swipe detected
if (dx > 0 && runner.lane < NUM_LANES - 1) {
runner.moveToLane(runner.lane + 1);
dragStartX = x;
} else if (dx < 0 && runner.lane > 0) {
runner.moveToLane(runner.lane - 1);
dragStartX = x;
}
}
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
if (currentState !== GameState.PLAYING) return;
dragActive = true;
dragStartX = x;
dragStartLane = runner.lane;
// Start jump hold if not swiping
jumpHoldActive = false;
jumpHoldStartTick = LK.ticks;
jumpHoldCurrentDuration = 0;
if (!runner.isJumping) {
// We'll only trigger jump on up if it's a tap, but we start tracking hold here
jumpHoldActive = true;
// Show jump indicator
jumpIndicator.show();
// Set jumpIndicator position to match current runner lane
jumpIndicator.x = runner.x + 160;
}
};
game.up = function (x, y, obj) {
if (currentState !== GameState.PLAYING) return;
// If it was a tap (not a swipe), trigger jump
if (dragActive && dragStartX !== null && Math.abs(x - dragStartX) < 40) {
if (!runner.isJumping && jumpHoldActive) {
// Calculate hold duration in frames
var holdFrames = Math.max(1, Math.min(jumpHoldMaxDuration, LK.ticks - jumpHoldStartTick));
// Clamp to min/max
if (holdFrames < jumpHoldMinDuration) holdFrames = jumpHoldMinDuration;
if (holdFrames > jumpHoldMaxDuration) holdFrames = jumpHoldMaxDuration;
jumpHoldCurrentDuration = holdFrames;
runner.jump(jumpHoldCurrentDuration);
// Hide the jump indicator once the jump starts
jumpIndicator.hide();
}
}
jumpHoldActive = false;
dragActive = false;
dragStartX = null;
dragStartLane = null;
};
// --- TAP TO MOVE (for single taps) ---
game.tap = function (x, y, obj) {
// Not supported by LK, so handled via down/up quick tap
// (No implementation needed)
};
// --- SPAWN OBSTACLES & COINS ---
function spawnObstacle() {
var lane = Math.floor(Math.random() * NUM_LANES);
var obs = new Obstacle();
obs.lane = lane;
obs.x = laneToX(lane);
obs.y = -180;
obstacles.push(obs);
game.addChild(obs);
lastObstacleY = obs.y;
}
function spawnCoin() {
// Prevent coins from spawning in the same lane and too close to obstacles, and at the same Y as obstacles
var availableLanes = [0, 1, 2];
var coinSpawnY = -120;
// Remove lanes with obstacles too close to spawn Y or at the same Y
for (var i = 0; i < obstacles.length; i++) {
var obs = obstacles[i];
// If obstacle is in the same lane and within 320px vertically of coin spawn Y (-120)
if (Math.abs(obs.y - coinSpawnY) < 320) {
var idx = availableLanes.indexOf(obs.lane);
if (idx !== -1) availableLanes.splice(idx, 1);
}
// Prevent coin from spawning at the same Y as any obstacle (even in other lanes)
if (Math.abs(obs.y - coinSpawnY) < 1) {
// If any obstacle is at the same Y, skip coin spawn entirely
return;
}
}
if (availableLanes.length === 0) return; // No safe lane, skip coin spawn
var lane = availableLanes[Math.floor(Math.random() * availableLanes.length)];
var coin = new Coin();
coin.lane = lane;
coin.x = laneToX(lane);
coin.y = coinSpawnY;
coinObjects.push(coin);
game.addChild(coin);
}
// --- COLLISION DETECTION ---
function checkCollision(a, b) {
// Simple AABB
var dx = Math.abs(a.x - b.x);
var dy = Math.abs(a.y - b.y);
var aw = a.width || 180,
ah = a.height || 180;
var bw = b.width || 180,
bh = b.height || 180;
return dx < aw / 2 + bw / 2 - 20 && dy < ah / 2 + bh / 2 - 20;
}
// --- GAME LOOP ---
game.update = function () {
if (isGameOver || currentState !== GameState.PLAYING) return;
// --- Runner jump update ---
if (runner && typeof runner.update === "function") {
runner.update();
}
// Update jump indicator if hold is active
if (jumpHoldActive && !runner.isJumping) {
var holdFrames = Math.min(jumpHoldMaxDuration, LK.ticks - jumpHoldStartTick);
var progress = Math.max(0, Math.min(1, holdFrames / jumpHoldMaxDuration));
jumpIndicator.updateProgress(progress);
// Update indicator y-position to match runner
jumpIndicator.y = runner.y;
}
// Scroll road background
roadBg1.y += speed;
roadBg2.y += speed;
// Loop backgrounds
if (roadBg1.y >= 2732) {
roadBg1.y = roadBg2.y - 2732;
}
if (roadBg2.y >= 2732) {
roadBg2.y = roadBg1.y - 2732;
}
// Increase speed
speed += speedIncrease;
// Handle coin combo timer
if (coinCombo > 0) {
coinComboTimer--;
if (coinComboTimer <= 0) {
// Reset combo when timer expires
coinCombo = 0;
if (coinComboText) {
coinComboText.destroy();
coinComboText = null;
}
}
}
// Move obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
var obs = obstacles[i];
obs.y += speed;
if (obs.y > 2732 + 200) {
obs.destroy();
obstacles.splice(i, 1);
continue;
}
// Collision with runner
if (checkCollision(obs, runner) && !runner.isJumping) {
isGameOver = true;
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
return;
}
}
// Move coins
for (var j = coinObjects.length - 1; j >= 0; j--) {
var coin = coinObjects[j];
coin.y += speed;
if (coin.y > 2732 + 120) {
coin.destroy();
coinObjects.splice(j, 1);
continue;
}
// Collision with runner
if (checkCollision(coin, runner)) {
// Increase combo counter
coinCombo++;
coinComboTimer = coinComboTimeout;
// Calculate bonus based on combo
var bonus = Math.min(coinCombo, 5); // Cap bonus at 5x
var coinValue = 10 * bonus;
// Collect coin
coins += 1;
score += coinValue;
LK.setScore(score);
scoreTxt.setText(score);
coinsTxt.setText(coins);
// Show combo text
if (coinComboText) {
coinComboText.destroy();
}
if (coinCombo > 1) {
coinComboText = new Text2('x' + coinCombo + ' COMBO!', {
size: 80,
fill: 0xFFCC00
});
coinComboText.anchor.set(0.5, 0.5);
coinComboText.x = coin.x;
coinComboText.y = coin.y - 100;
game.addChild(coinComboText);
// Animate combo text
tween(coinComboText, {
y: coinComboText.y - 100,
alpha: 0
}, {
duration: 1000,
easing: tween.cubicOut,
onFinish: function onFinish() {
if (coinComboText) {
coinComboText.destroy();
coinComboText = null;
}
}
});
}
// Add sparkle effect
LK.effects.flashObject(coin, 0xffff00, 200);
// Play coin sound
LK.getSound('coin_collect').play();
// Play a bounce animation on the coin counter
tween(coinIcon, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 150,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(coinIcon, {
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 150,
easing: tween.easeOut
});
}
});
coin.destroy();
coinObjects.splice(j, 1);
continue;
}
}
// Spawn obstacles
spawnTimer -= speed;
if (spawnTimer <= 0) {
spawnObstacle();
// Next spawn in 700-1100 px
spawnTimer = minObstacleGap + Math.floor(Math.random() * 400);
}
// Spawn coins
coinSpawnTimer -= speed;
if (coinSpawnTimer <= 0) {
spawnCoin();
// Next coin in 400-900 px
coinSpawnTimer = 400 + Math.floor(Math.random() * 500);
}
};
// --- RESET ON GAME OVER ---
game.on('destroy', function () {
// Clean up arrays
for (var i = 0; i < obstacles.length; i++) {
obstacles[i].destroy();
}
for (var j = 0; j < coinObjects.length; j++) {
coinObjects[j].destroy();
}
obstacles = [];
coinObjects = [];
isGameOver = false;
speed = 18;
score = 0;
coins = 0;
coinCombo = 0;
coinComboTimer = 0;
if (coinComboText) {
coinComboText.destroy();
coinComboText = null;
}
LK.setScore(0);
scoreTxt.setText('0');
coinsTxt.setText('0');
runner.x = laneToX(1);
runner.lane = 1;
runner.y = 2732 - 400;
spawnTimer = 0;
coinSpawnTimer = 0;
roadBg1.y = 0;
roadBg2.y = -2732;
// Return to menu
currentState = GameState.MENU;
gameUI.showMenu();
// Hide game UI
scoreTxt.visible = false;
coinCounter.visible = false;
}); ===================================================================
--- original.js
+++ change.js
@@ -105,15 +105,23 @@
instText3.anchor.set(0.5, 0);
instText3.x = 2048 / 2;
instText3.y = 1000;
self.instructions.addChild(instText3);
+ var instText3b = new Text2('• Watch the green bar for jump power', {
+ size: 80,
+ fill: 0xFFFFFF
+ });
+ instText3b.anchor.set(0.5, 0);
+ instText3b.x = 2048 / 2;
+ instText3b.y = 1100;
+ self.instructions.addChild(instText3b);
var instText4 = new Text2('• Collect coins for bonus points', {
size: 80,
fill: 0xFFFFFF
});
instText4.anchor.set(0.5, 0);
instText4.x = 2048 / 2;
- instText4.y = 1100;
+ instText4.y = 1200;
self.instructions.addChild(instText4);
// Back button for instructions
self.backBtn = new Container();
var backBtnBg = LK.getAsset('obstacle', {
@@ -218,8 +226,64 @@
self.visible = false;
};
return self;
});
+// JumpIndicator for showing jump power
+var JumpIndicator = Container.expand(function () {
+ var self = Container.call(this);
+ // Create the background bar
+ self.bgBar = LK.getAsset('obstacle', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ scaleX: 0.8,
+ scaleY: 0.15
+ });
+ self.bgBar.tint = 0x333333;
+ self.addChild(self.bgBar);
+ // Create the fill bar (inner progress)
+ self.fillBar = LK.getAsset('obstacle', {
+ anchorX: 0,
+ anchorY: 0.5,
+ scaleX: 0,
+ scaleY: 0.12
+ });
+ self.fillBar.tint = 0x22DD88;
+ self.fillBar.x = -self.bgBar.width / 2;
+ self.addChild(self.fillBar);
+ // Initialize as hidden
+ self.visible = false;
+ // Update the progress (0-1)
+ self.updateProgress = function (progress) {
+ progress = Math.max(0, Math.min(1, progress));
+ self.fillBar.scaleX = progress * 0.8;
+ };
+ // Show the indicator
+ self.show = function () {
+ self.visible = true;
+ self.updateProgress(0);
+ // Make it appear with animation
+ self.alpha = 0;
+ tween(self, {
+ alpha: 1
+ }, {
+ duration: 200,
+ easing: tween.easeOut
+ });
+ };
+ // Hide the indicator
+ self.hide = function () {
+ tween(self, {
+ alpha: 0
+ }, {
+ duration: 200,
+ easing: tween.easeIn,
+ onFinish: function onFinish() {
+ self.visible = false;
+ }
+ });
+ };
+ return self;
+});
// Obstacle
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obsGfx = self.attachAsset('obstacle', {
@@ -262,9 +326,20 @@
tween(self, {
x: targetX
}, {
duration: duration || 120,
- easing: tween.cubicOut
+ easing: tween.cubicOut,
+ onFinish: function onFinish() {
+ // Update jump indicator position when lane changes
+ if (jumpIndicator) {
+ tween(jumpIndicator, {
+ x: targetX + 160
+ }, {
+ duration: 100,
+ easing: tween.easeOut
+ });
+ }
+ }
});
};
// Called every frame by game.update
self.update = function () {
@@ -274,8 +349,12 @@
var arc = -4 * self.jumpHeight * (t - 0.5) * (t - 0.5) + self.jumpHeight;
self.z = arc; // z is not rendered, but we use it for logic
self.yJumpOffset = -arc;
self.y = 2732 - 400 + self.yJumpOffset;
+ // Update jump indicator position to follow runner during jump
+ if (jumpIndicator && jumpIndicator.visible) {
+ jumpIndicator.y = self.y;
+ }
if (self.jumpTime >= self.jumpDuration) {
self.isJumping = false;
self.jumpTime = 0;
self.yJumpOffset = 0;
@@ -430,8 +509,13 @@
runner.x = laneToX(1);
runner.lane = 1;
runner.y = 2732 - 400;
runner.isJumping = false;
+ // Reset jump indicator position and hide it
+ jumpIndicator.x = runner.x + 160;
+ jumpIndicator.y = runner.y;
+ jumpIndicator.hide();
+ jumpIndicator.updateProgress(0);
spawnTimer = 0;
coinSpawnTimer = 0;
roadBg1.y = 0;
roadBg2.y = -2732;
@@ -441,8 +525,13 @@
runner.y = 2732 - 400;
runner.x = laneToX(1);
runner.lane = 1;
game.addChild(runner);
+// --- INIT JUMP INDICATOR ---
+var jumpIndicator = new JumpIndicator();
+jumpIndicator.x = laneToX(1) + 160; // Position to the right of runner
+jumpIndicator.y = 2732 - 400; // Same Y as runner
+game.addChild(jumpIndicator);
// --- INPUT HANDLING ---
var dragStartX = null;
var dragStartLane = null;
var dragActive = false;
@@ -480,8 +569,12 @@
jumpHoldCurrentDuration = 0;
if (!runner.isJumping) {
// We'll only trigger jump on up if it's a tap, but we start tracking hold here
jumpHoldActive = true;
+ // Show jump indicator
+ jumpIndicator.show();
+ // Set jumpIndicator position to match current runner lane
+ jumpIndicator.x = runner.x + 160;
}
};
game.up = function (x, y, obj) {
if (currentState !== GameState.PLAYING) return;
@@ -494,8 +587,10 @@
if (holdFrames < jumpHoldMinDuration) holdFrames = jumpHoldMinDuration;
if (holdFrames > jumpHoldMaxDuration) holdFrames = jumpHoldMaxDuration;
jumpHoldCurrentDuration = holdFrames;
runner.jump(jumpHoldCurrentDuration);
+ // Hide the jump indicator once the jump starts
+ jumpIndicator.hide();
}
}
jumpHoldActive = false;
dragActive = false;
@@ -562,8 +657,16 @@
// --- Runner jump update ---
if (runner && typeof runner.update === "function") {
runner.update();
}
+ // Update jump indicator if hold is active
+ if (jumpHoldActive && !runner.isJumping) {
+ var holdFrames = Math.min(jumpHoldMaxDuration, LK.ticks - jumpHoldStartTick);
+ var progress = Math.max(0, Math.min(1, holdFrames / jumpHoldMaxDuration));
+ jumpIndicator.updateProgress(progress);
+ // Update indicator y-position to match runner
+ jumpIndicator.y = runner.y;
+ }
// Scroll road background
roadBg1.y += speed;
roadBg2.y += speed;
// Loop backgrounds
Pixel, 2d, Coin. In-Game asset. 2d. High contrast. No shadows
container, 2d, pixel. In-Game asset. 2d. High contrast. No shadows
character with a hat, 2d, pixel,. 2d. High contrast. No shadows
road, pixel, 2d. In-Game asset. 2d. High contrast. No shadows. In-Game asset
indicator, pixel, 2d. In-Game asset. 2d. High contrast. No shadows
Start button, pixel art. In-Game asset. 2d. High contrast. No shadows
how to play button, orange outline, white text, pixel art. In-Game asset. 2d. High contrast. No shadows
restart button, orange outline, white text, pixel art.. In-Game asset. 2d. High contrast. No shadows