/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Background = Container.expand(function () { var self = Container.call(this); // Create sky var sky = self.attachAsset('sky', { anchorX: 0, anchorY: 0 }); // Create scrolling ground var ground1 = self.attachAsset('ground', { anchorX: 0, anchorY: 0 }); var ground2 = self.attachAsset('ground', { anchorX: 0, anchorY: 0 }); // Position ground at bottom of screen ground1.y = 2732 - ground1.height; ground2.y = 2732 - ground2.height; ground2.x = ground1.width; self.ground1 = ground1; self.ground2 = ground2; self.groundY = ground1.y; // Update ground scroll effect self.update = function () { if (game.gameActive) { // Scroll ground ground1.x -= 5; ground2.x -= 5; // Loop ground tiles if (ground1.x <= -ground1.width) { ground1.x = ground2.x + ground2.width; } if (ground2.x <= -ground2.width) { ground2.x = ground1.x + ground1.width; } } }; return self; }); var Bird = Container.expand(function () { var self = Container.call(this); // Bird graphics var birdGraphics = self.attachAsset('bird', { anchorX: 0.5, anchorY: 0.5 }); // Bird physics self.velocity = 0; self.gravity = 0.5; self.flapPower = -10; self.rotation = 0; self.isDead = false; self.lastY = 0; // Flap the bird's wings (jump) self.flap = function () { if (self.isDead) return; self.velocity = self.flapPower; LK.getSound('flap').play(); // Animate rotation upward tween(self, { rotation: -0.3 }, { duration: 100, easing: tween.easeOut }); }; // Update bird position and physics self.update = function () { self.lastY = self.y; if (!self.isDead) { // Apply gravity self.velocity += self.gravity; self.y += self.velocity; // Rotate bird based on velocity if (self.velocity > 0) { var targetRotation = Math.min(Math.PI / 2, self.velocity * 0.05); tween(self, { rotation: targetRotation }, { duration: 200, easing: tween.linear }); } } }; // Handle bird death self.die = function () { if (self.isDead) return; self.isDead = true; LK.getSound('hit').play(); // Animate death (fall with rotation) tween(self, { rotation: Math.PI / 2 }, { duration: 500, easing: tween.easeIn }); }; return self; }); var Pipe = Container.expand(function () { var self = Container.call(this); // Pipe properties self.speed = 5; self.passed = false; self.lastX = 0; // Create top and bottom pipes self.init = function (gapY, gapHeight) { // Top pipe var topPipe = self.attachAsset('pipe', { anchorX: 0.5, anchorY: 1.0 // Bottom anchor }); topPipe.y = gapY - gapHeight / 2; // Bottom pipe var bottomPipe = self.attachAsset('pipe', { anchorX: 0.5, anchorY: 0.0 // Top anchor }); bottomPipe.y = gapY + gapHeight / 2; // Store references self.topPipe = topPipe; self.bottomPipe = bottomPipe; }; // Update pipe position self.update = function () { self.lastX = self.x; self.x -= self.speed; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Set background color game.setBackgroundColor(0x87CEEB); // Game variables var pipes = []; var pipeSpawnInterval = 90; // In ticks (frames) var gapHeight = 300; // Gap between pipes var score = 0; var gameStarted = false; var gameActive = true; game.gameActive = gameActive; // Make it accessible to other classes // Create background var background = new Background(); game.addChild(background); // Create bird var bird = new Bird(); bird.x = 400; bird.y = 2732 / 2; game.addChild(bird); // Create score text var scoreText = new Text2('0', { size: 150, fill: 0xFFFFFF }); scoreText.anchor.set(0.5, 0); scoreText.y = 100; scoreText.x = 2048 / 2; LK.gui.top.addChild(scoreText); // Create tap to start text var startText = new Text2('TAP TO START', { size: 100, fill: 0xFFFFFF }); startText.anchor.set(0.5, 0.5); startText.x = 2048 / 2; startText.y = 2732 / 2; LK.gui.center.addChild(startText); // Create flash overlay for game over var flashOverlay = LK.getAsset('flash', { anchorX: 0, anchorY: 0, alpha: 0 }); game.addChild(flashOverlay); // Game event handlers game.down = function (x, y, obj) { if (!gameStarted) { // First tap starts the game gameStarted = true; startText.visible = false; } if (gameActive) { bird.flap(); } }; // Spawn a new pipe pair function spawnPipe() { var pipe = new Pipe(); // Random gap position between pipes (constrained to middle 60% of screen) var minY = 2732 * 0.2; var maxY = 2732 * 0.8 - background.ground1.height; var gapY = minY + Math.random() * (maxY - minY); pipe.init(gapY, gapHeight); pipe.x = 2048 + 100; pipes.push(pipe); game.addChild(pipe); } // Check collisions function checkCollisions() { // Ground collision if (bird.y + bird.height / 2 >= background.groundY) { bird.y = background.groundY - bird.height / 2; gameOver(); return; } // Ceiling collision if (bird.y - bird.height / 2 <= 0) { bird.y = bird.height / 2; bird.velocity = 0; } // Pipe collisions for (var i = 0; i < pipes.length; i++) { var pipe = pipes[i]; if (bird.intersects(pipe.topPipe) || bird.intersects(pipe.bottomPipe)) { gameOver(); return; } // Score when passing pipe if (!pipe.passed && pipe.lastX > bird.x && pipe.x <= bird.x) { pipe.passed = true; score++; LK.setScore(score); scoreText.setText(score.toString()); LK.getSound('score').play(); } } } // Game over handling function gameOver() { if (!gameActive) return; gameActive = false; game.gameActive = false; bird.die(); // Flash effect flashOverlay.alpha = 0.8; tween(flashOverlay, { alpha: 0 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { // Show game over after flash LK.setTimeout(function () { LK.showGameOver(); }, 1000); } }); } // Main game update loop game.update = function () { if (!gameStarted) return; // Update bird bird.update(); if (gameActive) { // Spawn pipes at interval if (LK.ticks % pipeSpawnInterval === 0) { spawnPipe(); } // Update pipes for (var i = pipes.length - 1; i >= 0; i--) { var pipe = pipes[i]; pipe.update(); // Remove pipes that have gone off screen if (pipe.x < -150) { pipe.destroy(); pipes.splice(i, 1); } } // Check for collisions checkCollisions(); } // Update background background.update(); };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Background = Container.expand(function () {
var self = Container.call(this);
// Create sky
var sky = self.attachAsset('sky', {
anchorX: 0,
anchorY: 0
});
// Create scrolling ground
var ground1 = self.attachAsset('ground', {
anchorX: 0,
anchorY: 0
});
var ground2 = self.attachAsset('ground', {
anchorX: 0,
anchorY: 0
});
// Position ground at bottom of screen
ground1.y = 2732 - ground1.height;
ground2.y = 2732 - ground2.height;
ground2.x = ground1.width;
self.ground1 = ground1;
self.ground2 = ground2;
self.groundY = ground1.y;
// Update ground scroll effect
self.update = function () {
if (game.gameActive) {
// Scroll ground
ground1.x -= 5;
ground2.x -= 5;
// Loop ground tiles
if (ground1.x <= -ground1.width) {
ground1.x = ground2.x + ground2.width;
}
if (ground2.x <= -ground2.width) {
ground2.x = ground1.x + ground1.width;
}
}
};
return self;
});
var Bird = Container.expand(function () {
var self = Container.call(this);
// Bird graphics
var birdGraphics = self.attachAsset('bird', {
anchorX: 0.5,
anchorY: 0.5
});
// Bird physics
self.velocity = 0;
self.gravity = 0.5;
self.flapPower = -10;
self.rotation = 0;
self.isDead = false;
self.lastY = 0;
// Flap the bird's wings (jump)
self.flap = function () {
if (self.isDead) return;
self.velocity = self.flapPower;
LK.getSound('flap').play();
// Animate rotation upward
tween(self, {
rotation: -0.3
}, {
duration: 100,
easing: tween.easeOut
});
};
// Update bird position and physics
self.update = function () {
self.lastY = self.y;
if (!self.isDead) {
// Apply gravity
self.velocity += self.gravity;
self.y += self.velocity;
// Rotate bird based on velocity
if (self.velocity > 0) {
var targetRotation = Math.min(Math.PI / 2, self.velocity * 0.05);
tween(self, {
rotation: targetRotation
}, {
duration: 200,
easing: tween.linear
});
}
}
};
// Handle bird death
self.die = function () {
if (self.isDead) return;
self.isDead = true;
LK.getSound('hit').play();
// Animate death (fall with rotation)
tween(self, {
rotation: Math.PI / 2
}, {
duration: 500,
easing: tween.easeIn
});
};
return self;
});
var Pipe = Container.expand(function () {
var self = Container.call(this);
// Pipe properties
self.speed = 5;
self.passed = false;
self.lastX = 0;
// Create top and bottom pipes
self.init = function (gapY, gapHeight) {
// Top pipe
var topPipe = self.attachAsset('pipe', {
anchorX: 0.5,
anchorY: 1.0 // Bottom anchor
});
topPipe.y = gapY - gapHeight / 2;
// Bottom pipe
var bottomPipe = self.attachAsset('pipe', {
anchorX: 0.5,
anchorY: 0.0 // Top anchor
});
bottomPipe.y = gapY + gapHeight / 2;
// Store references
self.topPipe = topPipe;
self.bottomPipe = bottomPipe;
};
// Update pipe position
self.update = function () {
self.lastX = self.x;
self.x -= self.speed;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Set background color
game.setBackgroundColor(0x87CEEB);
// Game variables
var pipes = [];
var pipeSpawnInterval = 90; // In ticks (frames)
var gapHeight = 300; // Gap between pipes
var score = 0;
var gameStarted = false;
var gameActive = true;
game.gameActive = gameActive; // Make it accessible to other classes
// Create background
var background = new Background();
game.addChild(background);
// Create bird
var bird = new Bird();
bird.x = 400;
bird.y = 2732 / 2;
game.addChild(bird);
// Create score text
var scoreText = new Text2('0', {
size: 150,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
scoreText.y = 100;
scoreText.x = 2048 / 2;
LK.gui.top.addChild(scoreText);
// Create tap to start text
var startText = new Text2('TAP TO START', {
size: 100,
fill: 0xFFFFFF
});
startText.anchor.set(0.5, 0.5);
startText.x = 2048 / 2;
startText.y = 2732 / 2;
LK.gui.center.addChild(startText);
// Create flash overlay for game over
var flashOverlay = LK.getAsset('flash', {
anchorX: 0,
anchorY: 0,
alpha: 0
});
game.addChild(flashOverlay);
// Game event handlers
game.down = function (x, y, obj) {
if (!gameStarted) {
// First tap starts the game
gameStarted = true;
startText.visible = false;
}
if (gameActive) {
bird.flap();
}
};
// Spawn a new pipe pair
function spawnPipe() {
var pipe = new Pipe();
// Random gap position between pipes (constrained to middle 60% of screen)
var minY = 2732 * 0.2;
var maxY = 2732 * 0.8 - background.ground1.height;
var gapY = minY + Math.random() * (maxY - minY);
pipe.init(gapY, gapHeight);
pipe.x = 2048 + 100;
pipes.push(pipe);
game.addChild(pipe);
}
// Check collisions
function checkCollisions() {
// Ground collision
if (bird.y + bird.height / 2 >= background.groundY) {
bird.y = background.groundY - bird.height / 2;
gameOver();
return;
}
// Ceiling collision
if (bird.y - bird.height / 2 <= 0) {
bird.y = bird.height / 2;
bird.velocity = 0;
}
// Pipe collisions
for (var i = 0; i < pipes.length; i++) {
var pipe = pipes[i];
if (bird.intersects(pipe.topPipe) || bird.intersects(pipe.bottomPipe)) {
gameOver();
return;
}
// Score when passing pipe
if (!pipe.passed && pipe.lastX > bird.x && pipe.x <= bird.x) {
pipe.passed = true;
score++;
LK.setScore(score);
scoreText.setText(score.toString());
LK.getSound('score').play();
}
}
}
// Game over handling
function gameOver() {
if (!gameActive) return;
gameActive = false;
game.gameActive = false;
bird.die();
// Flash effect
flashOverlay.alpha = 0.8;
tween(flashOverlay, {
alpha: 0
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
// Show game over after flash
LK.setTimeout(function () {
LK.showGameOver();
}, 1000);
}
});
}
// Main game update loop
game.update = function () {
if (!gameStarted) return;
// Update bird
bird.update();
if (gameActive) {
// Spawn pipes at interval
if (LK.ticks % pipeSpawnInterval === 0) {
spawnPipe();
}
// Update pipes
for (var i = pipes.length - 1; i >= 0; i--) {
var pipe = pipes[i];
pipe.update();
// Remove pipes that have gone off screen
if (pipe.x < -150) {
pipe.destroy();
pipes.splice(i, 1);
}
}
// Check for collisions
checkCollisions();
}
// Update background
background.update();
};