/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Bird class var Bird = Container.expand(function () { var self = Container.call(this); var birdSprite = self.attachAsset('bird', { anchorX: 0.5, anchorY: 0.5 }); self.vy = 0; // vertical velocity // Bird physics update self.update = function () { self.vy += gravity; self.y += self.vy; // Clamp rotation for visual feedback var maxAngle = Math.PI / 4; var minAngle = -Math.PI / 2.5; var t = Math.max(-1, Math.min(1, self.vy / 30)); birdSprite.rotation = t * maxAngle; if (birdSprite.rotation < minAngle) birdSprite.rotation = minAngle; if (birdSprite.rotation > maxAngle) birdSprite.rotation = maxAngle; }; // Flap (jump) self.flap = function () { self.vy = flapStrength; LK.getSound('flap').play(); }; return self; }); // PipePair class (top and bottom pipes) var PipePair = Container.expand(function () { var self = Container.call(this); // Top pipe self.topPipe = self.attachAsset('pipe', { anchorX: 0, anchorY: 1 }); // Bottom pipe self.bottomPipe = self.attachAsset('pipe', { anchorX: 0, anchorY: 0 }); // Used for scoring self.passed = false; // Set gap and position self.setGap = function (gapY, gapHeight) { // Top pipe: bottom at gapY - gapHeight/2 self.topPipe.height = Math.max(100, gapY - gapHeight / 2); self.topPipe.y = self.topPipe.height; // Bottom pipe: top at gapY + gapHeight/2 self.bottomPipe.y = gapY + gapHeight / 2; self.bottomPipe.height = Math.max(100, 2732 - self.bottomPipe.y - groundHeight); }; // Move pipes left self.update = function () { self.x -= pipeSpeed; }; // Get bounding rectangles for collision self.getTopRect = function () { return new Rectangle(self.x, 0, self.topPipe.width, self.topPipe.height); }; self.getBottomRect = function () { return new Rectangle(self.x, self.bottomPipe.y, self.bottomPipe.width, self.bottomPipe.height); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87ceeb // Sky blue }); /**** * Game Code ****/ // Constants // Bird: yellow ellipse // Pipe: green box // Ground: brown box // Sound: Flap // Sound: Score // Sound: Hit var gravity = 1.0; var flapStrength = -20; var pipeSpeed = 9; var pipeInterval = 140; // frames between pipes (slower spawn) var pipeGap = 520; // vertical gap between pipes var groundHeight = 120; var birdStartX = 600; var birdStartY = 1200; // Game state var bird; var pipes = []; var score = 0; var scoreTxt; var ground; var gameStarted = false; var gameOver = false; var lastPipeFrame = 0; // GUI Score scoreTxt = new Text2('0', { size: 180, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Add ground ground = LK.getAsset('ground', { anchorX: 0, anchorY: 0, x: 0, y: 2732 - groundHeight }); game.addChild(ground); // Add bird bird = new Bird(); bird.x = birdStartX; bird.y = birdStartY; game.addChild(bird); // Reset function function resetGame() { // Remove pipes for (var i = 0; i < pipes.length; i++) { pipes[i].destroy(); } pipes = []; // Reset bird bird.x = birdStartX; bird.y = birdStartY; bird.vy = 0; // Reset score score = 0; scoreTxt.setText(score); // Reset state gameStarted = false; gameOver = false; lastPipeFrame = LK.ticks; } // Start game on first tap function startGame() { if (!gameStarted && !gameOver) { gameStarted = true; bird.flap(); } } // Flap on tap game.down = function (x, y, obj) { if (gameOver) return; if (!gameStarted) { startGame(); return; } bird.flap(); }; // Main update loop game.update = function () { if (gameOver) return; // Bird physics if (gameStarted) { bird.update(); } // Pipes if (gameStarted) { // Spawn pipes if (LK.ticks - lastPipeFrame >= pipeInterval) { var gapY = 400 + Math.floor(Math.random() * (2732 - groundHeight - 800)); var pair = new PipePair(); pair.x = 2048; pair.setGap(gapY, pipeGap); pipes.push(pair); game.addChild(pair); lastPipeFrame = LK.ticks; } } // Update pipes and check for collisions for (var i = pipes.length - 1; i >= 0; i--) { var pair = pipes[i]; if (gameStarted) { pair.update(); } // Remove pipes off screen if (pair.x + pair.topPipe.width < 0) { pair.destroy(); pipes.splice(i, 1); continue; } // Score: passed pipe if (!pair.passed && pair.x + pair.topPipe.width < bird.x) { pair.passed = true; score += 1; scoreTxt.setText(score); LK.getSound('score').play(); } // Collision detection if (rectIntersectsCircle(pair.getTopRect(), bird.x, bird.y, birdWidth() / 2) || rectIntersectsCircle(pair.getBottomRect(), bird.x, bird.y, birdWidth() / 2)) { triggerGameOver(); return; } } // Ground collision if (bird.y + birdHeight() / 2 >= 2732 - groundHeight) { bird.y = 2732 - groundHeight - birdHeight() / 2; triggerGameOver(); return; } // Ceiling clamp if (bird.y - birdHeight() / 2 < 0) { bird.y = birdHeight() / 2; bird.vy = 0; } }; // Helper: get bird width/height function birdWidth() { return bird.children[0].width; } function birdHeight() { return bird.children[0].height; } // Rectangle-circle collision function rectIntersectsCircle(rect, cx, cy, cr) { // Find closest point to circle center within the rectangle var closestX = Math.max(rect.x, Math.min(cx, rect.x + rect.width)); var closestY = Math.max(rect.y, Math.min(cy, rect.y + rect.height)); // Calculate distance var dx = cx - closestX; var dy = cy - closestY; return dx * dx + dy * dy < cr * cr; } // Game over function triggerGameOver() { if (gameOver) return; gameOver = true; LK.getSound('hit').play(); LK.effects.flashScreen(0xff0000, 600); LK.showGameOver(); } // Reset on game over LK.on('gameover', function () { resetGame(); }); // Initial reset resetGame();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Bird class
var Bird = Container.expand(function () {
var self = Container.call(this);
var birdSprite = self.attachAsset('bird', {
anchorX: 0.5,
anchorY: 0.5
});
self.vy = 0; // vertical velocity
// Bird physics update
self.update = function () {
self.vy += gravity;
self.y += self.vy;
// Clamp rotation for visual feedback
var maxAngle = Math.PI / 4;
var minAngle = -Math.PI / 2.5;
var t = Math.max(-1, Math.min(1, self.vy / 30));
birdSprite.rotation = t * maxAngle;
if (birdSprite.rotation < minAngle) birdSprite.rotation = minAngle;
if (birdSprite.rotation > maxAngle) birdSprite.rotation = maxAngle;
};
// Flap (jump)
self.flap = function () {
self.vy = flapStrength;
LK.getSound('flap').play();
};
return self;
});
// PipePair class (top and bottom pipes)
var PipePair = Container.expand(function () {
var self = Container.call(this);
// Top pipe
self.topPipe = self.attachAsset('pipe', {
anchorX: 0,
anchorY: 1
});
// Bottom pipe
self.bottomPipe = self.attachAsset('pipe', {
anchorX: 0,
anchorY: 0
});
// Used for scoring
self.passed = false;
// Set gap and position
self.setGap = function (gapY, gapHeight) {
// Top pipe: bottom at gapY - gapHeight/2
self.topPipe.height = Math.max(100, gapY - gapHeight / 2);
self.topPipe.y = self.topPipe.height;
// Bottom pipe: top at gapY + gapHeight/2
self.bottomPipe.y = gapY + gapHeight / 2;
self.bottomPipe.height = Math.max(100, 2732 - self.bottomPipe.y - groundHeight);
};
// Move pipes left
self.update = function () {
self.x -= pipeSpeed;
};
// Get bounding rectangles for collision
self.getTopRect = function () {
return new Rectangle(self.x, 0, self.topPipe.width, self.topPipe.height);
};
self.getBottomRect = function () {
return new Rectangle(self.x, self.bottomPipe.y, self.bottomPipe.width, self.bottomPipe.height);
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87ceeb // Sky blue
});
/****
* Game Code
****/
// Constants
// Bird: yellow ellipse
// Pipe: green box
// Ground: brown box
// Sound: Flap
// Sound: Score
// Sound: Hit
var gravity = 1.0;
var flapStrength = -20;
var pipeSpeed = 9;
var pipeInterval = 140; // frames between pipes (slower spawn)
var pipeGap = 520; // vertical gap between pipes
var groundHeight = 120;
var birdStartX = 600;
var birdStartY = 1200;
// Game state
var bird;
var pipes = [];
var score = 0;
var scoreTxt;
var ground;
var gameStarted = false;
var gameOver = false;
var lastPipeFrame = 0;
// GUI Score
scoreTxt = new Text2('0', {
size: 180,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Add ground
ground = LK.getAsset('ground', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 2732 - groundHeight
});
game.addChild(ground);
// Add bird
bird = new Bird();
bird.x = birdStartX;
bird.y = birdStartY;
game.addChild(bird);
// Reset function
function resetGame() {
// Remove pipes
for (var i = 0; i < pipes.length; i++) {
pipes[i].destroy();
}
pipes = [];
// Reset bird
bird.x = birdStartX;
bird.y = birdStartY;
bird.vy = 0;
// Reset score
score = 0;
scoreTxt.setText(score);
// Reset state
gameStarted = false;
gameOver = false;
lastPipeFrame = LK.ticks;
}
// Start game on first tap
function startGame() {
if (!gameStarted && !gameOver) {
gameStarted = true;
bird.flap();
}
}
// Flap on tap
game.down = function (x, y, obj) {
if (gameOver) return;
if (!gameStarted) {
startGame();
return;
}
bird.flap();
};
// Main update loop
game.update = function () {
if (gameOver) return;
// Bird physics
if (gameStarted) {
bird.update();
}
// Pipes
if (gameStarted) {
// Spawn pipes
if (LK.ticks - lastPipeFrame >= pipeInterval) {
var gapY = 400 + Math.floor(Math.random() * (2732 - groundHeight - 800));
var pair = new PipePair();
pair.x = 2048;
pair.setGap(gapY, pipeGap);
pipes.push(pair);
game.addChild(pair);
lastPipeFrame = LK.ticks;
}
}
// Update pipes and check for collisions
for (var i = pipes.length - 1; i >= 0; i--) {
var pair = pipes[i];
if (gameStarted) {
pair.update();
}
// Remove pipes off screen
if (pair.x + pair.topPipe.width < 0) {
pair.destroy();
pipes.splice(i, 1);
continue;
}
// Score: passed pipe
if (!pair.passed && pair.x + pair.topPipe.width < bird.x) {
pair.passed = true;
score += 1;
scoreTxt.setText(score);
LK.getSound('score').play();
}
// Collision detection
if (rectIntersectsCircle(pair.getTopRect(), bird.x, bird.y, birdWidth() / 2) || rectIntersectsCircle(pair.getBottomRect(), bird.x, bird.y, birdWidth() / 2)) {
triggerGameOver();
return;
}
}
// Ground collision
if (bird.y + birdHeight() / 2 >= 2732 - groundHeight) {
bird.y = 2732 - groundHeight - birdHeight() / 2;
triggerGameOver();
return;
}
// Ceiling clamp
if (bird.y - birdHeight() / 2 < 0) {
bird.y = birdHeight() / 2;
bird.vy = 0;
}
};
// Helper: get bird width/height
function birdWidth() {
return bird.children[0].width;
}
function birdHeight() {
return bird.children[0].height;
}
// Rectangle-circle collision
function rectIntersectsCircle(rect, cx, cy, cr) {
// Find closest point to circle center within the rectangle
var closestX = Math.max(rect.x, Math.min(cx, rect.x + rect.width));
var closestY = Math.max(rect.y, Math.min(cy, rect.y + rect.height));
// Calculate distance
var dx = cx - closestX;
var dy = cy - closestY;
return dx * dx + dy * dy < cr * cr;
}
// Game over
function triggerGameOver() {
if (gameOver) return;
gameOver = true;
LK.getSound('hit').play();
LK.effects.flashScreen(0xff0000, 600);
LK.showGameOver();
}
// Reset on game over
LK.on('gameover', function () {
resetGame();
});
// Initial reset
resetGame();
Fullscreen modern App Store landscape banner, 16:9, high definition, for a game titled "Flappy Bird: Tap to Fly" and with the description "Tap to keep the bird airborne and navigate through gaps in pipes. Avoid obstacles and see how far you can go!". No text on banner!
wall . No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
coin . No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat