/****
* 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.8;
self.flapPower = -12;
self.maxVelocity = 15;
self.flap = function () {
self.velocity = self.flapPower;
LK.getSound('flap').play();
// Flap animation
tween(birdGraphics, {
rotation: -0.3
}, {
duration: 100
});
tween(birdGraphics, {
rotation: 0.3
}, {
duration: 200,
onFinish: function onFinish() {
tween(birdGraphics, {
rotation: 0
}, {
duration: 100
});
}
});
};
self.update = function () {
self.velocity += self.gravity;
if (self.velocity > self.maxVelocity) {
self.velocity = self.maxVelocity;
}
self.y += self.velocity;
// Rotate bird based on velocity
var targetRotation = Math.min(Math.max(self.velocity * 0.1, -0.5), 0.8);
birdGraphics.rotation = targetRotation;
};
return self;
});
var Pipe = Container.expand(function () {
var self = Container.call(this);
self.gapSize = 300;
self.speed = -4;
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.setGapPosition = function (gapY) {
self.topPipe.y = gapY - self.gapSize / 2;
self.bottomPipe.y = gapY + self.gapSize / 2;
// Make bottom pipe extend to ground - position it properly and scale it
var groundY = 2732 - 100; // Ground position
var bottomPipeTop = gapY + self.gapSize / 2;
var distanceToGround = groundY - bottomPipeTop;
// Position bottom pipe at gap bottom and scale to reach ground
self.bottomPipe.y = bottomPipeTop;
self.bottomPipe.scaleY = distanceToGround / 800; // Scale to reach ground
self.bottomPipe.anchorY = 0; // Anchor at top so it grows downward
};
self.update = function () {
self.x += self.speed;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
var bird;
var pipes = [];
var ground;
var scoreTxt;
var gameStarted = false;
var pipeSpawnTimer = 0;
var pipeDistance = 400;
// Create bird
bird = game.addChild(new Bird());
bird.x = 300;
bird.y = 1366; // Center vertically
// Create ground
ground = game.addChild(LK.getAsset('ground', {
anchorX: 0,
anchorY: 0
}));
ground.x = 0;
ground.y = 2732 - 100;
// Create score display
scoreTxt = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
scoreTxt.y = 150;
// Create best score display
var bestScoreTxt = new Text2('Best: ' + (storage.bestScore || 0), {
size: 60,
fill: 0xFFD700
});
bestScoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(bestScoreTxt);
bestScoreTxt.y = 250;
// Custom game over screen variables
var gameOverScreen = null;
// Game input handler
game.down = function (x, y, obj) {
if (!gameStarted) {
gameStarted = true;
}
bird.flap();
};
// Main game loop
game.update = function () {
if (!gameStarted) return;
// Spawn pipes
pipeSpawnTimer++;
if (pipeSpawnTimer >= pipeDistance / 4) {
var newPipe = new Pipe();
newPipe.x = 2048 + 60;
// Random gap position between 300 and 2000
var gapY = 300 + Math.random() * (2000 - 300);
newPipe.setGapPosition(gapY);
pipes.push(newPipe);
game.addChild(newPipe);
pipeSpawnTimer = 0;
}
// Update pipes and check collisions
for (var i = pipes.length - 1; i >= 0; i--) {
var pipe = pipes[i];
// Track last position for scoring
if (pipe.lastX === undefined) pipe.lastX = pipe.x;
// Check for scoring (bird passes pipe)
if (!pipe.scored && pipe.lastX > bird.x && pipe.x <= bird.x) {
pipe.scored = true;
LK.setScore(LK.getScore() + 1);
var currentScore = LK.getScore();
scoreTxt.setText('Score: ' + currentScore);
// Update best score if current score is higher
var bestScore = storage.bestScore || 0;
if (currentScore > bestScore) {
storage.bestScore = currentScore;
bestScoreTxt.setText('Best: ' + currentScore);
}
LK.getSound('score').play();
}
// Remove off-screen pipes
if (pipe.x < -120) {
pipe.destroy();
pipes.splice(i, 1);
continue;
}
// Collision detection
var birdBounds = {
x: bird.x - 30,
y: bird.y - 20,
width: 60,
height: 40
};
var topPipeBounds = {
x: pipe.x - 60,
y: 0,
width: 120,
height: pipe.topPipe.y
};
var bottomPipeBounds = {
x: pipe.x - 60,
y: pipe.bottomPipe.y,
width: 120,
height: 2732 - 100 - pipe.bottomPipe.y // Height from pipe start to ground
};
// Check collision with pipes
if (birdBounds.x < topPipeBounds.x + topPipeBounds.width && birdBounds.x + birdBounds.width > topPipeBounds.x && birdBounds.y < topPipeBounds.y + topPipeBounds.height && birdBounds.y + birdBounds.height > topPipeBounds.y) {
LK.getSound('hit').play();
LK.effects.flashScreen(0xff0000, 500);
LK.showGameOver();
return;
}
if (birdBounds.x < bottomPipeBounds.x + bottomPipeBounds.width && birdBounds.x + birdBounds.width > bottomPipeBounds.x && birdBounds.y < bottomPipeBounds.y + bottomPipeBounds.height && birdBounds.y + birdBounds.height > bottomPipeBounds.y) {
LK.getSound('hit').play();
LK.effects.flashScreen(0xff0000, 500);
LK.showGameOver();
return;
}
pipe.lastX = pipe.x;
}
// Check ground collision
if (bird.y + 20 >= ground.y) {
LK.getSound('hit').play();
LK.effects.flashScreen(0xff0000, 500);
LK.showGameOver();
return;
}
// Check ceiling collision
if (bird.y - 20 <= 0) {
LK.getSound('hit').play();
LK.effects.flashScreen(0xff0000, 500);
LK.showGameOver();
return;
}
}; /****
* 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.8;
self.flapPower = -12;
self.maxVelocity = 15;
self.flap = function () {
self.velocity = self.flapPower;
LK.getSound('flap').play();
// Flap animation
tween(birdGraphics, {
rotation: -0.3
}, {
duration: 100
});
tween(birdGraphics, {
rotation: 0.3
}, {
duration: 200,
onFinish: function onFinish() {
tween(birdGraphics, {
rotation: 0
}, {
duration: 100
});
}
});
};
self.update = function () {
self.velocity += self.gravity;
if (self.velocity > self.maxVelocity) {
self.velocity = self.maxVelocity;
}
self.y += self.velocity;
// Rotate bird based on velocity
var targetRotation = Math.min(Math.max(self.velocity * 0.1, -0.5), 0.8);
birdGraphics.rotation = targetRotation;
};
return self;
});
var Pipe = Container.expand(function () {
var self = Container.call(this);
self.gapSize = 300;
self.speed = -4;
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.setGapPosition = function (gapY) {
self.topPipe.y = gapY - self.gapSize / 2;
self.bottomPipe.y = gapY + self.gapSize / 2;
// Make bottom pipe extend to ground - position it properly and scale it
var groundY = 2732 - 100; // Ground position
var bottomPipeTop = gapY + self.gapSize / 2;
var distanceToGround = groundY - bottomPipeTop;
// Position bottom pipe at gap bottom and scale to reach ground
self.bottomPipe.y = bottomPipeTop;
self.bottomPipe.scaleY = distanceToGround / 800; // Scale to reach ground
self.bottomPipe.anchorY = 0; // Anchor at top so it grows downward
};
self.update = function () {
self.x += self.speed;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
var bird;
var pipes = [];
var ground;
var scoreTxt;
var gameStarted = false;
var pipeSpawnTimer = 0;
var pipeDistance = 400;
// Create bird
bird = game.addChild(new Bird());
bird.x = 300;
bird.y = 1366; // Center vertically
// Create ground
ground = game.addChild(LK.getAsset('ground', {
anchorX: 0,
anchorY: 0
}));
ground.x = 0;
ground.y = 2732 - 100;
// Create score display
scoreTxt = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
scoreTxt.y = 150;
// Create best score display
var bestScoreTxt = new Text2('Best: ' + (storage.bestScore || 0), {
size: 60,
fill: 0xFFD700
});
bestScoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(bestScoreTxt);
bestScoreTxt.y = 250;
// Custom game over screen variables
var gameOverScreen = null;
// Game input handler
game.down = function (x, y, obj) {
if (!gameStarted) {
gameStarted = true;
}
bird.flap();
};
// Main game loop
game.update = function () {
if (!gameStarted) return;
// Spawn pipes
pipeSpawnTimer++;
if (pipeSpawnTimer >= pipeDistance / 4) {
var newPipe = new Pipe();
newPipe.x = 2048 + 60;
// Random gap position between 300 and 2000
var gapY = 300 + Math.random() * (2000 - 300);
newPipe.setGapPosition(gapY);
pipes.push(newPipe);
game.addChild(newPipe);
pipeSpawnTimer = 0;
}
// Update pipes and check collisions
for (var i = pipes.length - 1; i >= 0; i--) {
var pipe = pipes[i];
// Track last position for scoring
if (pipe.lastX === undefined) pipe.lastX = pipe.x;
// Check for scoring (bird passes pipe)
if (!pipe.scored && pipe.lastX > bird.x && pipe.x <= bird.x) {
pipe.scored = true;
LK.setScore(LK.getScore() + 1);
var currentScore = LK.getScore();
scoreTxt.setText('Score: ' + currentScore);
// Update best score if current score is higher
var bestScore = storage.bestScore || 0;
if (currentScore > bestScore) {
storage.bestScore = currentScore;
bestScoreTxt.setText('Best: ' + currentScore);
}
LK.getSound('score').play();
}
// Remove off-screen pipes
if (pipe.x < -120) {
pipe.destroy();
pipes.splice(i, 1);
continue;
}
// Collision detection
var birdBounds = {
x: bird.x - 30,
y: bird.y - 20,
width: 60,
height: 40
};
var topPipeBounds = {
x: pipe.x - 60,
y: 0,
width: 120,
height: pipe.topPipe.y
};
var bottomPipeBounds = {
x: pipe.x - 60,
y: pipe.bottomPipe.y,
width: 120,
height: 2732 - 100 - pipe.bottomPipe.y // Height from pipe start to ground
};
// Check collision with pipes
if (birdBounds.x < topPipeBounds.x + topPipeBounds.width && birdBounds.x + birdBounds.width > topPipeBounds.x && birdBounds.y < topPipeBounds.y + topPipeBounds.height && birdBounds.y + birdBounds.height > topPipeBounds.y) {
LK.getSound('hit').play();
LK.effects.flashScreen(0xff0000, 500);
LK.showGameOver();
return;
}
if (birdBounds.x < bottomPipeBounds.x + bottomPipeBounds.width && birdBounds.x + birdBounds.width > bottomPipeBounds.x && birdBounds.y < bottomPipeBounds.y + bottomPipeBounds.height && birdBounds.y + birdBounds.height > bottomPipeBounds.y) {
LK.getSound('hit').play();
LK.effects.flashScreen(0xff0000, 500);
LK.showGameOver();
return;
}
pipe.lastX = pipe.x;
}
// Check ground collision
if (bird.y + 20 >= ground.y) {
LK.getSound('hit').play();
LK.effects.flashScreen(0xff0000, 500);
LK.showGameOver();
return;
}
// Check ceiling collision
if (bird.y - 20 <= 0) {
LK.getSound('hit').play();
LK.effects.flashScreen(0xff0000, 500);
LK.showGameOver();
return;
}
};