/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Bird = Container.expand(function () { var self = Container.call(this); var birdGraphics = self.attachAsset('bird', { anchorX: 0.5, anchorY: 0.5 }); self.velocity = 0; self.gravity = 0.4; // Reduced from 0.6 for gentler falling self.flapStrength = -14; // Reduced from -16 for more controlled flight self.maxVelocity = 12; // Reduced from 15 for better control self.flap = function () { self.velocity = self.flapStrength; LK.getSound('flap').play(); // Animation for flap tween(birdGraphics, { rotation: -0.5 }, { duration: 150 }); tween(birdGraphics, { rotation: 0 }, { duration: 300 }); }; self.update = function () { // Apply gravity self.velocity += self.gravity; // Limit velocity if (self.velocity > self.maxVelocity) { self.velocity = self.maxVelocity; } // Update position self.y += self.velocity; // Rotate bird based on velocity var targetRotation = Math.min(Math.max(self.velocity * 0.1, -0.5), 1.5); birdGraphics.rotation = targetRotation; }; return self; }); var Cloud = Container.expand(function () { var self = Container.call(this); var cloudGraphics = self.attachAsset('cloud', { anchorX: 0.5, anchorY: 0.5 }); cloudGraphics.alpha = 0.6; self.speed = -1 - Math.random() * 2; // Random speed between -1 and -3 self.floatOffset = Math.random() * Math.PI * 2; // Random starting float phase self.floatSpeed = 0.02 + Math.random() * 0.02; // Random float speed self.initialY = self.y; self.update = function () { self.x += self.speed; // Floating motion using sine wave self.y = self.initialY + Math.sin(LK.ticks * self.floatSpeed + self.floatOffset) * 20; }; return self; }); var Pipe = Container.expand(function () { var self = Container.call(this); self.speed = -4; self.gapSize = 600; // Increased from 400 to 600 for easier passage self.scored = false; // Create top pipe self.topPipe = self.attachAsset('pipe', { anchorX: 0.5, anchorY: 1 }); // Create bottom pipe self.bottomPipe = self.attachAsset('pipe', { anchorX: 0.5, anchorY: 0 }); self.setupPipes = function (gapCenterY) { self.topPipe.y = gapCenterY - self.gapSize / 2; self.bottomPipe.y = gapCenterY + self.gapSize / 2; }; self.update = function () { self.x += self.speed; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87CEEB }); /**** * Game Code ****/ // Game variables var bird; var pipes = []; var clouds = []; var cloudSpawnTimer = 0; var cloudSpawnDelay = 300; // Spawn cloud every 300 frames (5 seconds at 60fps) var ground; var gameStarted = false; var gameOver = false; var pipeSpawnTimer = 0; var pipeSpawnDelay = 120; // Reduced from 200 frames for closer obstacles var initialPipeSpawnDelay = 100; // Increased from 60 frames for better start // UI elements var scoreTxt = new Text2('0', { size: 80, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Best score display var bestScore = storage.bestScore || 0; var bestScoreTxt = new Text2('BEST: ' + bestScore, { size: 50, fill: 0xFFFFFF }); bestScoreTxt.anchor.set(1, 0); LK.gui.topRight.addChild(bestScoreTxt); var instructionTxt = new Text2('TAP TO FLAP', { size: 60, fill: 0xFFFFFF }); instructionTxt.anchor.set(0.5, 0.5); instructionTxt.x = 2048 / 2; instructionTxt.y = 2732 / 2 + 200; game.addChild(instructionTxt); // Create bird bird = game.addChild(new Bird()); bird.x = 400; bird.y = 2732 / 2; // Create ground - duplicated side by side for continuous ground - moved to GUI layer and fixed var groundTiles = []; for (var i = 0; i < 8; i++) { var groundTile = LK.getAsset('ground', { anchorX: 0, anchorY: 1 }); groundTile.x = i * 291; // Position tiles side by side based on ground width groundTile.y = 2732; groundTiles.push(groundTile); LK.gui.addChild(groundTile); // Add to GUI layer instead of game world } ground = groundTiles[0]; // Keep reference to first tile for backward compatibility // Spawn initial clouds for (var c = 0; c < 3; c++) { var initialCloud = new Cloud(); initialCloud.x = Math.random() * 2048; initialCloud.y = 200 + Math.random() * 800; initialCloud.initialY = initialCloud.y; var scale = 0.8 + Math.random() * 0.6; initialCloud.scaleX = scale; initialCloud.scaleY = scale; clouds.push(initialCloud); game.addChild(initialCloud); game.setChildIndex(initialCloud, 0); } function startGame() { gameStarted = true; instructionTxt.visible = false; bird.flap(); } function spawnPipe() { var pipe = new Pipe(); var minY = 400; var maxY = 2732 - 400; var gapCenterY = minY + Math.random() * (maxY - minY); pipe.x = 2048 + 60; pipe.setupPipes(gapCenterY); pipes.push(pipe); game.addChild(pipe); } function spawnCloud() { var cloud = new Cloud(); cloud.x = 2048 + 100; cloud.y = 200 + Math.random() * 800; // Random Y position in upper part of screen cloud.initialY = cloud.y; // Random scale for variety var scale = 0.8 + Math.random() * 0.6; // Scale between 0.8 and 1.4 cloud.scaleX = scale; cloud.scaleY = scale; clouds.push(cloud); game.addChild(cloud); // Send cloud to back so it appears behind other elements game.setChildIndex(cloud, 0); } function checkCollisions() { // Check ground collision - check against all ground tiles for (var g = 0; g < groundTiles.length; g++) { if (bird.y + 20 >= groundTiles[g].y) { // Reduced from 30 to 20 for more forgiving ground collision // Immediate death with ground tween effect tween(groundTiles[g], { scaleX: 1.3, scaleY: 1.3 }, { duration: 300 }); return true; } // Check if bird intersects with ground tile for immediate death if (bird.intersects(groundTiles[g])) { // Resize ground with tween effect tween(groundTiles[g], { scaleX: 1.3, scaleY: 1.3 }, { duration: 300 }); return true; } } // Check ceiling collision if (bird.y - 30 <= 0) { return true; } // Check pipe collisions for (var i = 0; i < pipes.length; i++) { var pipe = pipes[i]; // Simple collision detection with pipes - reduced hitbox for easier gameplay var birdLeft = bird.x - 20; // Reduced from 30 var birdRight = bird.x + 20; // Reduced from 30 var birdTop = bird.y - 15; // Reduced from 22 var birdBottom = bird.y + 15; // Reduced from 22 var pipeLeft = pipe.x - 60; var pipeRight = pipe.x + 60; if (birdRight > pipeLeft && birdLeft < pipeRight) { // Bird is within pipe's horizontal bounds var topPipeBottom = pipe.topPipe.y; var bottomPipeTop = pipe.bottomPipe.y; if (birdTop < topPipeBottom || birdBottom > bottomPipeTop) { return true; } } // Check if bird touches pipe - immediate death with resize effect if (bird.intersects(pipe.topPipe) || bird.intersects(pipe.bottomPipe)) { // Calculate current pipe scale and apply death effect scaling var currentTopScaleX = pipe.topPipe.scaleX || 1; var currentTopScaleY = pipe.topPipe.scaleY || 1; var currentBottomScaleX = pipe.bottomPipe.scaleX || 1; var currentBottomScaleY = pipe.bottomPipe.scaleY || 1; // Resize pipe with tween effect based on current size tween(pipe.topPipe, { scaleX: currentTopScaleX * 1.2, scaleY: currentTopScaleY * 1.2 }, { duration: 200 }); tween(pipe.bottomPipe, { scaleX: currentBottomScaleX * 1.2, scaleY: currentBottomScaleY * 1.2 }, { duration: 200 }); return true; } } return false; } function updateScore() { for (var i = 0; i < pipes.length; i++) { var pipe = pipes[i]; if (!pipe.scored && bird.x > pipe.x + 60) { pipe.scored = true; LK.setScore(LK.getScore() + 1); scoreTxt.setText(LK.getScore()); // Update best score if current score is higher if (LK.getScore() > bestScore) { bestScore = LK.getScore(); storage.bestScore = bestScore; bestScoreTxt.setText('BEST: ' + bestScore); } LK.getSound('score').play(); } } } function cleanupPipes() { for (var i = pipes.length - 1; i >= 0; i--) { var pipe = pipes[i]; if (pipe.x < -120) { pipe.destroy(); pipes.splice(i, 1); } } } // Input handling game.down = function (x, y, obj) { if (!gameStarted) { startGame(); } else if (!gameOver) { bird.flap(); } }; // Main game loop game.update = function () { if (!gameStarted || gameOver) { return; } // Update bird bird.update(); // Spawn pipes pipeSpawnTimer++; var currentDelay = pipes.length === 0 ? initialPipeSpawnDelay : pipeSpawnDelay; if (pipeSpawnTimer >= currentDelay) { spawnPipe(); pipeSpawnTimer = 0; } // Spawn clouds cloudSpawnTimer++; if (cloudSpawnTimer >= cloudSpawnDelay) { spawnCloud(); cloudSpawnTimer = 0; } // Update clouds for (var c = 0; c < clouds.length; c++) { clouds[c].update(); } // Cleanup off-screen clouds for (var c = clouds.length - 1; c >= 0; c--) { var cloud = clouds[c]; if (cloud.x < -300) { cloud.destroy(); clouds.splice(c, 1); } } // Update pipes for (var i = 0; i < pipes.length; i++) { pipes[i].update(); } // Ground is now fixed in GUI layer, no scrolling needed // Check collisions if (checkCollisions()) { gameOver = true; LK.getSound('hit').play(); LK.showGameOver(); return; } // Update score updateScore(); // Cleanup off-screen pipes cleanupPipes(); };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Bird = Container.expand(function () {
var self = Container.call(this);
var birdGraphics = self.attachAsset('bird', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocity = 0;
self.gravity = 0.4; // Reduced from 0.6 for gentler falling
self.flapStrength = -14; // Reduced from -16 for more controlled flight
self.maxVelocity = 12; // Reduced from 15 for better control
self.flap = function () {
self.velocity = self.flapStrength;
LK.getSound('flap').play();
// Animation for flap
tween(birdGraphics, {
rotation: -0.5
}, {
duration: 150
});
tween(birdGraphics, {
rotation: 0
}, {
duration: 300
});
};
self.update = function () {
// Apply gravity
self.velocity += self.gravity;
// Limit velocity
if (self.velocity > self.maxVelocity) {
self.velocity = self.maxVelocity;
}
// Update position
self.y += self.velocity;
// Rotate bird based on velocity
var targetRotation = Math.min(Math.max(self.velocity * 0.1, -0.5), 1.5);
birdGraphics.rotation = targetRotation;
};
return self;
});
var Cloud = Container.expand(function () {
var self = Container.call(this);
var cloudGraphics = self.attachAsset('cloud', {
anchorX: 0.5,
anchorY: 0.5
});
cloudGraphics.alpha = 0.6;
self.speed = -1 - Math.random() * 2; // Random speed between -1 and -3
self.floatOffset = Math.random() * Math.PI * 2; // Random starting float phase
self.floatSpeed = 0.02 + Math.random() * 0.02; // Random float speed
self.initialY = self.y;
self.update = function () {
self.x += self.speed;
// Floating motion using sine wave
self.y = self.initialY + Math.sin(LK.ticks * self.floatSpeed + self.floatOffset) * 20;
};
return self;
});
var Pipe = Container.expand(function () {
var self = Container.call(this);
self.speed = -4;
self.gapSize = 600; // Increased from 400 to 600 for easier passage
self.scored = false;
// Create top pipe
self.topPipe = self.attachAsset('pipe', {
anchorX: 0.5,
anchorY: 1
});
// Create bottom pipe
self.bottomPipe = self.attachAsset('pipe', {
anchorX: 0.5,
anchorY: 0
});
self.setupPipes = function (gapCenterY) {
self.topPipe.y = gapCenterY - self.gapSize / 2;
self.bottomPipe.y = gapCenterY + self.gapSize / 2;
};
self.update = function () {
self.x += self.speed;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
// Game variables
var bird;
var pipes = [];
var clouds = [];
var cloudSpawnTimer = 0;
var cloudSpawnDelay = 300; // Spawn cloud every 300 frames (5 seconds at 60fps)
var ground;
var gameStarted = false;
var gameOver = false;
var pipeSpawnTimer = 0;
var pipeSpawnDelay = 120; // Reduced from 200 frames for closer obstacles
var initialPipeSpawnDelay = 100; // Increased from 60 frames for better start
// UI elements
var scoreTxt = new Text2('0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Best score display
var bestScore = storage.bestScore || 0;
var bestScoreTxt = new Text2('BEST: ' + bestScore, {
size: 50,
fill: 0xFFFFFF
});
bestScoreTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(bestScoreTxt);
var instructionTxt = new Text2('TAP TO FLAP', {
size: 60,
fill: 0xFFFFFF
});
instructionTxt.anchor.set(0.5, 0.5);
instructionTxt.x = 2048 / 2;
instructionTxt.y = 2732 / 2 + 200;
game.addChild(instructionTxt);
// Create bird
bird = game.addChild(new Bird());
bird.x = 400;
bird.y = 2732 / 2;
// Create ground - duplicated side by side for continuous ground - moved to GUI layer and fixed
var groundTiles = [];
for (var i = 0; i < 8; i++) {
var groundTile = LK.getAsset('ground', {
anchorX: 0,
anchorY: 1
});
groundTile.x = i * 291; // Position tiles side by side based on ground width
groundTile.y = 2732;
groundTiles.push(groundTile);
LK.gui.addChild(groundTile); // Add to GUI layer instead of game world
}
ground = groundTiles[0]; // Keep reference to first tile for backward compatibility
// Spawn initial clouds
for (var c = 0; c < 3; c++) {
var initialCloud = new Cloud();
initialCloud.x = Math.random() * 2048;
initialCloud.y = 200 + Math.random() * 800;
initialCloud.initialY = initialCloud.y;
var scale = 0.8 + Math.random() * 0.6;
initialCloud.scaleX = scale;
initialCloud.scaleY = scale;
clouds.push(initialCloud);
game.addChild(initialCloud);
game.setChildIndex(initialCloud, 0);
}
function startGame() {
gameStarted = true;
instructionTxt.visible = false;
bird.flap();
}
function spawnPipe() {
var pipe = new Pipe();
var minY = 400;
var maxY = 2732 - 400;
var gapCenterY = minY + Math.random() * (maxY - minY);
pipe.x = 2048 + 60;
pipe.setupPipes(gapCenterY);
pipes.push(pipe);
game.addChild(pipe);
}
function spawnCloud() {
var cloud = new Cloud();
cloud.x = 2048 + 100;
cloud.y = 200 + Math.random() * 800; // Random Y position in upper part of screen
cloud.initialY = cloud.y;
// Random scale for variety
var scale = 0.8 + Math.random() * 0.6; // Scale between 0.8 and 1.4
cloud.scaleX = scale;
cloud.scaleY = scale;
clouds.push(cloud);
game.addChild(cloud);
// Send cloud to back so it appears behind other elements
game.setChildIndex(cloud, 0);
}
function checkCollisions() {
// Check ground collision - check against all ground tiles
for (var g = 0; g < groundTiles.length; g++) {
if (bird.y + 20 >= groundTiles[g].y) {
// Reduced from 30 to 20 for more forgiving ground collision
// Immediate death with ground tween effect
tween(groundTiles[g], {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 300
});
return true;
}
// Check if bird intersects with ground tile for immediate death
if (bird.intersects(groundTiles[g])) {
// Resize ground with tween effect
tween(groundTiles[g], {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 300
});
return true;
}
}
// Check ceiling collision
if (bird.y - 30 <= 0) {
return true;
}
// Check pipe collisions
for (var i = 0; i < pipes.length; i++) {
var pipe = pipes[i];
// Simple collision detection with pipes - reduced hitbox for easier gameplay
var birdLeft = bird.x - 20; // Reduced from 30
var birdRight = bird.x + 20; // Reduced from 30
var birdTop = bird.y - 15; // Reduced from 22
var birdBottom = bird.y + 15; // Reduced from 22
var pipeLeft = pipe.x - 60;
var pipeRight = pipe.x + 60;
if (birdRight > pipeLeft && birdLeft < pipeRight) {
// Bird is within pipe's horizontal bounds
var topPipeBottom = pipe.topPipe.y;
var bottomPipeTop = pipe.bottomPipe.y;
if (birdTop < topPipeBottom || birdBottom > bottomPipeTop) {
return true;
}
}
// Check if bird touches pipe - immediate death with resize effect
if (bird.intersects(pipe.topPipe) || bird.intersects(pipe.bottomPipe)) {
// Calculate current pipe scale and apply death effect scaling
var currentTopScaleX = pipe.topPipe.scaleX || 1;
var currentTopScaleY = pipe.topPipe.scaleY || 1;
var currentBottomScaleX = pipe.bottomPipe.scaleX || 1;
var currentBottomScaleY = pipe.bottomPipe.scaleY || 1;
// Resize pipe with tween effect based on current size
tween(pipe.topPipe, {
scaleX: currentTopScaleX * 1.2,
scaleY: currentTopScaleY * 1.2
}, {
duration: 200
});
tween(pipe.bottomPipe, {
scaleX: currentBottomScaleX * 1.2,
scaleY: currentBottomScaleY * 1.2
}, {
duration: 200
});
return true;
}
}
return false;
}
function updateScore() {
for (var i = 0; i < pipes.length; i++) {
var pipe = pipes[i];
if (!pipe.scored && bird.x > pipe.x + 60) {
pipe.scored = true;
LK.setScore(LK.getScore() + 1);
scoreTxt.setText(LK.getScore());
// Update best score if current score is higher
if (LK.getScore() > bestScore) {
bestScore = LK.getScore();
storage.bestScore = bestScore;
bestScoreTxt.setText('BEST: ' + bestScore);
}
LK.getSound('score').play();
}
}
}
function cleanupPipes() {
for (var i = pipes.length - 1; i >= 0; i--) {
var pipe = pipes[i];
if (pipe.x < -120) {
pipe.destroy();
pipes.splice(i, 1);
}
}
}
// Input handling
game.down = function (x, y, obj) {
if (!gameStarted) {
startGame();
} else if (!gameOver) {
bird.flap();
}
};
// Main game loop
game.update = function () {
if (!gameStarted || gameOver) {
return;
}
// Update bird
bird.update();
// Spawn pipes
pipeSpawnTimer++;
var currentDelay = pipes.length === 0 ? initialPipeSpawnDelay : pipeSpawnDelay;
if (pipeSpawnTimer >= currentDelay) {
spawnPipe();
pipeSpawnTimer = 0;
}
// Spawn clouds
cloudSpawnTimer++;
if (cloudSpawnTimer >= cloudSpawnDelay) {
spawnCloud();
cloudSpawnTimer = 0;
}
// Update clouds
for (var c = 0; c < clouds.length; c++) {
clouds[c].update();
}
// Cleanup off-screen clouds
for (var c = clouds.length - 1; c >= 0; c--) {
var cloud = clouds[c];
if (cloud.x < -300) {
cloud.destroy();
clouds.splice(c, 1);
}
}
// Update pipes
for (var i = 0; i < pipes.length; i++) {
pipes[i].update();
}
// Ground is now fixed in GUI layer, no scrolling needed
// Check collisions
if (checkCollisions()) {
gameOver = true;
LK.getSound('hit').play();
LK.showGameOver();
return;
}
// Update score
updateScore();
// Cleanup off-screen pipes
cleanupPipes();
};
Fullscreen modern App Store landscape banner, 16:9, high definition, for a game titled "Flappy Bird" and with the description "Control a bird through pipe obstacles by tapping to flap and avoid crashing. Simple controls, challenging gameplay.". No text on banner!
vertical long pipe. In-Game asset. 2d. High contrast. No shadows
cloudy. In-Game asset. 2d. High contrast. No shadows