User prompt
increase pipe distance up down
Code edit (1 edits merged)
Please save this source code
User prompt
decrease gravity and flap str
Code edit (1 edits merged)
Please save this source code
User prompt
increase pipe heights until screen end (up and down pipes)
Code edit (1 edits merged)
Please save this source code
User prompt
please lower the gravity beacuse its too much
Code edit (1 edits merged)
Please save this source code
User prompt
Flappy Flight
Initial prompt
make me a game like flappy bird
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Bird class var Bird = Container.expand(function () { var self = Container.call(this); // Attach bird asset (ellipse, yellow) var birdAsset = self.attachAsset('bird', { anchorX: 0.5, anchorY: 0.5 }); // Physics properties self.velocityY = 0; self.gravity = 0.5; // Lower gravity per frame for slower fall self.flapStrength = -20; // Lower flap strength for gentler jump // Bird size for collision self.radius = birdAsset.width * 0.45; // Flap method self.flap = function () { self.velocityY = self.flapStrength; }; // Update method (called every tick) self.update = function () { // Only apply gravity and velocity if gameStarted is true if (typeof gameStarted !== "undefined" && gameStarted) { self.velocityY += self.gravity; self.y += self.velocityY; } // Clamp rotation for visual feedback (optional) var maxAngle = Math.PI / 4; var minAngle = -Math.PI / 6; var angle = self.velocityY / 40 * maxAngle; if (angle > maxAngle) { angle = maxAngle; } if (angle < minAngle) { angle = minAngle; } birdAsset.rotation = angle; }; return self; }); // PipePair class (top and bottom pipes as a pair) var PipePair = Container.expand(function () { var self = Container.call(this); // Pipe properties self.pipeWidth = 80; // Start very thin, will be set wider on spawn self.gapHeight = 520; // Will be randomized a bit self.speed = 12; // Speed at which pipes move left // Top pipe (cloud) self.topPipe = self.attachAsset('cloud', { anchorX: 0, anchorY: 1, width: self.pipeWidth, height: 300, // cloud height y: 0, x: 0 }); // Bottom pipe (mountain) self.bottomPipe = self.attachAsset('mountain', { anchorX: 0, anchorY: 0, width: self.pipeWidth, height: 600, // mountain height y: 0, x: 0 }); // Used to track if score was already given for this pipe self.passed = false; // Set pipes' vertical positions self.setGap = function (gapY, gapHeight) { self.gapHeight = gapHeight; // Top cloud: position at gapY, stretch to fill from top to gapY self.topPipe.y = gapY; self.topPipe.height = Math.max(1, gapY); // cloud height = gapY, min 1 self.topPipe.width = self.pipeWidth; // Bottom mountain: position at gapY+gapHeight, stretch to fill to bottom self.bottomPipe.y = gapY + gapHeight; self.bottomPipe.height = Math.max(1, 2732 - (gapY + gapHeight)); // mountain height self.bottomPipe.width = self.pipeWidth; }; // Update method self.update = function () { self.x -= self.speed; }; // Collision check with bird self.collidesWith = function (bird) { // Bird's bounding circle var bx = bird.x; var by = bird.y; var r = bird.radius; // Top pipe rectangle var topRect = { x: self.x, y: 0, w: self.pipeWidth, h: self.topPipe.y }; // Bottom pipe rectangle var bottomRect = { x: self.x, y: self.bottomPipe.y, w: self.pipeWidth, h: 2732 - self.bottomPipe.y }; // Helper: circle-rect collision function circleRectCollide(cx, cy, cr, rx, ry, rw, rh) { var closestX = Math.max(rx, Math.min(cx, rx + rw)); var closestY = Math.max(ry, Math.min(cy, ry + rh)); var dx = cx - closestX; var dy = cy - closestY; return dx * dx + dy * dy < cr * cr; } if (circleRectCollide(bx, by, r, topRect.x, topRect.y, topRect.w, topRect.h)) { return true; } if (circleRectCollide(bx, by, r, bottomRect.x, bottomRect.y, bottomRect.w, bottomRect.h)) { return true; } return false; }; // Has bird passed this pipe? (for scoring) self.isPassedBy = function (bird) { return !self.passed && self.x + self.pipeWidth < bird.x - bird.radius; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // --- No dynamic ground/upground height/width/distance here; handled in update below -- var background = LK.getAsset('background', { anchorX: 0, anchorY: 0, x: 0, y: 0, width: 2048, height: 2732 }); game.addChild(background); // --- Cloud (top pipe) - white, wide, ellipse // Mountain (bottom pipe) - green, wide, triangle-like // --- Game Variables --- // Tween plugin for animations (not strictly needed for MVP, but included for future use) // --- Asset Initialization (shapes) --- var bird; var pipes = []; var pipeSpawnTimer = 0; var pipeInterval = 90; // Frames between pipes (1.5s at 60fps) var score = 0; var scoreTxt; var ground; var gameStarted = false; var gameOver = false; // --- Tap to Start Overlay --- var tapToStartTxt = new Text2('Tap to Start', { size: 140, fill: 0xffffff }); tapToStartTxt.anchor.set(0.5, 1); // anchor bottom center tapToStartTxt.x = 2048 / 2; // Move down so the layer sits on the ground, with a gap above ground var tapToStartGroundGap = 60; // distance above ground var tapToStartMaxY = 2732 - 920 - tapToStartGroundGap; // 120 is base ground height tapToStartTxt.y = tapToStartMaxY; tapToStartTxt.visible = false; // Add "How to play" instructions below the Tap to Start text var howToPlayTxt = new Text2('Tap to power up and guide your UFO through doors.\nAvoid galactic doors and see how far you can go!', { size: 70, fill: 0xffffff }); howToPlayTxt.anchor.set(0.5, 0); // anchor top center howToPlayTxt.x = 2048 / 2; howToPlayTxt.y = tapToStartTxt.y + 40; // 40px below tapToStartTxt // Remove from game if already added, then add to top if (tapToStartTxt.parent) { tapToStartTxt.parent.removeChild(tapToStartTxt); } game.addChild(tapToStartTxt); game.setChildIndex(tapToStartTxt, game.children.length - 1); // Always on top if (howToPlayTxt.parent) { howToPlayTxt.parent.removeChild(howToPlayTxt); } game.addChild(howToPlayTxt); game.setChildIndex(howToPlayTxt, game.children.length - 1); // --- GUI Score Display --- if (typeof scoreTxt !== "undefined" && scoreTxt && scoreTxt.parent) { scoreTxt.parent.removeChild(scoreTxt); scoreTxt.destroy(); } if (typeof scoreCircleBg !== "undefined" && scoreCircleBg && scoreCircleBg.parent) { scoreCircleBg.parent.removeChild(scoreCircleBg); scoreCircleBg.destroy(); } // Create a perfect circle background for the score and center it scoreCircleBg = LK.getAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, x: LK.gui.width / 2, y: 80 + 45, // 45 is half of 90 (score text size), so circle is centered behind text scaleX: 0.8, scaleY: 0.8 }); LK.gui.addChild(scoreCircleBg); scoreTxt = new Text2('0', { size: 90, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0.5); // Center score horizontally at the top, vertically centered in the circle scoreTxt.x = LK.gui.width / 2; scoreTxt.y = 80 + 45; LK.gui.addChild(scoreTxt); // Add game name text under the score in big font, but on overlay layer (with tapToStartTxt) var gameNameTxt = new Text2('Galactic Doors', { size: 220, fill: 0xffffff, font: "'Orbitron', 'Audiowide', 'Space Mono', 'GillSans-Bold', Impact, 'Arial Black', Tahoma" }); gameNameTxt.anchor.set(0.5, 0); // Place it under the score circle, with a gap, but on overlay gameNameTxt.x = 2048 / 2; gameNameTxt.y = tapToStartTxt.y - 1000; // 1000px above tapToStartTxt (visually grouped) gameNameTxt.visible = false; // Remove from game if already added, then add to top if (gameNameTxt.parent) { gameNameTxt.parent.removeChild(gameNameTxt); } game.addChild(gameNameTxt); game.setChildIndex(gameNameTxt, game.children.length - 1); // Move howToPlayTxt to overlay and hide initially howToPlayTxt.x = 2048 / 2; howToPlayTxt.y = tapToStartTxt.y + 40; howToPlayTxt.visible = false; if (howToPlayTxt.parent) { howToPlayTxt.parent.removeChild(howToPlayTxt); } game.addChild(howToPlayTxt); game.setChildIndex(howToPlayTxt, game.children.length - 1); // --- Ground (for collision) --- ground = LK.getAsset('ground', { anchorX: 0, anchorY: 0, x: 0, y: 2732 - 120 }); game.addChild(ground); // --- Mirrored ground at top (now called upground) --- var upground = LK.getAsset('ground', { anchorX: 0, anchorY: 1, x: 0, y: 0, scaleY: -1 }); game.addChild(upground); // --- Minimum ground width (never narrower than mountain/cloud asset) --- var minGroundWidth = 800; // same as mountain/cloud asset width var maxGroundWidth = 2048; // full width // --- Bird --- bird = new Bird(); game.addChild(bird); // Start position: horizontally 30% from left, vertically centered bird.x = 2048 * 0.3; bird.y = 2732 / 2; // --- Reset function --- function resetGame() { // Remove pipes for (var i = 0; i < pipes.length; i++) { pipes[i].destroy(); } pipes = []; // Ensure background is always at the back if (background && background.parent) { background.parent.setChildIndex(background, 0); } // Reset bird bird.x = 2048 * 0.3; bird.y = 2732 / 2; bird.velocityY = 0; // Reset score score = 0; scoreTxt.setText(score); // Reset timers pipeSpawnTimer = 0; gameStarted = false; gameOver = false; if (tapToStartTxt) { tapToStartTxt.visible = true; // Remove and re-add to ensure it's on top if (tapToStartTxt.parent) { tapToStartTxt.parent.removeChild(tapToStartTxt); } game.addChild(tapToStartTxt); game.setChildIndex(tapToStartTxt, game.children.length - 1); } if (gameNameTxt) { gameNameTxt.visible = true; if (gameNameTxt.parent) { gameNameTxt.parent.removeChild(gameNameTxt); } game.addChild(gameNameTxt); game.setChildIndex(gameNameTxt, game.children.length - 1); } if (howToPlayTxt) { howToPlayTxt.visible = true; if (howToPlayTxt.parent) { howToPlayTxt.parent.removeChild(howToPlayTxt); } game.addChild(howToPlayTxt); game.setChildIndex(howToPlayTxt, game.children.length - 1); } } // --- Start game on first tap --- game.down = function (x, y, obj) { if (gameOver) { return; } if (!gameStarted) { gameStarted = true; if (tapToStartTxt) { tapToStartTxt.visible = false; } if (gameNameTxt) { gameNameTxt.visible = false; } if (howToPlayTxt) { howToPlayTxt.visible = false; } bird.flap(); return; } bird.flap(); }; // --- Main update loop --- game.update = function () { if (gameOver) { return; } // Bird physics if (gameStarted) { bird.update(); } // Clamp bird to top of screen if (bird.y - bird.radius < 0) { bird.y = bird.radius; bird.velocityY = 0; } // Pipe spawning if (gameStarted) { pipeSpawnTimer++; if (pipeSpawnTimer >= pipeInterval) { pipeSpawnTimer = 0; // Mountain-like curve for gapY using a sine wave var minGapY = 350; var maxGapY = 2732 - 120 - 350 - 700; var t = (score + pipes.length) * 0.5; // t increases as more pipes spawn var amplitude = (maxGapY - minGapY) / 2; var centerY = minGapY + amplitude; var gapY = centerY + Math.sin(t) * amplitude; gapY = Math.max(minGapY, Math.min(maxGapY, gapY)); var gapHeight = 650 + Math.floor(Math.random() * 120); // 650-770 px (increased gap) // Calculate pipe width based on score: start thin, widen much more slowly var minPipeWidth = 80; var maxPipeWidth = 880; // Use a slower growth curve (e.g. score/120 instead of score/40) var pipeWidth = minPipeWidth + Math.floor((maxPipeWidth - minPipeWidth) * Math.min(1, score / 120)); // Clamp to max if (pipeWidth > maxPipeWidth) { pipeWidth = maxPipeWidth; } if (pipeWidth < minPipeWidth) { pipeWidth = minPipeWidth; } var pipePair = new PipePair(); pipePair.pipeWidth = pipeWidth; pipePair.topPipe.width = pipeWidth; pipePair.bottomPipe.width = pipeWidth; pipePair.x = 2048; pipePair.setGap(gapY, gapHeight); pipes.push(pipePair); game.addChild(pipePair); } } // Update pipes, check for collisions and scoring for (var i = pipes.length - 1; i >= 0; i--) { var pipe = pipes[i]; if (gameStarted) { pipe.update(); } // Remove pipes that are off screen if (pipe.x + pipe.pipeWidth < 0) { pipe.destroy(); pipes.splice(i, 1); continue; } // Collision if (gameStarted && pipe.collidesWith(bird)) { endGame(); return; } // Scoring if (gameStarted && pipe.isPassedBy(bird)) { score += 1; scoreTxt.setText(score); LK.getSound('score').play(); pipe.passed = true; } } // --- Move upground to upper edge and ground to lower edge, fixed positions --- if (ground && upground) { // Increase ground height as score increases, but clamp to a max (e.g. 600) var baseGroundHeight = 120; var maxGroundHeight = 600; // For every 5 points, increase by 12px (tune as needed) var groundHeight = baseGroundHeight + Math.floor(score / 5) * 12; if (groundHeight > maxGroundHeight) { groundHeight = maxGroundHeight; } // Always full width, always at x=0 ground.x = 0; upground.x = 0; ground.width = 2048; upground.width = 2048; ground.height = groundHeight; upground.height = groundHeight; // upground at very top edge upground.y = 0; // ground at very bottom edge ground.y = 2732 - groundHeight; } // Ground collision if (gameStarted && bird.y + bird.radius >= ground.y) { bird.y = ground.y - bird.radius; endGame(); return; } // Upground (top ground) collision if (gameStarted && bird.y - bird.radius <= upground.y + upground.height) { bird.y = upground.y + upground.height + bird.radius; endGame(); return; } }; // --- End game --- function endGame() { if (gameOver) { return; } gameOver = true; bird.velocityY = 0; // Stop bird movement after death // Add a black circle under the blackhole layer on death, fade in like a blackhole var blackCircle = LK.getAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, scaleX: 2.5, scaleY: 2.5, tint: 0x000000 }); blackCircle.alpha = 0; var birdIndex = game.children.indexOf(bird); if (birdIndex > -1) { game.addChildAt(blackCircle, birdIndex); } else { game.addChild(blackCircle); } // Fade in the black circle slowly (like a blackhole) tween(blackCircle, { alpha: 1 }, { duration: 900, easing: tween.cubicInOut }); // Show blackhole image at center of screen, under the bird layer var blackhole = LK.getAsset('blackhole', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 }); blackhole.alpha = 0; // Start invisible var blackholeIndex = game.children.indexOf(bird); if (blackholeIndex > -1) { game.addChildAt(blackhole, blackholeIndex); } else { game.addChild(blackhole); } // Removed red flash effect on death // Fade in blackhole slowly, then spiral-pull bird to center and fix position tween(blackhole, { alpha: 1 }, { duration: 900, easing: tween.cubicInOut, onFinish: function onFinish() { // Spiral parameters var spiralSteps = 60; // 60 frames for spiral var spiralDuration = 1200; // ms // Capture bird's position at spiral start to avoid vibration var spiralStartX = bird.x; var spiralStartY = bird.y; var centerX = 2048 / 2; var centerY = 2732 / 2; var startAngle = Math.atan2(spiralStartY - centerY, spiralStartX - centerX); var radius = Math.sqrt((spiralStartX - centerX) * (spiralStartX - centerX) + (spiralStartY - centerY) * (spiralStartY - centerY)); var spiralTick = 0; // Stop any previous tweens on bird tween.stop(bird); // Spiral animation using manual tweening (only update position, do not tween other sprite properties) function spiralStep() { // Use t in [0,1] var t = spiralTick / spiralSteps; // Easing for spiral progress var easeT = tween.cubicInOut(t); // Spiral angle increases as t increases, start from startAngle var spiralAngle = startAngle + 4 * Math.PI * easeT; // 2 full turns // Radius shrinks to 0 var r = radius * (1 - easeT); // Calculate new position along spiral var x = centerX + r * Math.cos(spiralAngle); var y = centerY + r * Math.sin(spiralAngle); // Only update position, do not tween scale/rotation/etc bird.x = x; bird.y = y; spiralTick++; if (spiralTick <= spiralSteps) { LK.setTimeout(spiralStep, spiralDuration / spiralSteps); } else { // Snap to exact center bird.x = centerX; bird.y = centerY; LK.showGameOver(); } } spiralStep(); } }); } // --- Reset on game over (handled by LK) --- LK.on('gameover', function () { resetGame(); }); // --- Initial reset --- resetGame(); // --- Pause Button --- // Place pause button at right, same vertical as scoreTxt var pauseBtn = new Text2('II', { size: 120, fill: 0xffffff }); pauseBtn.anchor.set(1, 0); // Place at top right, just below top edge (keep 80px from top for visibility) pauseBtn.x = 2048 - 60; // 60px from right edge pauseBtn.y = 80; LK.gui.addChild(pauseBtn); // Pause event (optional: you may want to hook this up to LK.pauseGame if available) // pauseBtn.down = function(x, y, obj) { // LK.pauseGame(); // };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Bird class
var Bird = Container.expand(function () {
var self = Container.call(this);
// Attach bird asset (ellipse, yellow)
var birdAsset = self.attachAsset('bird', {
anchorX: 0.5,
anchorY: 0.5
});
// Physics properties
self.velocityY = 0;
self.gravity = 0.5; // Lower gravity per frame for slower fall
self.flapStrength = -20; // Lower flap strength for gentler jump
// Bird size for collision
self.radius = birdAsset.width * 0.45;
// Flap method
self.flap = function () {
self.velocityY = self.flapStrength;
};
// Update method (called every tick)
self.update = function () {
// Only apply gravity and velocity if gameStarted is true
if (typeof gameStarted !== "undefined" && gameStarted) {
self.velocityY += self.gravity;
self.y += self.velocityY;
}
// Clamp rotation for visual feedback (optional)
var maxAngle = Math.PI / 4;
var minAngle = -Math.PI / 6;
var angle = self.velocityY / 40 * maxAngle;
if (angle > maxAngle) {
angle = maxAngle;
}
if (angle < minAngle) {
angle = minAngle;
}
birdAsset.rotation = angle;
};
return self;
});
// PipePair class (top and bottom pipes as a pair)
var PipePair = Container.expand(function () {
var self = Container.call(this);
// Pipe properties
self.pipeWidth = 80; // Start very thin, will be set wider on spawn
self.gapHeight = 520; // Will be randomized a bit
self.speed = 12; // Speed at which pipes move left
// Top pipe (cloud)
self.topPipe = self.attachAsset('cloud', {
anchorX: 0,
anchorY: 1,
width: self.pipeWidth,
height: 300,
// cloud height
y: 0,
x: 0
});
// Bottom pipe (mountain)
self.bottomPipe = self.attachAsset('mountain', {
anchorX: 0,
anchorY: 0,
width: self.pipeWidth,
height: 600,
// mountain height
y: 0,
x: 0
});
// Used to track if score was already given for this pipe
self.passed = false;
// Set pipes' vertical positions
self.setGap = function (gapY, gapHeight) {
self.gapHeight = gapHeight;
// Top cloud: position at gapY, stretch to fill from top to gapY
self.topPipe.y = gapY;
self.topPipe.height = Math.max(1, gapY); // cloud height = gapY, min 1
self.topPipe.width = self.pipeWidth;
// Bottom mountain: position at gapY+gapHeight, stretch to fill to bottom
self.bottomPipe.y = gapY + gapHeight;
self.bottomPipe.height = Math.max(1, 2732 - (gapY + gapHeight)); // mountain height
self.bottomPipe.width = self.pipeWidth;
};
// Update method
self.update = function () {
self.x -= self.speed;
};
// Collision check with bird
self.collidesWith = function (bird) {
// Bird's bounding circle
var bx = bird.x;
var by = bird.y;
var r = bird.radius;
// Top pipe rectangle
var topRect = {
x: self.x,
y: 0,
w: self.pipeWidth,
h: self.topPipe.y
};
// Bottom pipe rectangle
var bottomRect = {
x: self.x,
y: self.bottomPipe.y,
w: self.pipeWidth,
h: 2732 - self.bottomPipe.y
};
// Helper: circle-rect collision
function circleRectCollide(cx, cy, cr, rx, ry, rw, rh) {
var closestX = Math.max(rx, Math.min(cx, rx + rw));
var closestY = Math.max(ry, Math.min(cy, ry + rh));
var dx = cx - closestX;
var dy = cy - closestY;
return dx * dx + dy * dy < cr * cr;
}
if (circleRectCollide(bx, by, r, topRect.x, topRect.y, topRect.w, topRect.h)) {
return true;
}
if (circleRectCollide(bx, by, r, bottomRect.x, bottomRect.y, bottomRect.w, bottomRect.h)) {
return true;
}
return false;
};
// Has bird passed this pipe? (for scoring)
self.isPassedBy = function (bird) {
return !self.passed && self.x + self.pipeWidth < bird.x - bird.radius;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// --- No dynamic ground/upground height/width/distance here; handled in update below --
var background = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
width: 2048,
height: 2732
});
game.addChild(background);
// --- Cloud (top pipe) - white, wide, ellipse
// Mountain (bottom pipe) - green, wide, triangle-like
// --- Game Variables ---
// Tween plugin for animations (not strictly needed for MVP, but included for future use)
// --- Asset Initialization (shapes) ---
var bird;
var pipes = [];
var pipeSpawnTimer = 0;
var pipeInterval = 90; // Frames between pipes (1.5s at 60fps)
var score = 0;
var scoreTxt;
var ground;
var gameStarted = false;
var gameOver = false;
// --- Tap to Start Overlay ---
var tapToStartTxt = new Text2('Tap to Start', {
size: 140,
fill: 0xffffff
});
tapToStartTxt.anchor.set(0.5, 1); // anchor bottom center
tapToStartTxt.x = 2048 / 2;
// Move down so the layer sits on the ground, with a gap above ground
var tapToStartGroundGap = 60; // distance above ground
var tapToStartMaxY = 2732 - 920 - tapToStartGroundGap; // 120 is base ground height
tapToStartTxt.y = tapToStartMaxY;
tapToStartTxt.visible = false;
// Add "How to play" instructions below the Tap to Start text
var howToPlayTxt = new Text2('Tap to power up and guide your UFO through doors.\nAvoid galactic doors and see how far you can go!', {
size: 70,
fill: 0xffffff
});
howToPlayTxt.anchor.set(0.5, 0); // anchor top center
howToPlayTxt.x = 2048 / 2;
howToPlayTxt.y = tapToStartTxt.y + 40; // 40px below tapToStartTxt
// Remove from game if already added, then add to top
if (tapToStartTxt.parent) {
tapToStartTxt.parent.removeChild(tapToStartTxt);
}
game.addChild(tapToStartTxt);
game.setChildIndex(tapToStartTxt, game.children.length - 1); // Always on top
if (howToPlayTxt.parent) {
howToPlayTxt.parent.removeChild(howToPlayTxt);
}
game.addChild(howToPlayTxt);
game.setChildIndex(howToPlayTxt, game.children.length - 1);
// --- GUI Score Display ---
if (typeof scoreTxt !== "undefined" && scoreTxt && scoreTxt.parent) {
scoreTxt.parent.removeChild(scoreTxt);
scoreTxt.destroy();
}
if (typeof scoreCircleBg !== "undefined" && scoreCircleBg && scoreCircleBg.parent) {
scoreCircleBg.parent.removeChild(scoreCircleBg);
scoreCircleBg.destroy();
}
// Create a perfect circle background for the score and center it
scoreCircleBg = LK.getAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
x: LK.gui.width / 2,
y: 80 + 45,
// 45 is half of 90 (score text size), so circle is centered behind text
scaleX: 0.8,
scaleY: 0.8
});
LK.gui.addChild(scoreCircleBg);
scoreTxt = new Text2('0', {
size: 90,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0.5);
// Center score horizontally at the top, vertically centered in the circle
scoreTxt.x = LK.gui.width / 2;
scoreTxt.y = 80 + 45;
LK.gui.addChild(scoreTxt);
// Add game name text under the score in big font, but on overlay layer (with tapToStartTxt)
var gameNameTxt = new Text2('Galactic Doors', {
size: 220,
fill: 0xffffff,
font: "'Orbitron', 'Audiowide', 'Space Mono', 'GillSans-Bold', Impact, 'Arial Black', Tahoma"
});
gameNameTxt.anchor.set(0.5, 0);
// Place it under the score circle, with a gap, but on overlay
gameNameTxt.x = 2048 / 2;
gameNameTxt.y = tapToStartTxt.y - 1000; // 1000px above tapToStartTxt (visually grouped)
gameNameTxt.visible = false;
// Remove from game if already added, then add to top
if (gameNameTxt.parent) {
gameNameTxt.parent.removeChild(gameNameTxt);
}
game.addChild(gameNameTxt);
game.setChildIndex(gameNameTxt, game.children.length - 1);
// Move howToPlayTxt to overlay and hide initially
howToPlayTxt.x = 2048 / 2;
howToPlayTxt.y = tapToStartTxt.y + 40;
howToPlayTxt.visible = false;
if (howToPlayTxt.parent) {
howToPlayTxt.parent.removeChild(howToPlayTxt);
}
game.addChild(howToPlayTxt);
game.setChildIndex(howToPlayTxt, game.children.length - 1);
// --- Ground (for collision) ---
ground = LK.getAsset('ground', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 2732 - 120
});
game.addChild(ground);
// --- Mirrored ground at top (now called upground) ---
var upground = LK.getAsset('ground', {
anchorX: 0,
anchorY: 1,
x: 0,
y: 0,
scaleY: -1
});
game.addChild(upground);
// --- Minimum ground width (never narrower than mountain/cloud asset) ---
var minGroundWidth = 800; // same as mountain/cloud asset width
var maxGroundWidth = 2048; // full width
// --- Bird ---
bird = new Bird();
game.addChild(bird);
// Start position: horizontally 30% from left, vertically centered
bird.x = 2048 * 0.3;
bird.y = 2732 / 2;
// --- Reset function ---
function resetGame() {
// Remove pipes
for (var i = 0; i < pipes.length; i++) {
pipes[i].destroy();
}
pipes = [];
// Ensure background is always at the back
if (background && background.parent) {
background.parent.setChildIndex(background, 0);
}
// Reset bird
bird.x = 2048 * 0.3;
bird.y = 2732 / 2;
bird.velocityY = 0;
// Reset score
score = 0;
scoreTxt.setText(score);
// Reset timers
pipeSpawnTimer = 0;
gameStarted = false;
gameOver = false;
if (tapToStartTxt) {
tapToStartTxt.visible = true;
// Remove and re-add to ensure it's on top
if (tapToStartTxt.parent) {
tapToStartTxt.parent.removeChild(tapToStartTxt);
}
game.addChild(tapToStartTxt);
game.setChildIndex(tapToStartTxt, game.children.length - 1);
}
if (gameNameTxt) {
gameNameTxt.visible = true;
if (gameNameTxt.parent) {
gameNameTxt.parent.removeChild(gameNameTxt);
}
game.addChild(gameNameTxt);
game.setChildIndex(gameNameTxt, game.children.length - 1);
}
if (howToPlayTxt) {
howToPlayTxt.visible = true;
if (howToPlayTxt.parent) {
howToPlayTxt.parent.removeChild(howToPlayTxt);
}
game.addChild(howToPlayTxt);
game.setChildIndex(howToPlayTxt, game.children.length - 1);
}
}
// --- Start game on first tap ---
game.down = function (x, y, obj) {
if (gameOver) {
return;
}
if (!gameStarted) {
gameStarted = true;
if (tapToStartTxt) {
tapToStartTxt.visible = false;
}
if (gameNameTxt) {
gameNameTxt.visible = false;
}
if (howToPlayTxt) {
howToPlayTxt.visible = false;
}
bird.flap();
return;
}
bird.flap();
};
// --- Main update loop ---
game.update = function () {
if (gameOver) {
return;
}
// Bird physics
if (gameStarted) {
bird.update();
}
// Clamp bird to top of screen
if (bird.y - bird.radius < 0) {
bird.y = bird.radius;
bird.velocityY = 0;
}
// Pipe spawning
if (gameStarted) {
pipeSpawnTimer++;
if (pipeSpawnTimer >= pipeInterval) {
pipeSpawnTimer = 0;
// Mountain-like curve for gapY using a sine wave
var minGapY = 350;
var maxGapY = 2732 - 120 - 350 - 700;
var t = (score + pipes.length) * 0.5; // t increases as more pipes spawn
var amplitude = (maxGapY - minGapY) / 2;
var centerY = minGapY + amplitude;
var gapY = centerY + Math.sin(t) * amplitude;
gapY = Math.max(minGapY, Math.min(maxGapY, gapY));
var gapHeight = 650 + Math.floor(Math.random() * 120); // 650-770 px (increased gap)
// Calculate pipe width based on score: start thin, widen much more slowly
var minPipeWidth = 80;
var maxPipeWidth = 880;
// Use a slower growth curve (e.g. score/120 instead of score/40)
var pipeWidth = minPipeWidth + Math.floor((maxPipeWidth - minPipeWidth) * Math.min(1, score / 120));
// Clamp to max
if (pipeWidth > maxPipeWidth) {
pipeWidth = maxPipeWidth;
}
if (pipeWidth < minPipeWidth) {
pipeWidth = minPipeWidth;
}
var pipePair = new PipePair();
pipePair.pipeWidth = pipeWidth;
pipePair.topPipe.width = pipeWidth;
pipePair.bottomPipe.width = pipeWidth;
pipePair.x = 2048;
pipePair.setGap(gapY, gapHeight);
pipes.push(pipePair);
game.addChild(pipePair);
}
}
// Update pipes, check for collisions and scoring
for (var i = pipes.length - 1; i >= 0; i--) {
var pipe = pipes[i];
if (gameStarted) {
pipe.update();
}
// Remove pipes that are off screen
if (pipe.x + pipe.pipeWidth < 0) {
pipe.destroy();
pipes.splice(i, 1);
continue;
}
// Collision
if (gameStarted && pipe.collidesWith(bird)) {
endGame();
return;
}
// Scoring
if (gameStarted && pipe.isPassedBy(bird)) {
score += 1;
scoreTxt.setText(score);
LK.getSound('score').play();
pipe.passed = true;
}
}
// --- Move upground to upper edge and ground to lower edge, fixed positions ---
if (ground && upground) {
// Increase ground height as score increases, but clamp to a max (e.g. 600)
var baseGroundHeight = 120;
var maxGroundHeight = 600;
// For every 5 points, increase by 12px (tune as needed)
var groundHeight = baseGroundHeight + Math.floor(score / 5) * 12;
if (groundHeight > maxGroundHeight) {
groundHeight = maxGroundHeight;
}
// Always full width, always at x=0
ground.x = 0;
upground.x = 0;
ground.width = 2048;
upground.width = 2048;
ground.height = groundHeight;
upground.height = groundHeight;
// upground at very top edge
upground.y = 0;
// ground at very bottom edge
ground.y = 2732 - groundHeight;
}
// Ground collision
if (gameStarted && bird.y + bird.radius >= ground.y) {
bird.y = ground.y - bird.radius;
endGame();
return;
}
// Upground (top ground) collision
if (gameStarted && bird.y - bird.radius <= upground.y + upground.height) {
bird.y = upground.y + upground.height + bird.radius;
endGame();
return;
}
};
// --- End game ---
function endGame() {
if (gameOver) {
return;
}
gameOver = true;
bird.velocityY = 0; // Stop bird movement after death
// Add a black circle under the blackhole layer on death, fade in like a blackhole
var blackCircle = LK.getAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
scaleX: 2.5,
scaleY: 2.5,
tint: 0x000000
});
blackCircle.alpha = 0;
var birdIndex = game.children.indexOf(bird);
if (birdIndex > -1) {
game.addChildAt(blackCircle, birdIndex);
} else {
game.addChild(blackCircle);
}
// Fade in the black circle slowly (like a blackhole)
tween(blackCircle, {
alpha: 1
}, {
duration: 900,
easing: tween.cubicInOut
});
// Show blackhole image at center of screen, under the bird layer
var blackhole = LK.getAsset('blackhole', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2
});
blackhole.alpha = 0; // Start invisible
var blackholeIndex = game.children.indexOf(bird);
if (blackholeIndex > -1) {
game.addChildAt(blackhole, blackholeIndex);
} else {
game.addChild(blackhole);
}
// Removed red flash effect on death
// Fade in blackhole slowly, then spiral-pull bird to center and fix position
tween(blackhole, {
alpha: 1
}, {
duration: 900,
easing: tween.cubicInOut,
onFinish: function onFinish() {
// Spiral parameters
var spiralSteps = 60; // 60 frames for spiral
var spiralDuration = 1200; // ms
// Capture bird's position at spiral start to avoid vibration
var spiralStartX = bird.x;
var spiralStartY = bird.y;
var centerX = 2048 / 2;
var centerY = 2732 / 2;
var startAngle = Math.atan2(spiralStartY - centerY, spiralStartX - centerX);
var radius = Math.sqrt((spiralStartX - centerX) * (spiralStartX - centerX) + (spiralStartY - centerY) * (spiralStartY - centerY));
var spiralTick = 0;
// Stop any previous tweens on bird
tween.stop(bird);
// Spiral animation using manual tweening (only update position, do not tween other sprite properties)
function spiralStep() {
// Use t in [0,1]
var t = spiralTick / spiralSteps;
// Easing for spiral progress
var easeT = tween.cubicInOut(t);
// Spiral angle increases as t increases, start from startAngle
var spiralAngle = startAngle + 4 * Math.PI * easeT; // 2 full turns
// Radius shrinks to 0
var r = radius * (1 - easeT);
// Calculate new position along spiral
var x = centerX + r * Math.cos(spiralAngle);
var y = centerY + r * Math.sin(spiralAngle);
// Only update position, do not tween scale/rotation/etc
bird.x = x;
bird.y = y;
spiralTick++;
if (spiralTick <= spiralSteps) {
LK.setTimeout(spiralStep, spiralDuration / spiralSteps);
} else {
// Snap to exact center
bird.x = centerX;
bird.y = centerY;
LK.showGameOver();
}
}
spiralStep();
}
});
}
// --- Reset on game over (handled by LK) ---
LK.on('gameover', function () {
resetGame();
});
// --- Initial reset ---
resetGame();
// --- Pause Button ---
// Place pause button at right, same vertical as scoreTxt
var pauseBtn = new Text2('II', {
size: 120,
fill: 0xffffff
});
pauseBtn.anchor.set(1, 0);
// Place at top right, just below top edge (keep 80px from top for visibility)
pauseBtn.x = 2048 - 60; // 60px from right edge
pauseBtn.y = 80;
LK.gui.addChild(pauseBtn);
// Pause event (optional: you may want to hook this up to LK.pauseGame if available)
// pauseBtn.down = function(x, y, obj) {
// LK.pauseGame();
// };
galaxy theme background. In-Game asset. 2d. High contrast. No shadows
only black hole simple no background. In-Game asset. 2d. High contrast. No shadows
galactic portals looks like a black hole. In-Game asset. 2d. High contrast. No shadows
galactic portals looks like a long straight pillar . In-Game asset. 2d. High contrast. No shadows
create an ufo without legs only frizby shape. In-Game asset. 2d. High contrast. No shadows