/****
* 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