User prompt
animate gold
User prompt
make gold move a little bit right
User prompt
gold coin is not in the center of the gap
User prompt
add gold between gaps so that player can collect them
User prompt
landscape should also move as we move towards
User prompt
increase the gap between the pipes a little
User prompt
add background landscape and some clouds
User prompt
let's do it
User prompt
make top pipe upside down
User prompt
make to pipe flip horizontally
User prompt
make top pipe flip vertically
User prompt
pipes should be on the same vertical axis
User prompt
but rotated pipes should be stick to the top side of the screen
User prompt
pipes on the top side of the screen should be rotated 180 degrees
User prompt
decrease the gravitiy a little
User prompt
increase the bounce effect a little
User prompt
decrease the bounce effect by half
User prompt
slow down the starting pace, and decrease the gravity force
Code edit (1 edits merged)
Please save this source code
User prompt
Flappy Gap
User prompt
Note that, there shouldn't be any gap between the top pipe and the top screen, there shouldn't be any gap between the bottom pipe and the bottom screen. Player can only move towards the gap between the pipes
Initial prompt
make a flapy bird like game
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Character: the player-controlled ellipse var Character = Container.expand(function () { var self = Container.call(this); var _char = self.attachAsset('character', { anchorX: 0.5, anchorY: 0.5 }); // Physics self.velocity = 0; self.gravity = 2.2; self.flapStrength = -19; // Flap (jump) self.flap = function () { self.velocity = self.flapStrength; }; // Update position and velocity self.update = function () { // Always apply gravity and velocity, even during powerupActive self.velocity += self.gravity; self.y += self.velocity; }; // For collision detection self.getBounds = function () { return { x: self.x - _char.width / 2, y: self.y - _char.height / 2, width: _char.width, height: _char.height }; }; return self; }); // CometParticle: a single particle for the comet trail effect var CometParticle = Container.expand(function () { var self = Container.call(this); // Use pure white for a star-like effect var color = 0xffffff; // Use a bright ellipse as the particle var _shape = self.attachAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.22 + Math.random() * 0.18, scaleY: 0.16 + Math.random() * 0.14, color: color }); // Particle properties self.lifetime = 22 + Math.random() * 10; // frames self.age = 0; self.vx = -6 + Math.random() * -2; // leftward self.vy = (Math.random() - 0.5) * 4; // slight up/down self.alpha = 0.85 + Math.random() * 0.15; // Brighter self.update = function () { self.x += self.vx; self.y += self.vy; self.age++; // Fade out, but keep a bit brighter self.alpha *= 0.93; if (self.age > self.lifetime) { if (self.parent) self.parent.removeChild(self); self.destroy(); } }; return self; }); // Gold: collectible coin between pipes var Gold = Container.expand(function () { var self = Container.call(this); var _gold = self.attachAsset('gold', { anchorX: 0.5, anchorY: 0.5 }); self.collected = false; self._bobPhase = Math.random() * Math.PI * 2; // randomize phase so coins don't bob in sync self.update = function () { self.x += self.speed; // Animate gold with a gentle up-and-down bobbing motion self.y += Math.sin(LK.ticks / 18 + self._bobPhase) * 1.5; }; // For collision detection self.getBounds = function () { return { x: self.x - _gold.width / 2, y: self.y - _gold.height / 2, width: _gold.width, height: _gold.height }; }; return self; }); // PipePair: a pair of pipes (top and bottom) with a gap var PipePair = Container.expand(function () { var self = Container.call(this); // Pipe dimensions var pipeWidth = 220; var pipeHeight = 1200; var gapHeight = 500; // Will be set dynamically // Top pipe var topPipe = self.attachAsset('pipe_top', { anchorX: 0, anchorY: 1 }); // Bottom pipe var bottomPipe = self.attachAsset('pipe_bottom', { anchorX: 0, anchorY: 0 }); // Set pipes' positions based on gapY and gapHeight self.setGap = function (gapY, gapHeight) { // Top pipe: bottom at gapY topPipe.x = 0; topPipe.y = gapY; topPipe.height = gapY; // Bottom pipe: top at gapY+gapHeight bottomPipe.x = 0; bottomPipe.y = gapY + gapHeight; bottomPipe.height = 2732 - (gapY + gapHeight); }; // For collision detection self.getTopPipe = function () { return topPipe; }; self.getBottomPipe = function () { return bottomPipe; }; // Move pipes leftward self.update = function () { self.x += self.speed; }; // Set initial speed self.speed = -12; return self; }); // Powerup: special collectible (separate from Gold) var Powerup = Container.expand(function () { var self = Container.call(this); var _powerup = self.attachAsset('powerup', { anchorX: 0.5, anchorY: 0.5 }); self.collected = false; self._bobPhase = Math.random() * Math.PI * 2; self.update = function () { self.x += self.speed; // Animate with a gentle up-and-down bobbing motion self.y += Math.sin(LK.ticks / 18 + self._bobPhase) * 1.5; }; self.getBounds = function () { return { x: self.x - _powerup.width / 2, y: self.y - _powerup.height / 2, width: _powerup.width, height: _powerup.height }; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x4db8ff // Light blue sky }); /**** * Game Code ****/ // Pipes: green boxes, character: yellow ellipse, background: blue // Game constants // Background landscape and clouds // unique icon for powerup var GAME_WIDTH = 2048; var GAME_HEIGHT = 2732; var PIPE_WIDTH = 220; var PIPE_GAP_MIN = 480; var PIPE_GAP_MAX = 700; var PIPE_DISTANCE = 900; // Horizontal distance between pipes var PIPE_SPEED_START = -7; // Slower initial pipe speed var PIPE_SPEED_MAX = -28; var PIPE_FREQ_START = 120; // Pipes appear less frequently at start var PIPE_FREQ_MIN = 45; var GRAVITY_START = 1.05; // Slightly lower gravity for even gentler fall var FLAP_STRENGTH = -22; // Game variables var pipes = []; var golds = []; var character; var score = 0; var scoreTxt; var lastPipeTick = 0; var pipeFreq = PIPE_FREQ_START; var pipeSpeed = PIPE_SPEED_START; var gameStarted = false; var gameOver = false; // Powerup/auto-fly state var powerupActive = false; var powerupPipesLeft = 0; var autoFlyTargetY = null; var autoFlyPipeIndex = null; var autoFlyJustPassedPipe = false; // Comet particle system for powerup effect var cometParticles = []; // Add city background image (behind landscape and clouds) var bgCity = LK.getAsset('city', { anchorX: 0, anchorY: 1, x: 0, y: GAME_HEIGHT - 600, width: GAME_WIDTH, height: 900 }); var bgCity2 = LK.getAsset('city', { anchorX: 0, anchorY: 1, x: GAME_WIDTH, y: GAME_HEIGHT - 600, width: GAME_WIDTH, height: 900 }); game.addChild(bgCity); game.addChild(bgCity2); // Add background landscape (two for seamless looping) var bgLandscape = LK.getAsset('bg_landscape', { anchorX: 0, anchorY: 1, x: 0, y: GAME_HEIGHT, width: GAME_WIDTH, height: 600 }); var bgLandscape2 = LK.getAsset('bg_landscape', { anchorX: 0, anchorY: 1, x: GAME_WIDTH, y: GAME_HEIGHT, width: GAME_WIDTH, height: 600 }); game.addChild(bgLandscape); game.addChild(bgLandscape2); // Add clouds (3 clouds at different positions and scales) var clouds = []; for (var i = 0; i < 3; i++) { var cloud = LK.getAsset('cloud', { anchorX: 0.5, anchorY: 0.5, x: 400 + i * 600, y: 300 + i * 120, scaleX: 0.8 + 0.3 * Math.random(), scaleY: 0.8 + 0.3 * Math.random() }); game.addChild(cloud); clouds.push(cloud); } // Add score text to GUI scoreTxt = new Text2('0', { size: 150, fill: 0xFFFFFF, stroke: 0x222222, strokeThickness: 12 }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Center score text horizontally, avoid top left 100x100 scoreTxt.x = LK.gui.top.width / 2; scoreTxt.y = 40; // Add countdown text for auto-fly powerup var powerupCountdownTxt = new Text2('', { size: 100, fill: 0x00ff00, font: "Impact, Arial Black, Tahoma" }); powerupCountdownTxt.anchor.set(0.5, 0); powerupCountdownTxt.x = LK.gui.top.width / 2; powerupCountdownTxt.y = 200; powerupCountdownTxt.visible = false; LK.gui.top.addChild(powerupCountdownTxt); // Add restart button to GUI (top right, not overlapping score) var restartBtn = new Text2('⟳', { size: 120, fill: 0xffffff, font: "Impact, Arial Black, Tahoma" }); restartBtn.anchor.set(1, 0); // right-top restartBtn.x = LK.gui.top.width - 40; restartBtn.y = 40; restartBtn.interactive = true; restartBtn.buttonMode = true; restartBtn.visible = false; // Only show after pause restartBtn.down = function (x, y, obj) { if (!gameOver) return; resetGame(); }; LK.gui.top.addChild(restartBtn); // Show restart button after pause, hide otherwise LK.on('pause', function () { restartBtn.visible = true; }); LK.on('resume', function () { restartBtn.visible = false; }); LK.on('gameover', function () { resetGame(); restartBtn.visible = false; }); // Create character and position character = new Character(); character.x = GAME_WIDTH * 0.35; character.y = GAME_HEIGHT / 2; character.gravity = GRAVITY_START; character.flapStrength = FLAP_STRENGTH; // Add character after pipes and collectibles so it renders above them game.addChild(character); // Reset game state function resetGame() { // Remove pipes for (var i = pipes.length - 1; i >= 0; i--) { pipes[i].destroy(); pipes.splice(i, 1); } // Reset lastX for all pipes (defensive, in case any remain) for (var i = 0; i < pipes.length; i++) { pipes[i].lastX = undefined; } // Remove golds for (var i = golds.length - 1; i >= 0; i--) { golds[i].destroy(); golds.splice(i, 1); } // Reset character character.x = GAME_WIDTH * 0.35; character.y = GAME_HEIGHT / 2; character.velocity = 0; character.gravity = GRAVITY_START; character.flapStrength = FLAP_STRENGTH; // Reset variables score = 0; scoreTxt.setText(score); lastPipeTick = 0; pipeFreq = PIPE_FREQ_START; // Use new slower frequency pipeSpeed = PIPE_SPEED_START; // Use new slower speed gameStarted = false; gameOver = false; // Reset powerup/auto-fly state powerupActive = false; powerupPipesLeft = 0; autoFlyTargetY = null; autoFlyPipeIndex = null; autoFlyJustPassedPipe = false; // Reset bird color and stop blinking if (character._powerupBlinkTween) tween.stop(character, { tint: true }); character.tint = 0xffffff; character._powerupBlinking = false; character._powerupBlinkTween = null; character._powerupBlinkStep = 0; character._powerupBlinkTotal = 0; // Hide countdown text if (typeof powerupCountdownTxt !== "undefined") { powerupCountdownTxt.visible = false; powerupCountdownTxt.setText(''); } // Clear any pending auto-fly end timeout if (game._powerupEndTimeout) { LK.clearTimeout(game._powerupEndTimeout); game._powerupEndTimeout = null; } } // Start game on first tap function startGame() { if (!gameStarted && !gameOver) { gameStarted = true; character.flap(); } } // Flap on tap/click game.down = function (x, y, obj) { if (gameOver) return; // Allow input during powerup auto-fly if (!gameStarted) { startGame(); } else { character.flap(); } }; // Main update loop game.update = function () { if (gameOver) return; // Animate clouds (move right to left, loop) var bgMoveSpeed = Math.abs(pipeSpeed) * 0.5; if (powerupActive && autoFlyPipeIndex !== null && pipes[autoFlyPipeIndex]) { // During auto-fly, match background and clouds speed to the pipe's speed bgMoveSpeed = Math.abs(pipes[autoFlyPipeIndex].speed) * 0.5; } for (var i = 0; i < clouds.length; i++) { var cloud = clouds[i]; var cloudMoveSpeed = 0.7 + 0.2 * i; if (powerupActive && autoFlyPipeIndex !== null && pipes[autoFlyPipeIndex]) { cloudMoveSpeed = Math.abs(pipes[autoFlyPipeIndex].speed) * (0.7 + 0.2 * i) / Math.abs(pipeSpeed); } cloud.x -= cloudMoveSpeed; if (cloud.x < -cloud.width / 2) { cloud.x = GAME_WIDTH + cloud.width / 2 + Math.random() * 200; cloud.y = 200 + Math.random() * 400 + i * 100; } } // Animate city background to move leftward and loop (slower for parallax effect) var cityMoveSpeed = bgMoveSpeed * 0.5; bgCity.x -= cityMoveSpeed; bgCity2.x -= cityMoveSpeed; if (bgCity.x <= -GAME_WIDTH) { bgCity.x = bgCity2.x + GAME_WIDTH; } if (bgCity2.x <= -GAME_WIDTH) { bgCity2.x = bgCity.x + GAME_WIDTH; } // Animate background landscape to move leftward and loop bgLandscape.x -= bgMoveSpeed; bgLandscape2.x -= bgMoveSpeed; // Loop both backgrounds for seamless scrolling if (bgLandscape.x <= -GAME_WIDTH) { bgLandscape.x = bgLandscape2.x + GAME_WIDTH; } if (bgLandscape2.x <= -GAME_WIDTH) { bgLandscape2.x = bgLandscape.x + GAME_WIDTH; } if (!gameStarted) { // Idle animation: bob up and down character.y = GAME_HEIGHT / 2 + Math.sin(LK.ticks / 30) * 30; return; } // Character physics if (powerupActive) { // Find the next pipe to pass if needed if (autoFlyPipeIndex === null || autoFlyPipeIndex >= pipes.length) { // Find the next pipe for (var pi = 0; pi < pipes.length; pi++) { if (!pipes[pi].passed && pipes[pi].x + PIPE_WIDTH / 2 > character.x) { autoFlyPipeIndex = pi; break; } } if (autoFlyPipeIndex === null && pipes.length > 0) autoFlyPipeIndex = pipes.length - 1; } // Set target Y to the center of the gap of the next pipe if (autoFlyPipeIndex !== null && pipes[autoFlyPipeIndex]) { var pipePair = pipes[autoFlyPipeIndex]; // Calculate gap center var gapY = pipePair.getTopPipe().height; var gapHeight = pipePair.getBottomPipe().y - pipePair.getTopPipe().height; autoFlyTargetY = gapY + gapHeight / 2; // Do NOT override character.x or character.y; player keeps full control // Calculate the pipe's center X var pipeCenterX = pipePair.x + PIPE_WIDTH / 2; // Store the last pipe center X for transition detection if (pipePair.lastX === undefined) pipePair.lastX = pipeCenterX; // Detect passing through the pipe (only when crossing the pipe center this frame) if (pipePair.lastX >= character.x && pipeCenterX < character.x) { powerupPipesLeft--; autoFlyJustPassedPipe = true; } // When the pipe is behind, reset for next pipe if (autoFlyJustPassedPipe && pipeCenterX < character.x - 40) { autoFlyPipeIndex++; autoFlyJustPassedPipe = false; } pipePair.lastX = pipeCenterX; // Animate bird's color to blink green during auto-fly, increasing blink frequency towards the end if (!character._powerupBlinking) { character._powerupBlinking = true; character._powerupBlinkTween = null; character._powerupBlinkStep = 0; character._powerupBlinkTotal = powerupPipesLeft * 12; // estimate 12 blinks per pipe } if (character._powerupBlinking) { // Calculate blink frequency: faster as powerupPipesLeft decreases var minBlink = 180, maxBlink = 60; // ms var blinkDuration = minBlink - (minBlink - maxBlink) * (3 - powerupPipesLeft) / 3; if (!character._powerupBlinkTween || character._powerupBlinkTween._finished) { // Alternate between green and normal var toGreen = character._powerupBlinkStep % 2 === 0; var targetTint = toGreen ? 0x00ff00 : 0xffffff; if (character._powerupBlinkTween) tween.stop(character, { tint: true }); character._powerupBlinkTween = tween(character, { tint: targetTint }, { duration: blinkDuration, easing: tween.linear }); character._powerupBlinkStep++; } } } // Show and update countdown text during auto-fly if (powerupActive && powerupPipesLeft > 0) { powerupCountdownTxt.visible = true; powerupCountdownTxt.setText("You are immortal for: " + powerupPipesLeft); } else { powerupCountdownTxt.visible = false; } // End powerup after 3 pipes, but delay deactivation by 0.5s if (powerupPipesLeft <= 0 && powerupActive) { if (!game._powerupEndTimeout) { game._powerupEndTimeout = LK.setTimeout(function () { powerupActive = false; autoFlyTargetY = null; autoFlyPipeIndex = null; autoFlyJustPassedPipe = false; // Stop blinking and reset color if (character._powerupBlinkTween) tween.stop(character, { tint: true }); character.tint = 0xffffff; character._powerupBlinking = false; character._powerupBlinkTween = null; character._powerupBlinkStep = 0; character._powerupBlinkTotal = 0; // Stop powerupmusic and fade bgmusic back to 100% LK.stopMusic('powerupmusic'); LK.playMusic('bgmusic', { fade: { start: 0.2, end: 1, duration: 400 } }); game._powerupEndTimeout = null; }, 500); } } // Always call character.update() so player has full control character.update(); // Spawn comet particles if powerupActive if (powerupActive) { // Spawn 2-3 particles per frame for a dense trail for (var cp = 0; cp < 2 + Math.floor(Math.random() * 2); cp++) { var p = new CometParticle(); // Place particle slightly behind and below the bird for a comet tail p.x = character.x - 40 + Math.random() * 10; p.y = character.y + 20 + Math.random() * 16 - 8; p.alpha = 0.6 + Math.random() * 0.25; cometParticles.push(p); game.addChildAt ? game.addChildAt(p, 0) : game.addChild(p); // add behind bird if possible } } // Update and clean up comet particles for (var i = cometParticles.length - 1; i >= 0; i--) { var part = cometParticles[i]; part.update(); if (part.age > part.lifetime || part.alpha < 0.05) { if (part.parent) part.parent.removeChild(part); cometParticles.splice(i, 1); } } } else { character.update(); } // Clamp character to screen if (character.y < character.attachAsset('character', { anchorX: 0.5, anchorY: 0.5 }).height / 2) { character.y = character.attachAsset('character', { anchorX: 0.5, anchorY: 0.5 }).height / 2; character.velocity = 0; } if (character.y > GAME_HEIGHT - character.attachAsset('character', { anchorX: 0.5, anchorY: 0.5 }).height / 2) { character.y = GAME_HEIGHT - character.attachAsset('character', { anchorX: 0.5, anchorY: 0.5 }).height / 2; character.velocity = 0; if (!powerupActive) { triggerGameOver(); return; } // If powerupActive, do not trigger game over, just clamp position } // Pipes movement and collision for (var i = pipes.length - 1; i >= 0; i--) { var pipePair = pipes[i]; pipePair.update(); // Remove pipes off screen if (pipePair.x + PIPE_WIDTH < 0) { pipePair.destroy(); pipes.splice(i, 1); continue; } // Collision detection var charBounds = character.getBounds(); var topPipe = pipePair.getTopPipe(); var bottomPipe = pipePair.getBottomPipe(); // Top pipe collision if (rectsIntersect(charBounds, { x: pipePair.x, y: 0, width: PIPE_WIDTH, height: topPipe.height })) { if (!powerupActive) { triggerGameOver(); return; } // If powerupActive, ignore collision and allow player to keep playing } // Bottom pipe collision if (rectsIntersect(charBounds, { x: pipePair.x, y: bottomPipe.y, width: PIPE_WIDTH, height: bottomPipe.height })) { if (!powerupActive) { triggerGameOver(); return; } // If powerupActive, ignore collision and allow player to keep playing } // Score: when character passes the pipe (center) if (!pipePair.passed && pipePair.x + PIPE_WIDTH / 2 < character.x) { pipePair.passed = true; score += 1; scoreTxt.setText(score); // Increase difficulty if (pipeFreq > PIPE_FREQ_MIN) { pipeFreq -= 2; if (pipeFreq < PIPE_FREQ_MIN) pipeFreq = PIPE_FREQ_MIN; } if (pipeSpeed > PIPE_SPEED_MAX) { pipeSpeed -= 0.5; if (pipeSpeed < PIPE_SPEED_MAX) pipeSpeed = PIPE_SPEED_MAX; } character.gravity += 0.02; } } // Golds movement and collection for (var i = golds.length - 1; i >= 0; i--) { var gold = golds[i]; gold.update(); // Remove gold if off screen if (gold.x + 50 < 0) { gold.destroy(); golds.splice(i, 1); continue; } // Collision with character if (!gold.collected && rectsIntersect(character.getBounds(), gold.getBounds())) { gold.collected = true; gold.destroy(); golds.splice(i, 1); // Only increase score if this is a Gold, not a Powerup if (gold instanceof Gold) { LK.getSound('goldcollect').play(); score += 3; scoreTxt.setText(score); } // Powerup collection: activate or extend auto-fly mode if (gold instanceof Powerup) { LK.getSound('powerupcollect').play(); // Stop powerupmusic if already playing before starting again LK.stopMusic('powerupmusic'); // Fade out bgmusic to 20% and play powerupmusic LK.playMusic('bgmusic', { fade: { start: 1, end: 0.2, duration: 400 } }); LK.playMusic('powerupmusic', { loop: false }); if (!powerupActive) { powerupActive = true; powerupPipesLeft = 3; // Find the next pipe the player will pass autoFlyPipeIndex = null; for (var pi = 0; pi < pipes.length; pi++) { if (!pipes[pi].passed && pipes[pi].x + PIPE_WIDTH / 2 > character.x) { autoFlyPipeIndex = pi; break; } } // If not found, just use the last pipe if (autoFlyPipeIndex === null && pipes.length > 0) autoFlyPipeIndex = pipes.length - 1; autoFlyTargetY = null; autoFlyJustPassedPipe = false; // Set velocity to 0, but do NOT override y or gravity; player keeps control, collisions are ignored character.velocity = 0; // Start blinking effect character._powerupBlinking = false; character._powerupBlinkTween = null; character._powerupBlinkStep = 0; character._powerupBlinkTotal = 0; } else { // If already active, increase duration by 3 more pipes, but cap at 3 powerupPipesLeft += 3; if (powerupPipesLeft > 3) powerupPipesLeft = 3; // Cancel pending end timeout if any, so effect doesn't end early if (game._powerupEndTimeout) { LK.clearTimeout(game._powerupEndTimeout); game._powerupEndTimeout = null; } } } // Optionally: add a flash or effect here } } // --- Ensure character is always rendered above pipes and golds --- if (character.parent) { character.parent.removeChild(character); } game.addChild(character); // Pipe spawning if (LK.ticks - lastPipeTick >= pipeFreq) { spawnPipePair(); lastPipeTick = LK.ticks; } }; // Rectangle intersection helper function rectsIntersect(a, b) { return a.x < b.x + b.width && a.x + a.width > b.x && a.y < b.y + b.height && a.y + a.height > b.y; } // Spawn a new pipe pair function spawnPipePair() { var gapHeight = PIPE_GAP_MIN + Math.floor(Math.random() * (PIPE_GAP_MAX - PIPE_GAP_MIN + 1)); var minGapY = 180; var maxGapY = GAME_HEIGHT - gapHeight - 180; var gapY = minGapY + Math.floor(Math.random() * (maxGapY - minGapY + 1)); var pipePair = new PipePair(); pipePair.x = GAME_WIDTH; pipePair.y = 0; pipePair.setGap(gapY, gapHeight); pipePair.speed = pipeSpeed; pipePair.passed = false; pipes.push(pipePair); game.addChild(pipePair); // 15% chance to spawn Powerup, 85% chance to spawn Gold if (Math.random() < 0.15) { var powerup = new Powerup(); powerup.x = pipePair.x + PIPE_WIDTH / 2 + 60; powerup.y = gapY + gapHeight / 2; powerup.speed = pipeSpeed; golds.push(powerup); game.addChild(powerup); } else { var gold = new Gold(); gold.x = pipePair.x + PIPE_WIDTH / 2 + 60; gold.y = gapY + gapHeight / 2; gold.speed = pipeSpeed; golds.push(gold); game.addChild(gold); } } // Game over logic function triggerGameOver() { if (gameOver) return; gameOver = true; LK.getSound('hitsound').play(); LK.effects.flashScreen(0xff0000, 800); // Do not stop or fade out bgmusic here; let it continue during game over screen // (No call to LK.stopMusic or LK.playMusic for bgmusic here) LK.showGameOver(); } // Reset game on game over LK.on('gameover', function () { resetGame(); }); // Initial game state resetGame(); LK.playMusic('bgmusic');
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Character: the player-controlled ellipse
var Character = Container.expand(function () {
var self = Container.call(this);
var _char = self.attachAsset('character', {
anchorX: 0.5,
anchorY: 0.5
});
// Physics
self.velocity = 0;
self.gravity = 2.2;
self.flapStrength = -19;
// Flap (jump)
self.flap = function () {
self.velocity = self.flapStrength;
};
// Update position and velocity
self.update = function () {
// Always apply gravity and velocity, even during powerupActive
self.velocity += self.gravity;
self.y += self.velocity;
};
// For collision detection
self.getBounds = function () {
return {
x: self.x - _char.width / 2,
y: self.y - _char.height / 2,
width: _char.width,
height: _char.height
};
};
return self;
});
// CometParticle: a single particle for the comet trail effect
var CometParticle = Container.expand(function () {
var self = Container.call(this);
// Use pure white for a star-like effect
var color = 0xffffff;
// Use a bright ellipse as the particle
var _shape = self.attachAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.22 + Math.random() * 0.18,
scaleY: 0.16 + Math.random() * 0.14,
color: color
});
// Particle properties
self.lifetime = 22 + Math.random() * 10; // frames
self.age = 0;
self.vx = -6 + Math.random() * -2; // leftward
self.vy = (Math.random() - 0.5) * 4; // slight up/down
self.alpha = 0.85 + Math.random() * 0.15; // Brighter
self.update = function () {
self.x += self.vx;
self.y += self.vy;
self.age++;
// Fade out, but keep a bit brighter
self.alpha *= 0.93;
if (self.age > self.lifetime) {
if (self.parent) self.parent.removeChild(self);
self.destroy();
}
};
return self;
});
// Gold: collectible coin between pipes
var Gold = Container.expand(function () {
var self = Container.call(this);
var _gold = self.attachAsset('gold', {
anchorX: 0.5,
anchorY: 0.5
});
self.collected = false;
self._bobPhase = Math.random() * Math.PI * 2; // randomize phase so coins don't bob in sync
self.update = function () {
self.x += self.speed;
// Animate gold with a gentle up-and-down bobbing motion
self.y += Math.sin(LK.ticks / 18 + self._bobPhase) * 1.5;
};
// For collision detection
self.getBounds = function () {
return {
x: self.x - _gold.width / 2,
y: self.y - _gold.height / 2,
width: _gold.width,
height: _gold.height
};
};
return self;
});
// PipePair: a pair of pipes (top and bottom) with a gap
var PipePair = Container.expand(function () {
var self = Container.call(this);
// Pipe dimensions
var pipeWidth = 220;
var pipeHeight = 1200;
var gapHeight = 500; // Will be set dynamically
// Top pipe
var topPipe = self.attachAsset('pipe_top', {
anchorX: 0,
anchorY: 1
});
// Bottom pipe
var bottomPipe = self.attachAsset('pipe_bottom', {
anchorX: 0,
anchorY: 0
});
// Set pipes' positions based on gapY and gapHeight
self.setGap = function (gapY, gapHeight) {
// Top pipe: bottom at gapY
topPipe.x = 0;
topPipe.y = gapY;
topPipe.height = gapY;
// Bottom pipe: top at gapY+gapHeight
bottomPipe.x = 0;
bottomPipe.y = gapY + gapHeight;
bottomPipe.height = 2732 - (gapY + gapHeight);
};
// For collision detection
self.getTopPipe = function () {
return topPipe;
};
self.getBottomPipe = function () {
return bottomPipe;
};
// Move pipes leftward
self.update = function () {
self.x += self.speed;
};
// Set initial speed
self.speed = -12;
return self;
});
// Powerup: special collectible (separate from Gold)
var Powerup = Container.expand(function () {
var self = Container.call(this);
var _powerup = self.attachAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5
});
self.collected = false;
self._bobPhase = Math.random() * Math.PI * 2;
self.update = function () {
self.x += self.speed;
// Animate with a gentle up-and-down bobbing motion
self.y += Math.sin(LK.ticks / 18 + self._bobPhase) * 1.5;
};
self.getBounds = function () {
return {
x: self.x - _powerup.width / 2,
y: self.y - _powerup.height / 2,
width: _powerup.width,
height: _powerup.height
};
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x4db8ff // Light blue sky
});
/****
* Game Code
****/
// Pipes: green boxes, character: yellow ellipse, background: blue
// Game constants
// Background landscape and clouds
// unique icon for powerup
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var PIPE_WIDTH = 220;
var PIPE_GAP_MIN = 480;
var PIPE_GAP_MAX = 700;
var PIPE_DISTANCE = 900; // Horizontal distance between pipes
var PIPE_SPEED_START = -7; // Slower initial pipe speed
var PIPE_SPEED_MAX = -28;
var PIPE_FREQ_START = 120; // Pipes appear less frequently at start
var PIPE_FREQ_MIN = 45;
var GRAVITY_START = 1.05; // Slightly lower gravity for even gentler fall
var FLAP_STRENGTH = -22;
// Game variables
var pipes = [];
var golds = [];
var character;
var score = 0;
var scoreTxt;
var lastPipeTick = 0;
var pipeFreq = PIPE_FREQ_START;
var pipeSpeed = PIPE_SPEED_START;
var gameStarted = false;
var gameOver = false;
// Powerup/auto-fly state
var powerupActive = false;
var powerupPipesLeft = 0;
var autoFlyTargetY = null;
var autoFlyPipeIndex = null;
var autoFlyJustPassedPipe = false;
// Comet particle system for powerup effect
var cometParticles = [];
// Add city background image (behind landscape and clouds)
var bgCity = LK.getAsset('city', {
anchorX: 0,
anchorY: 1,
x: 0,
y: GAME_HEIGHT - 600,
width: GAME_WIDTH,
height: 900
});
var bgCity2 = LK.getAsset('city', {
anchorX: 0,
anchorY: 1,
x: GAME_WIDTH,
y: GAME_HEIGHT - 600,
width: GAME_WIDTH,
height: 900
});
game.addChild(bgCity);
game.addChild(bgCity2);
// Add background landscape (two for seamless looping)
var bgLandscape = LK.getAsset('bg_landscape', {
anchorX: 0,
anchorY: 1,
x: 0,
y: GAME_HEIGHT,
width: GAME_WIDTH,
height: 600
});
var bgLandscape2 = LK.getAsset('bg_landscape', {
anchorX: 0,
anchorY: 1,
x: GAME_WIDTH,
y: GAME_HEIGHT,
width: GAME_WIDTH,
height: 600
});
game.addChild(bgLandscape);
game.addChild(bgLandscape2);
// Add clouds (3 clouds at different positions and scales)
var clouds = [];
for (var i = 0; i < 3; i++) {
var cloud = LK.getAsset('cloud', {
anchorX: 0.5,
anchorY: 0.5,
x: 400 + i * 600,
y: 300 + i * 120,
scaleX: 0.8 + 0.3 * Math.random(),
scaleY: 0.8 + 0.3 * Math.random()
});
game.addChild(cloud);
clouds.push(cloud);
}
// Add score text to GUI
scoreTxt = new Text2('0', {
size: 150,
fill: 0xFFFFFF,
stroke: 0x222222,
strokeThickness: 12
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Center score text horizontally, avoid top left 100x100
scoreTxt.x = LK.gui.top.width / 2;
scoreTxt.y = 40;
// Add countdown text for auto-fly powerup
var powerupCountdownTxt = new Text2('', {
size: 100,
fill: 0x00ff00,
font: "Impact, Arial Black, Tahoma"
});
powerupCountdownTxt.anchor.set(0.5, 0);
powerupCountdownTxt.x = LK.gui.top.width / 2;
powerupCountdownTxt.y = 200;
powerupCountdownTxt.visible = false;
LK.gui.top.addChild(powerupCountdownTxt);
// Add restart button to GUI (top right, not overlapping score)
var restartBtn = new Text2('⟳', {
size: 120,
fill: 0xffffff,
font: "Impact, Arial Black, Tahoma"
});
restartBtn.anchor.set(1, 0); // right-top
restartBtn.x = LK.gui.top.width - 40;
restartBtn.y = 40;
restartBtn.interactive = true;
restartBtn.buttonMode = true;
restartBtn.visible = false; // Only show after pause
restartBtn.down = function (x, y, obj) {
if (!gameOver) return;
resetGame();
};
LK.gui.top.addChild(restartBtn);
// Show restart button after pause, hide otherwise
LK.on('pause', function () {
restartBtn.visible = true;
});
LK.on('resume', function () {
restartBtn.visible = false;
});
LK.on('gameover', function () {
resetGame();
restartBtn.visible = false;
});
// Create character and position
character = new Character();
character.x = GAME_WIDTH * 0.35;
character.y = GAME_HEIGHT / 2;
character.gravity = GRAVITY_START;
character.flapStrength = FLAP_STRENGTH;
// Add character after pipes and collectibles so it renders above them
game.addChild(character);
// Reset game state
function resetGame() {
// Remove pipes
for (var i = pipes.length - 1; i >= 0; i--) {
pipes[i].destroy();
pipes.splice(i, 1);
}
// Reset lastX for all pipes (defensive, in case any remain)
for (var i = 0; i < pipes.length; i++) {
pipes[i].lastX = undefined;
}
// Remove golds
for (var i = golds.length - 1; i >= 0; i--) {
golds[i].destroy();
golds.splice(i, 1);
}
// Reset character
character.x = GAME_WIDTH * 0.35;
character.y = GAME_HEIGHT / 2;
character.velocity = 0;
character.gravity = GRAVITY_START;
character.flapStrength = FLAP_STRENGTH;
// Reset variables
score = 0;
scoreTxt.setText(score);
lastPipeTick = 0;
pipeFreq = PIPE_FREQ_START; // Use new slower frequency
pipeSpeed = PIPE_SPEED_START; // Use new slower speed
gameStarted = false;
gameOver = false;
// Reset powerup/auto-fly state
powerupActive = false;
powerupPipesLeft = 0;
autoFlyTargetY = null;
autoFlyPipeIndex = null;
autoFlyJustPassedPipe = false;
// Reset bird color and stop blinking
if (character._powerupBlinkTween) tween.stop(character, {
tint: true
});
character.tint = 0xffffff;
character._powerupBlinking = false;
character._powerupBlinkTween = null;
character._powerupBlinkStep = 0;
character._powerupBlinkTotal = 0;
// Hide countdown text
if (typeof powerupCountdownTxt !== "undefined") {
powerupCountdownTxt.visible = false;
powerupCountdownTxt.setText('');
}
// Clear any pending auto-fly end timeout
if (game._powerupEndTimeout) {
LK.clearTimeout(game._powerupEndTimeout);
game._powerupEndTimeout = null;
}
}
// Start game on first tap
function startGame() {
if (!gameStarted && !gameOver) {
gameStarted = true;
character.flap();
}
}
// Flap on tap/click
game.down = function (x, y, obj) {
if (gameOver) return;
// Allow input during powerup auto-fly
if (!gameStarted) {
startGame();
} else {
character.flap();
}
};
// Main update loop
game.update = function () {
if (gameOver) return;
// Animate clouds (move right to left, loop)
var bgMoveSpeed = Math.abs(pipeSpeed) * 0.5;
if (powerupActive && autoFlyPipeIndex !== null && pipes[autoFlyPipeIndex]) {
// During auto-fly, match background and clouds speed to the pipe's speed
bgMoveSpeed = Math.abs(pipes[autoFlyPipeIndex].speed) * 0.5;
}
for (var i = 0; i < clouds.length; i++) {
var cloud = clouds[i];
var cloudMoveSpeed = 0.7 + 0.2 * i;
if (powerupActive && autoFlyPipeIndex !== null && pipes[autoFlyPipeIndex]) {
cloudMoveSpeed = Math.abs(pipes[autoFlyPipeIndex].speed) * (0.7 + 0.2 * i) / Math.abs(pipeSpeed);
}
cloud.x -= cloudMoveSpeed;
if (cloud.x < -cloud.width / 2) {
cloud.x = GAME_WIDTH + cloud.width / 2 + Math.random() * 200;
cloud.y = 200 + Math.random() * 400 + i * 100;
}
}
// Animate city background to move leftward and loop (slower for parallax effect)
var cityMoveSpeed = bgMoveSpeed * 0.5;
bgCity.x -= cityMoveSpeed;
bgCity2.x -= cityMoveSpeed;
if (bgCity.x <= -GAME_WIDTH) {
bgCity.x = bgCity2.x + GAME_WIDTH;
}
if (bgCity2.x <= -GAME_WIDTH) {
bgCity2.x = bgCity.x + GAME_WIDTH;
}
// Animate background landscape to move leftward and loop
bgLandscape.x -= bgMoveSpeed;
bgLandscape2.x -= bgMoveSpeed;
// Loop both backgrounds for seamless scrolling
if (bgLandscape.x <= -GAME_WIDTH) {
bgLandscape.x = bgLandscape2.x + GAME_WIDTH;
}
if (bgLandscape2.x <= -GAME_WIDTH) {
bgLandscape2.x = bgLandscape.x + GAME_WIDTH;
}
if (!gameStarted) {
// Idle animation: bob up and down
character.y = GAME_HEIGHT / 2 + Math.sin(LK.ticks / 30) * 30;
return;
}
// Character physics
if (powerupActive) {
// Find the next pipe to pass if needed
if (autoFlyPipeIndex === null || autoFlyPipeIndex >= pipes.length) {
// Find the next pipe
for (var pi = 0; pi < pipes.length; pi++) {
if (!pipes[pi].passed && pipes[pi].x + PIPE_WIDTH / 2 > character.x) {
autoFlyPipeIndex = pi;
break;
}
}
if (autoFlyPipeIndex === null && pipes.length > 0) autoFlyPipeIndex = pipes.length - 1;
}
// Set target Y to the center of the gap of the next pipe
if (autoFlyPipeIndex !== null && pipes[autoFlyPipeIndex]) {
var pipePair = pipes[autoFlyPipeIndex];
// Calculate gap center
var gapY = pipePair.getTopPipe().height;
var gapHeight = pipePair.getBottomPipe().y - pipePair.getTopPipe().height;
autoFlyTargetY = gapY + gapHeight / 2;
// Do NOT override character.x or character.y; player keeps full control
// Calculate the pipe's center X
var pipeCenterX = pipePair.x + PIPE_WIDTH / 2;
// Store the last pipe center X for transition detection
if (pipePair.lastX === undefined) pipePair.lastX = pipeCenterX;
// Detect passing through the pipe (only when crossing the pipe center this frame)
if (pipePair.lastX >= character.x && pipeCenterX < character.x) {
powerupPipesLeft--;
autoFlyJustPassedPipe = true;
}
// When the pipe is behind, reset for next pipe
if (autoFlyJustPassedPipe && pipeCenterX < character.x - 40) {
autoFlyPipeIndex++;
autoFlyJustPassedPipe = false;
}
pipePair.lastX = pipeCenterX;
// Animate bird's color to blink green during auto-fly, increasing blink frequency towards the end
if (!character._powerupBlinking) {
character._powerupBlinking = true;
character._powerupBlinkTween = null;
character._powerupBlinkStep = 0;
character._powerupBlinkTotal = powerupPipesLeft * 12; // estimate 12 blinks per pipe
}
if (character._powerupBlinking) {
// Calculate blink frequency: faster as powerupPipesLeft decreases
var minBlink = 180,
maxBlink = 60; // ms
var blinkDuration = minBlink - (minBlink - maxBlink) * (3 - powerupPipesLeft) / 3;
if (!character._powerupBlinkTween || character._powerupBlinkTween._finished) {
// Alternate between green and normal
var toGreen = character._powerupBlinkStep % 2 === 0;
var targetTint = toGreen ? 0x00ff00 : 0xffffff;
if (character._powerupBlinkTween) tween.stop(character, {
tint: true
});
character._powerupBlinkTween = tween(character, {
tint: targetTint
}, {
duration: blinkDuration,
easing: tween.linear
});
character._powerupBlinkStep++;
}
}
}
// Show and update countdown text during auto-fly
if (powerupActive && powerupPipesLeft > 0) {
powerupCountdownTxt.visible = true;
powerupCountdownTxt.setText("You are immortal for: " + powerupPipesLeft);
} else {
powerupCountdownTxt.visible = false;
}
// End powerup after 3 pipes, but delay deactivation by 0.5s
if (powerupPipesLeft <= 0 && powerupActive) {
if (!game._powerupEndTimeout) {
game._powerupEndTimeout = LK.setTimeout(function () {
powerupActive = false;
autoFlyTargetY = null;
autoFlyPipeIndex = null;
autoFlyJustPassedPipe = false;
// Stop blinking and reset color
if (character._powerupBlinkTween) tween.stop(character, {
tint: true
});
character.tint = 0xffffff;
character._powerupBlinking = false;
character._powerupBlinkTween = null;
character._powerupBlinkStep = 0;
character._powerupBlinkTotal = 0;
// Stop powerupmusic and fade bgmusic back to 100%
LK.stopMusic('powerupmusic');
LK.playMusic('bgmusic', {
fade: {
start: 0.2,
end: 1,
duration: 400
}
});
game._powerupEndTimeout = null;
}, 500);
}
}
// Always call character.update() so player has full control
character.update();
// Spawn comet particles if powerupActive
if (powerupActive) {
// Spawn 2-3 particles per frame for a dense trail
for (var cp = 0; cp < 2 + Math.floor(Math.random() * 2); cp++) {
var p = new CometParticle();
// Place particle slightly behind and below the bird for a comet tail
p.x = character.x - 40 + Math.random() * 10;
p.y = character.y + 20 + Math.random() * 16 - 8;
p.alpha = 0.6 + Math.random() * 0.25;
cometParticles.push(p);
game.addChildAt ? game.addChildAt(p, 0) : game.addChild(p); // add behind bird if possible
}
}
// Update and clean up comet particles
for (var i = cometParticles.length - 1; i >= 0; i--) {
var part = cometParticles[i];
part.update();
if (part.age > part.lifetime || part.alpha < 0.05) {
if (part.parent) part.parent.removeChild(part);
cometParticles.splice(i, 1);
}
}
} else {
character.update();
}
// Clamp character to screen
if (character.y < character.attachAsset('character', {
anchorX: 0.5,
anchorY: 0.5
}).height / 2) {
character.y = character.attachAsset('character', {
anchorX: 0.5,
anchorY: 0.5
}).height / 2;
character.velocity = 0;
}
if (character.y > GAME_HEIGHT - character.attachAsset('character', {
anchorX: 0.5,
anchorY: 0.5
}).height / 2) {
character.y = GAME_HEIGHT - character.attachAsset('character', {
anchorX: 0.5,
anchorY: 0.5
}).height / 2;
character.velocity = 0;
if (!powerupActive) {
triggerGameOver();
return;
}
// If powerupActive, do not trigger game over, just clamp position
}
// Pipes movement and collision
for (var i = pipes.length - 1; i >= 0; i--) {
var pipePair = pipes[i];
pipePair.update();
// Remove pipes off screen
if (pipePair.x + PIPE_WIDTH < 0) {
pipePair.destroy();
pipes.splice(i, 1);
continue;
}
// Collision detection
var charBounds = character.getBounds();
var topPipe = pipePair.getTopPipe();
var bottomPipe = pipePair.getBottomPipe();
// Top pipe collision
if (rectsIntersect(charBounds, {
x: pipePair.x,
y: 0,
width: PIPE_WIDTH,
height: topPipe.height
})) {
if (!powerupActive) {
triggerGameOver();
return;
}
// If powerupActive, ignore collision and allow player to keep playing
}
// Bottom pipe collision
if (rectsIntersect(charBounds, {
x: pipePair.x,
y: bottomPipe.y,
width: PIPE_WIDTH,
height: bottomPipe.height
})) {
if (!powerupActive) {
triggerGameOver();
return;
}
// If powerupActive, ignore collision and allow player to keep playing
}
// Score: when character passes the pipe (center)
if (!pipePair.passed && pipePair.x + PIPE_WIDTH / 2 < character.x) {
pipePair.passed = true;
score += 1;
scoreTxt.setText(score);
// Increase difficulty
if (pipeFreq > PIPE_FREQ_MIN) {
pipeFreq -= 2;
if (pipeFreq < PIPE_FREQ_MIN) pipeFreq = PIPE_FREQ_MIN;
}
if (pipeSpeed > PIPE_SPEED_MAX) {
pipeSpeed -= 0.5;
if (pipeSpeed < PIPE_SPEED_MAX) pipeSpeed = PIPE_SPEED_MAX;
}
character.gravity += 0.02;
}
}
// Golds movement and collection
for (var i = golds.length - 1; i >= 0; i--) {
var gold = golds[i];
gold.update();
// Remove gold if off screen
if (gold.x + 50 < 0) {
gold.destroy();
golds.splice(i, 1);
continue;
}
// Collision with character
if (!gold.collected && rectsIntersect(character.getBounds(), gold.getBounds())) {
gold.collected = true;
gold.destroy();
golds.splice(i, 1);
// Only increase score if this is a Gold, not a Powerup
if (gold instanceof Gold) {
LK.getSound('goldcollect').play();
score += 3;
scoreTxt.setText(score);
}
// Powerup collection: activate or extend auto-fly mode
if (gold instanceof Powerup) {
LK.getSound('powerupcollect').play();
// Stop powerupmusic if already playing before starting again
LK.stopMusic('powerupmusic');
// Fade out bgmusic to 20% and play powerupmusic
LK.playMusic('bgmusic', {
fade: {
start: 1,
end: 0.2,
duration: 400
}
});
LK.playMusic('powerupmusic', {
loop: false
});
if (!powerupActive) {
powerupActive = true;
powerupPipesLeft = 3;
// Find the next pipe the player will pass
autoFlyPipeIndex = null;
for (var pi = 0; pi < pipes.length; pi++) {
if (!pipes[pi].passed && pipes[pi].x + PIPE_WIDTH / 2 > character.x) {
autoFlyPipeIndex = pi;
break;
}
}
// If not found, just use the last pipe
if (autoFlyPipeIndex === null && pipes.length > 0) autoFlyPipeIndex = pipes.length - 1;
autoFlyTargetY = null;
autoFlyJustPassedPipe = false;
// Set velocity to 0, but do NOT override y or gravity; player keeps control, collisions are ignored
character.velocity = 0;
// Start blinking effect
character._powerupBlinking = false;
character._powerupBlinkTween = null;
character._powerupBlinkStep = 0;
character._powerupBlinkTotal = 0;
} else {
// If already active, increase duration by 3 more pipes, but cap at 3
powerupPipesLeft += 3;
if (powerupPipesLeft > 3) powerupPipesLeft = 3;
// Cancel pending end timeout if any, so effect doesn't end early
if (game._powerupEndTimeout) {
LK.clearTimeout(game._powerupEndTimeout);
game._powerupEndTimeout = null;
}
}
}
// Optionally: add a flash or effect here
}
}
// --- Ensure character is always rendered above pipes and golds ---
if (character.parent) {
character.parent.removeChild(character);
}
game.addChild(character);
// Pipe spawning
if (LK.ticks - lastPipeTick >= pipeFreq) {
spawnPipePair();
lastPipeTick = LK.ticks;
}
};
// Rectangle intersection helper
function rectsIntersect(a, b) {
return a.x < b.x + b.width && a.x + a.width > b.x && a.y < b.y + b.height && a.y + a.height > b.y;
}
// Spawn a new pipe pair
function spawnPipePair() {
var gapHeight = PIPE_GAP_MIN + Math.floor(Math.random() * (PIPE_GAP_MAX - PIPE_GAP_MIN + 1));
var minGapY = 180;
var maxGapY = GAME_HEIGHT - gapHeight - 180;
var gapY = minGapY + Math.floor(Math.random() * (maxGapY - minGapY + 1));
var pipePair = new PipePair();
pipePair.x = GAME_WIDTH;
pipePair.y = 0;
pipePair.setGap(gapY, gapHeight);
pipePair.speed = pipeSpeed;
pipePair.passed = false;
pipes.push(pipePair);
game.addChild(pipePair);
// 15% chance to spawn Powerup, 85% chance to spawn Gold
if (Math.random() < 0.15) {
var powerup = new Powerup();
powerup.x = pipePair.x + PIPE_WIDTH / 2 + 60;
powerup.y = gapY + gapHeight / 2;
powerup.speed = pipeSpeed;
golds.push(powerup);
game.addChild(powerup);
} else {
var gold = new Gold();
gold.x = pipePair.x + PIPE_WIDTH / 2 + 60;
gold.y = gapY + gapHeight / 2;
gold.speed = pipeSpeed;
golds.push(gold);
game.addChild(gold);
}
}
// Game over logic
function triggerGameOver() {
if (gameOver) return;
gameOver = true;
LK.getSound('hitsound').play();
LK.effects.flashScreen(0xff0000, 800);
// Do not stop or fade out bgmusic here; let it continue during game over screen
// (No call to LK.stopMusic or LK.playMusic for bgmusic here)
LK.showGameOver();
}
// Reset game on game over
LK.on('gameover', function () {
resetGame();
});
// Initial game state
resetGame();
LK.playMusic('bgmusic');
give me some alternatives of real flappy bird. In-Game asset. 2d. High contrast. No shadows. Just one image
flappy bird cloud, pixel art. In-Game asset. 2d. High contrast. No shadows
flappy bird ground. In-Game asset. 2d. High contrast. No shadows. pixel art. suitable for looping
flappy bird gold icon. pixel art style. In-Game asset. 2d. High contrast. No shadows
flappy bird power up icon. pixel art style. In-Game asset. 2d. High contrast. No shadows
Restyled