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