/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { highScore: 0 }); /**** * 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.5; self.flapStrength = -10; self.dead = false; self.flap = function () { // Animate wing flapping tween(birdGraphics, { scaleY: 0.8 }, { duration: 100, yoyo: true, repeat: 1, easing: tween.easeInOut }); if (!self.dead) { self.velocity = self.flapStrength; LK.getSound('flap').play(); // Rotate bird upward when flapping tween(self, { rotation: -0.5 }, { duration: 200, easing: tween.easeOut }); } }; self.update = function () { if (!self.dead) { // Add bobbing effect when idle if (!gameStarted) { self.y += Math.sin(LK.ticks * 0.1) * 0.5; } // Apply gravity self.velocity += self.gravity; self.y += self.velocity; // Simulate air resistance with slight horizontal movement self.x += Math.sin(self.y * 0.05) * 0.5; // 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: 100, easing: tween.linear }); } } }; self.die = function () { self.dead = true; LK.getSound('hit').play(); LK.effects.flashObject(self, 0xFF0000, 500); }; return self; }); var Ground = Container.expand(function () { var self = Container.call(this); self.groundGraphics = self.attachAsset('ground', { anchorX: 0, anchorY: 0 }); return self; }); var Pipe = Container.expand(function () { var self = Container.call(this); // Top pipe (from top of screen down to gap) var topPipe = self.attachAsset('pipe', { anchorX: 0.5, anchorY: 1.0 }); // Bottom pipe (from bottom of screen up to gap) var bottomPipe = self.attachAsset('pipe', { anchorX: 0.5, anchorY: 0.0 }); self.speed = 5; self.width = topPipe.width; self.scored = false; // Set the vertical position of the gap between pipes self.setGapPosition = function (y) { // y is the top of the gap (in negative, since gapY is -gapY) // Place topPipe so its bottom is at y topPipe.y = y; // Place bottomPipe so its top is at y + gapSize var gapSize = 500; // You can adjust this for difficulty bottomPipe.y = y + gapSize; }; self.update = function () { self.x -= self.speed; }; // Collision detection helper self.checkCollision = function (bird) { if (bird.dead) { return false; } var birdBox = { x: bird.x - bird.width / 3, y: bird.y - bird.height / 3, width: bird.width / 1.5, height: bird.height / 1.5 }; var topPipeBox = { x: self.x - topPipe.width / 2, y: self.y + topPipe.y - topPipe.height, width: topPipe.width, height: topPipe.height }; var bottomPipeBox = { x: self.x - bottomPipe.width / 2, y: self.y + bottomPipe.y, width: bottomPipe.width, height: bottomPipe.height }; // Check collision with top pipe if (birdBox.x + birdBox.width > topPipeBox.x && birdBox.x < topPipeBox.x + topPipeBox.width && birdBox.y + birdBox.height > topPipeBox.y && birdBox.y < topPipeBox.y + topPipeBox.height) { return true; } // Check collision with bottom pipe if (birdBox.x + birdBox.width > bottomPipeBox.x && birdBox.x < bottomPipeBox.x + bottomPipeBox.width && birdBox.y + birdBox.height > bottomPipeBox.y && birdBox.y < bottomPipeBox.y + bottomPipeBox.height) { return true; } return false; }; self.checkScore = function (bird) { if (bird.dead || self.scored) { return false; } if (self.x + self.width / 2 < bird.x) { self.scored = true; return true; } return false; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87CEEB // Sky blue background }); /**** * Game Code ****/ var gameWidth = 2048; var gameHeight = 2732; var gameStarted = false; var gameOver = false; var bird; var pipes = []; var grounds = []; var score = 0; var highScore = storage.highScore || 0; var pipeSpawnTimer; var groundSpawnTimer; // Text elements var scoreTxt = new Text2('0', { size: 150, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); scoreTxt.y = 100; var instructionsTxt = new Text2('TAP TO START', { size: 100, fill: 0xFFFFFF }); instructionsTxt.anchor.set(0.5, 0.5); LK.gui.center.addChild(instructionsTxt); var highScoreTxt = new Text2('BEST: 0', { size: 70, fill: 0xFFFFFF }); highScoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(highScoreTxt); highScoreTxt.y = 250; highScoreTxt.setText('BEST: ' + highScore); // Initialize bird function createBird() { bird = new Bird(); bird.x = gameWidth * 0.3; bird.y = gameHeight * 0.5; game.addChild(bird); } // Create initial ground segments function createInitialGrounds() { for (var i = 0; i < 3; i++) { var ground = new Ground(); ground.x = i * ground.width; ground.height = ground.groundGraphics.height || 0; ground.width = ground.groundGraphics.width || 0; ground.y = gameHeight - ground.height; grounds.push(ground); game.addChild(ground); } } // Create a new pipe function spawnPipe() { var pipe = new Pipe(); pipe.x = gameWidth + pipe.width / 2; // Random gap position, but not too high or low var minY = gameHeight * 0.2; var maxY = gameHeight * 0.6; var gapY = minY + Math.random() * (maxY - minY); // Set the gap between the pipes pipe.setGapPosition(-gapY); pipes.push(pipe); game.addChild(pipe); } // Create a new ground segment function spawnGround() { var lastGround = grounds[grounds.length - 1]; var ground = new Ground(); // Initialize ground dimensions first ground.height = ground.groundGraphics.height || 0; ground.width = ground.groundGraphics.width || 0; // Position the ground correctly based on lastGround, if it exists if (lastGround && lastGround.x !== undefined) { ground.x = lastGround.x + lastGround.width; } else { // Fallback positioning if lastGround is undefined ground.x = 0; } ground.y = gameHeight - ground.height; grounds.push(ground); game.addChild(ground); } // Update score function updateScore() { score++; scoreTxt.setText(score.toString()); LK.getSound('score').play(); if (score > highScore) { highScore = score; storage.highScore = highScore; highScoreTxt.setText('BEST: ' + highScore); } LK.setScore(score); } // Game over logic function triggerGameOver() { gameOver = true; LK.clearInterval(pipeSpawnTimer); LK.clearInterval(groundSpawnTimer); bird.die(); // Show game over after a short delay LK.setTimeout(function () { LK.showGameOver(); }, 1000); } // Start the game function startGame() { if (!gameStarted) { gameStarted = true; instructionsTxt.visible = false; // Spawn pipes at regular intervals pipeSpawnTimer = LK.setInterval(function () { spawnPipe(); }, 1500); // Spawn ground segments as needed groundSpawnTimer = LK.setInterval(function () { if (grounds.length < 5) { spawnGround(); } }, 1000); // Start background music LK.playMusic('bgmusic', { fade: { start: 0, end: 0.3, duration: 1000 } }); } // Make the bird flap if (bird && !gameOver) { bird.flap(); } } // Initialize game elements createBird(); createInitialGrounds(); // Input handling game.down = function (x, y, obj) { startGame(); }; // Game update loop game.update = function () { if (!gameStarted) { return; } // Update bird if (bird) { bird.update(); // Check for collision with ground if (grounds.length > 0 && bird.y > gameHeight - grounds[0].height - bird.height / 2) { bird.y = gameHeight - grounds[0].height - bird.height / 2; if (!gameOver) { triggerGameOver(); } } // Check for top boundary if (bird.y < bird.height / 2) { bird.y = bird.height / 2; bird.velocity = 0; } } // Update pipes for (var i = pipes.length - 1; i >= 0; i--) { var pipe = pipes[i]; pipe.update(); // Check if pipe is off screen if (pipe.x < -pipe.width) { pipe.destroy(); pipes.splice(i, 1); continue; } // Check for collision with bird if (pipe.checkCollision(bird) && !gameOver) { triggerGameOver(); } // Check if bird passed the pipe to score if (pipe.checkScore(bird)) { updateScore(); } } // Update ground segments for (var j = grounds.length - 1; j >= 0; j--) { var ground = grounds[j]; ground.x -= 5; // Same speed as pipes // Check if ground is off screen if (ground.x + ground.width < 0) { ground.destroy(); grounds.splice(j, 1); } } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0
});
/****
* 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.5;
self.flapStrength = -10;
self.dead = false;
self.flap = function () {
// Animate wing flapping
tween(birdGraphics, {
scaleY: 0.8
}, {
duration: 100,
yoyo: true,
repeat: 1,
easing: tween.easeInOut
});
if (!self.dead) {
self.velocity = self.flapStrength;
LK.getSound('flap').play();
// Rotate bird upward when flapping
tween(self, {
rotation: -0.5
}, {
duration: 200,
easing: tween.easeOut
});
}
};
self.update = function () {
if (!self.dead) {
// Add bobbing effect when idle
if (!gameStarted) {
self.y += Math.sin(LK.ticks * 0.1) * 0.5;
}
// Apply gravity
self.velocity += self.gravity;
self.y += self.velocity;
// Simulate air resistance with slight horizontal movement
self.x += Math.sin(self.y * 0.05) * 0.5;
// 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: 100,
easing: tween.linear
});
}
}
};
self.die = function () {
self.dead = true;
LK.getSound('hit').play();
LK.effects.flashObject(self, 0xFF0000, 500);
};
return self;
});
var Ground = Container.expand(function () {
var self = Container.call(this);
self.groundGraphics = self.attachAsset('ground', {
anchorX: 0,
anchorY: 0
});
return self;
});
var Pipe = Container.expand(function () {
var self = Container.call(this);
// Top pipe (from top of screen down to gap)
var topPipe = self.attachAsset('pipe', {
anchorX: 0.5,
anchorY: 1.0
});
// Bottom pipe (from bottom of screen up to gap)
var bottomPipe = self.attachAsset('pipe', {
anchorX: 0.5,
anchorY: 0.0
});
self.speed = 5;
self.width = topPipe.width;
self.scored = false;
// Set the vertical position of the gap between pipes
self.setGapPosition = function (y) {
// y is the top of the gap (in negative, since gapY is -gapY)
// Place topPipe so its bottom is at y
topPipe.y = y;
// Place bottomPipe so its top is at y + gapSize
var gapSize = 500; // You can adjust this for difficulty
bottomPipe.y = y + gapSize;
};
self.update = function () {
self.x -= self.speed;
};
// Collision detection helper
self.checkCollision = function (bird) {
if (bird.dead) {
return false;
}
var birdBox = {
x: bird.x - bird.width / 3,
y: bird.y - bird.height / 3,
width: bird.width / 1.5,
height: bird.height / 1.5
};
var topPipeBox = {
x: self.x - topPipe.width / 2,
y: self.y + topPipe.y - topPipe.height,
width: topPipe.width,
height: topPipe.height
};
var bottomPipeBox = {
x: self.x - bottomPipe.width / 2,
y: self.y + bottomPipe.y,
width: bottomPipe.width,
height: bottomPipe.height
};
// Check collision with top pipe
if (birdBox.x + birdBox.width > topPipeBox.x && birdBox.x < topPipeBox.x + topPipeBox.width && birdBox.y + birdBox.height > topPipeBox.y && birdBox.y < topPipeBox.y + topPipeBox.height) {
return true;
}
// Check collision with bottom pipe
if (birdBox.x + birdBox.width > bottomPipeBox.x && birdBox.x < bottomPipeBox.x + bottomPipeBox.width && birdBox.y + birdBox.height > bottomPipeBox.y && birdBox.y < bottomPipeBox.y + bottomPipeBox.height) {
return true;
}
return false;
};
self.checkScore = function (bird) {
if (bird.dead || self.scored) {
return false;
}
if (self.x + self.width / 2 < bird.x) {
self.scored = true;
return true;
}
return false;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB // Sky blue background
});
/****
* Game Code
****/
var gameWidth = 2048;
var gameHeight = 2732;
var gameStarted = false;
var gameOver = false;
var bird;
var pipes = [];
var grounds = [];
var score = 0;
var highScore = storage.highScore || 0;
var pipeSpawnTimer;
var groundSpawnTimer;
// Text elements
var scoreTxt = new Text2('0', {
size: 150,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
scoreTxt.y = 100;
var instructionsTxt = new Text2('TAP TO START', {
size: 100,
fill: 0xFFFFFF
});
instructionsTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(instructionsTxt);
var highScoreTxt = new Text2('BEST: 0', {
size: 70,
fill: 0xFFFFFF
});
highScoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(highScoreTxt);
highScoreTxt.y = 250;
highScoreTxt.setText('BEST: ' + highScore);
// Initialize bird
function createBird() {
bird = new Bird();
bird.x = gameWidth * 0.3;
bird.y = gameHeight * 0.5;
game.addChild(bird);
}
// Create initial ground segments
function createInitialGrounds() {
for (var i = 0; i < 3; i++) {
var ground = new Ground();
ground.x = i * ground.width;
ground.height = ground.groundGraphics.height || 0;
ground.width = ground.groundGraphics.width || 0;
ground.y = gameHeight - ground.height;
grounds.push(ground);
game.addChild(ground);
}
}
// Create a new pipe
function spawnPipe() {
var pipe = new Pipe();
pipe.x = gameWidth + pipe.width / 2;
// Random gap position, but not too high or low
var minY = gameHeight * 0.2;
var maxY = gameHeight * 0.6;
var gapY = minY + Math.random() * (maxY - minY);
// Set the gap between the pipes
pipe.setGapPosition(-gapY);
pipes.push(pipe);
game.addChild(pipe);
}
// Create a new ground segment
function spawnGround() {
var lastGround = grounds[grounds.length - 1];
var ground = new Ground();
// Initialize ground dimensions first
ground.height = ground.groundGraphics.height || 0;
ground.width = ground.groundGraphics.width || 0;
// Position the ground correctly based on lastGround, if it exists
if (lastGround && lastGround.x !== undefined) {
ground.x = lastGround.x + lastGround.width;
} else {
// Fallback positioning if lastGround is undefined
ground.x = 0;
}
ground.y = gameHeight - ground.height;
grounds.push(ground);
game.addChild(ground);
}
// Update score
function updateScore() {
score++;
scoreTxt.setText(score.toString());
LK.getSound('score').play();
if (score > highScore) {
highScore = score;
storage.highScore = highScore;
highScoreTxt.setText('BEST: ' + highScore);
}
LK.setScore(score);
}
// Game over logic
function triggerGameOver() {
gameOver = true;
LK.clearInterval(pipeSpawnTimer);
LK.clearInterval(groundSpawnTimer);
bird.die();
// Show game over after a short delay
LK.setTimeout(function () {
LK.showGameOver();
}, 1000);
}
// Start the game
function startGame() {
if (!gameStarted) {
gameStarted = true;
instructionsTxt.visible = false;
// Spawn pipes at regular intervals
pipeSpawnTimer = LK.setInterval(function () {
spawnPipe();
}, 1500);
// Spawn ground segments as needed
groundSpawnTimer = LK.setInterval(function () {
if (grounds.length < 5) {
spawnGround();
}
}, 1000);
// Start background music
LK.playMusic('bgmusic', {
fade: {
start: 0,
end: 0.3,
duration: 1000
}
});
}
// Make the bird flap
if (bird && !gameOver) {
bird.flap();
}
}
// Initialize game elements
createBird();
createInitialGrounds();
// Input handling
game.down = function (x, y, obj) {
startGame();
};
// Game update loop
game.update = function () {
if (!gameStarted) {
return;
}
// Update bird
if (bird) {
bird.update();
// Check for collision with ground
if (grounds.length > 0 && bird.y > gameHeight - grounds[0].height - bird.height / 2) {
bird.y = gameHeight - grounds[0].height - bird.height / 2;
if (!gameOver) {
triggerGameOver();
}
}
// Check for top boundary
if (bird.y < bird.height / 2) {
bird.y = bird.height / 2;
bird.velocity = 0;
}
}
// Update pipes
for (var i = pipes.length - 1; i >= 0; i--) {
var pipe = pipes[i];
pipe.update();
// Check if pipe is off screen
if (pipe.x < -pipe.width) {
pipe.destroy();
pipes.splice(i, 1);
continue;
}
// Check for collision with bird
if (pipe.checkCollision(bird) && !gameOver) {
triggerGameOver();
}
// Check if bird passed the pipe to score
if (pipe.checkScore(bird)) {
updateScore();
}
}
// Update ground segments
for (var j = grounds.length - 1; j >= 0; j--) {
var ground = grounds[j];
ground.x -= 5; // Same speed as pipes
// Check if ground is off screen
if (ground.x + ground.width < 0) {
ground.destroy();
grounds.splice(j, 1);
}
}
};