/**** * 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 speed self.gravity = 0.7; // gravity per frame (further reduced for slower fall) self.flapStrength = -18; // negative = up (further reduced for softer flap) self.flap = function () { self.vy = self.flapStrength; LK.getSound('flap').play(); // Animate a quick scale for feedback tween(birdSprite, { scaleY: 0.7 }, { duration: 60, easing: tween.cubicOut, onFinish: function onFinish() { tween(birdSprite, { scaleY: 1 }, { duration: 120, easing: tween.cubicOut }); } }); }; self.update = function () { self.vy += self.gravity; self.y += self.vy; // Clamp rotation for visual feedback var maxAngle = Math.PI / 4; var minAngle = -Math.PI / 6; var t = Math.max(-1, Math.min(1, self.vy / 40)); birdSprite.rotation = minAngle + (maxAngle - minAngle) * (t + 1) / 2; }; return self; }); // Ground class var Ground = Container.expand(function () { var self = Container.call(this); var groundSprite = self.attachAsset('ground', { anchorX: 0, anchorY: 0 }); return self; }); // PipePair class (top and bottom pipes) var PipePair = Container.expand(function () { var self = Container.call(this); // Pipe gap size self.gap = 600; // Pipe speed self.speed = 9; // Top pipe self.topPipe = self.attachAsset('pipe', { anchorX: 0.5, anchorY: 1 }); // Bottom pipe self.bottomPipe = self.attachAsset('pipe', { anchorX: 0.5, anchorY: 0 }); // Set pipes' positions based on gapY self.setGapY = function (gapY) { self.topPipe.y = gapY - self.gap / 2; self.bottomPipe.y = gapY + self.gap / 2; // Extend top pipe to reach ceiling self.topPipe.height = self.topPipe.y + 750; // Extend bottom pipe to reach ground self.bottomPipe.height = GROUND_Y - self.bottomPipe.y + 750; }; // For scoring self.passed = false; self.update = function () { self.x -= self.speed; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87ceeb // Sky blue }); /**** * Game Code ****/ // Game constants // Bird: yellow ellipse // Pipe: green box // Ground: brown box // Sound: Flap // Sound: Score // Sound: Hit var BIRD_START_X = 600; var BIRD_START_Y = 1100; var PIPE_INTERVAL = 90; // frames between pipes var PIPE_MIN_Y = 500; var PIPE_MAX_Y = 2000; var GROUND_Y = 2732 - 120; // Game state var bird; var pipes = []; var ground; var score = 0; var scoreTxt; var gameStarted = false; var gameOver = false; var gameInMenu = true; var lastPipeTick = 0; // GUI score scoreTxt = new Text2('0', { size: 150, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Menu UI elements var menuTitle = new Text2('FLAPPY BIRD', { size: 200, fill: 0xFFFFFF }); menuTitle.anchor.set(0.5, 0.5); menuTitle.x = 1024; menuTitle.y = 600; game.addChild(menuTitle); // Create a container for the play instruction and bird var menuPlayContainer = new Container(); menuPlayContainer.x = 1024; menuPlayContainer.y = 1200; game.addChild(menuPlayContainer); var menuInstructions = new Text2('OYNA KANKAM', { size: 100, fill: 0xFFDD00 }); menuInstructions.anchor.set(0.5, 0.5); menuInstructions.x = 0; menuInstructions.y = 0; menuPlayContainer.addChild(menuInstructions); var menuSubtitle = new Text2('Tap to flap and fly through pipes!', { size: 80, fill: 0xCCCCCC }); menuSubtitle.anchor.set(0.5, 0.5); menuSubtitle.x = 1024; menuSubtitle.y = 1200; game.addChild(menuSubtitle); // Add ground ground = new Ground(); ground.x = 0; ground.y = GROUND_Y; game.addChild(ground); // Add bird bird = new Bird(); bird.x = BIRD_START_X; bird.y = BIRD_START_Y; 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 = BIRD_START_X; bird.y = BIRD_START_Y; bird.vy = 0; // Reset score score = 0; scoreTxt.setText(score); // Reset state gameStarted = false; gameOver = false; gameInMenu = true; lastPipeTick = LK.ticks; // Show menu elements menuTitle.visible = true; menuPlayContainer.visible = true; menuSubtitle.visible = true; scoreTxt.visible = false; } // Start game on first tap game.down = function (x, y, obj) { if (gameOver) return; if (gameInMenu) { // Start game from menu gameInMenu = false; gameStarted = true; // Hide menu elements menuTitle.visible = false; menuPlayContainer.visible = false; menuSubtitle.visible = false; scoreTxt.visible = true; bird.flap(); return; } if (!gameStarted) { gameStarted = true; bird.flap(); return; } bird.flap(); }; // Main update loop game.update = function () { if (gameOver) return; if (gameInMenu) { // Bird idle bounce in menu bird.y = BIRD_START_Y + Math.sin(LK.ticks / 20) * 20; // Animate menu instructions menuInstructions.alpha = 0.5 + 0.5 * Math.sin(LK.ticks / 30); return; } if (!gameStarted) { // Bird idle bounce bird.y = BIRD_START_Y + Math.sin(LK.ticks / 20) * 20; return; } bird.update(); // Add pipes if (LK.ticks - lastPipeTick > PIPE_INTERVAL) { lastPipeTick = LK.ticks; var gapY = PIPE_MIN_Y + Math.floor(Math.random() * (PIPE_MAX_Y - PIPE_MIN_Y)); var pipePair = new PipePair(); pipePair.x = 2048 + 200; pipePair.setGapY(gapY); pipes.push(pipePair); game.addChild(pipePair); } // Update pipes and check for collisions for (var i = pipes.length - 1; i >= 0; i--) { var pipe = pipes[i]; pipe.update(); // Collision with pipes if (bird.x + 60 > pipe.x - 60 && bird.x - 60 < pipe.x + 60 // horizontal overlap ) { // Top pipe if (bird.y - 45 < pipe.topPipe.y) { // Hit top pipe endGame(); return; } // Bottom pipe if (bird.y + 45 > pipe.bottomPipe.y) { // Hit bottom pipe endGame(); return; } } // Score: passed pipe if (!pipe.passed && pipe.x + 60 < bird.x - 60) { pipe.passed = true; score += 1; scoreTxt.setText(score); LK.getSound('score').play(); } // Remove off-screen pipes if (pipe.x < -300) { pipe.destroy(); pipes.splice(i, 1); } } // Collision with ground if (bird.y + 45 > GROUND_Y) { endGame(); return; } // Collision with ceiling if (bird.y - 45 < 0) { bird.y = 45; bird.vy = 0; } }; // End game function endGame() { if (gameOver) return; gameOver = true; LK.getSound('hit').play(); LK.effects.flashScreen(0xff0000, 600); // Show game over popup (handled by LK) LK.showGameOver(); } // Reset on game over (handled by LK) game.on('reset', 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 speed
self.gravity = 0.7; // gravity per frame (further reduced for slower fall)
self.flapStrength = -18; // negative = up (further reduced for softer flap)
self.flap = function () {
self.vy = self.flapStrength;
LK.getSound('flap').play();
// Animate a quick scale for feedback
tween(birdSprite, {
scaleY: 0.7
}, {
duration: 60,
easing: tween.cubicOut,
onFinish: function onFinish() {
tween(birdSprite, {
scaleY: 1
}, {
duration: 120,
easing: tween.cubicOut
});
}
});
};
self.update = function () {
self.vy += self.gravity;
self.y += self.vy;
// Clamp rotation for visual feedback
var maxAngle = Math.PI / 4;
var minAngle = -Math.PI / 6;
var t = Math.max(-1, Math.min(1, self.vy / 40));
birdSprite.rotation = minAngle + (maxAngle - minAngle) * (t + 1) / 2;
};
return self;
});
// Ground class
var Ground = Container.expand(function () {
var self = Container.call(this);
var groundSprite = self.attachAsset('ground', {
anchorX: 0,
anchorY: 0
});
return self;
});
// PipePair class (top and bottom pipes)
var PipePair = Container.expand(function () {
var self = Container.call(this);
// Pipe gap size
self.gap = 600;
// Pipe speed
self.speed = 9;
// Top pipe
self.topPipe = self.attachAsset('pipe', {
anchorX: 0.5,
anchorY: 1
});
// Bottom pipe
self.bottomPipe = self.attachAsset('pipe', {
anchorX: 0.5,
anchorY: 0
});
// Set pipes' positions based on gapY
self.setGapY = function (gapY) {
self.topPipe.y = gapY - self.gap / 2;
self.bottomPipe.y = gapY + self.gap / 2;
// Extend top pipe to reach ceiling
self.topPipe.height = self.topPipe.y + 750;
// Extend bottom pipe to reach ground
self.bottomPipe.height = GROUND_Y - self.bottomPipe.y + 750;
};
// For scoring
self.passed = false;
self.update = function () {
self.x -= self.speed;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87ceeb // Sky blue
});
/****
* Game Code
****/
// Game constants
// Bird: yellow ellipse
// Pipe: green box
// Ground: brown box
// Sound: Flap
// Sound: Score
// Sound: Hit
var BIRD_START_X = 600;
var BIRD_START_Y = 1100;
var PIPE_INTERVAL = 90; // frames between pipes
var PIPE_MIN_Y = 500;
var PIPE_MAX_Y = 2000;
var GROUND_Y = 2732 - 120;
// Game state
var bird;
var pipes = [];
var ground;
var score = 0;
var scoreTxt;
var gameStarted = false;
var gameOver = false;
var gameInMenu = true;
var lastPipeTick = 0;
// GUI score
scoreTxt = new Text2('0', {
size: 150,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Menu UI elements
var menuTitle = new Text2('FLAPPY BIRD', {
size: 200,
fill: 0xFFFFFF
});
menuTitle.anchor.set(0.5, 0.5);
menuTitle.x = 1024;
menuTitle.y = 600;
game.addChild(menuTitle);
// Create a container for the play instruction and bird
var menuPlayContainer = new Container();
menuPlayContainer.x = 1024;
menuPlayContainer.y = 1200;
game.addChild(menuPlayContainer);
var menuInstructions = new Text2('OYNA KANKAM', {
size: 100,
fill: 0xFFDD00
});
menuInstructions.anchor.set(0.5, 0.5);
menuInstructions.x = 0;
menuInstructions.y = 0;
menuPlayContainer.addChild(menuInstructions);
var menuSubtitle = new Text2('Tap to flap and fly through pipes!', {
size: 80,
fill: 0xCCCCCC
});
menuSubtitle.anchor.set(0.5, 0.5);
menuSubtitle.x = 1024;
menuSubtitle.y = 1200;
game.addChild(menuSubtitle);
// Add ground
ground = new Ground();
ground.x = 0;
ground.y = GROUND_Y;
game.addChild(ground);
// Add bird
bird = new Bird();
bird.x = BIRD_START_X;
bird.y = BIRD_START_Y;
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 = BIRD_START_X;
bird.y = BIRD_START_Y;
bird.vy = 0;
// Reset score
score = 0;
scoreTxt.setText(score);
// Reset state
gameStarted = false;
gameOver = false;
gameInMenu = true;
lastPipeTick = LK.ticks;
// Show menu elements
menuTitle.visible = true;
menuPlayContainer.visible = true;
menuSubtitle.visible = true;
scoreTxt.visible = false;
}
// Start game on first tap
game.down = function (x, y, obj) {
if (gameOver) return;
if (gameInMenu) {
// Start game from menu
gameInMenu = false;
gameStarted = true;
// Hide menu elements
menuTitle.visible = false;
menuPlayContainer.visible = false;
menuSubtitle.visible = false;
scoreTxt.visible = true;
bird.flap();
return;
}
if (!gameStarted) {
gameStarted = true;
bird.flap();
return;
}
bird.flap();
};
// Main update loop
game.update = function () {
if (gameOver) return;
if (gameInMenu) {
// Bird idle bounce in menu
bird.y = BIRD_START_Y + Math.sin(LK.ticks / 20) * 20;
// Animate menu instructions
menuInstructions.alpha = 0.5 + 0.5 * Math.sin(LK.ticks / 30);
return;
}
if (!gameStarted) {
// Bird idle bounce
bird.y = BIRD_START_Y + Math.sin(LK.ticks / 20) * 20;
return;
}
bird.update();
// Add pipes
if (LK.ticks - lastPipeTick > PIPE_INTERVAL) {
lastPipeTick = LK.ticks;
var gapY = PIPE_MIN_Y + Math.floor(Math.random() * (PIPE_MAX_Y - PIPE_MIN_Y));
var pipePair = new PipePair();
pipePair.x = 2048 + 200;
pipePair.setGapY(gapY);
pipes.push(pipePair);
game.addChild(pipePair);
}
// Update pipes and check for collisions
for (var i = pipes.length - 1; i >= 0; i--) {
var pipe = pipes[i];
pipe.update();
// Collision with pipes
if (bird.x + 60 > pipe.x - 60 && bird.x - 60 < pipe.x + 60 // horizontal overlap
) {
// Top pipe
if (bird.y - 45 < pipe.topPipe.y) {
// Hit top pipe
endGame();
return;
}
// Bottom pipe
if (bird.y + 45 > pipe.bottomPipe.y) {
// Hit bottom pipe
endGame();
return;
}
}
// Score: passed pipe
if (!pipe.passed && pipe.x + 60 < bird.x - 60) {
pipe.passed = true;
score += 1;
scoreTxt.setText(score);
LK.getSound('score').play();
}
// Remove off-screen pipes
if (pipe.x < -300) {
pipe.destroy();
pipes.splice(i, 1);
}
}
// Collision with ground
if (bird.y + 45 > GROUND_Y) {
endGame();
return;
}
// Collision with ceiling
if (bird.y - 45 < 0) {
bird.y = 45;
bird.vy = 0;
}
};
// End game
function endGame() {
if (gameOver) return;
gameOver = true;
LK.getSound('hit').play();
LK.effects.flashScreen(0xff0000, 600);
// Show game over popup (handled by LK)
LK.showGameOver();
}
// Reset on game over (handled by LK)
game.on('reset', function () {
resetGame();
});
// Initial reset
resetGame();