/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Coin class var Coin = Container.expand(function () { var self = Container.call(this); var coinSprite = self.attachAsset('coin', { anchorX: 0.5, anchorY: 0.5 }); self.radius = coinSprite.width / 2; self.collected = false; // For collision detection self.getBounds = function () { return { x: self.x - self.radius, y: self.y - self.radius, width: self.radius * 2, height: self.radius * 2 }; }; // Animate coin (optional: simple rotation) self.update = function () { if (coinSprite) coinSprite.rotation += 0.13; }; return self; }); // PipePair class var PipePair = Container.expand(function () { var self = Container.call(this); // Top pipe var topPipe = self.attachAsset('pipe', { anchorX: 0, anchorY: 1 }); // Bottom pipe var bottomPipe = self.attachAsset('pipe', { anchorX: 0, anchorY: 0 }); self.topPipe = topPipe; self.bottomPipe = bottomPipe; // Add coin in the gap var coin = new Coin(); self.coin = coin; self.addChild(coin); // Set pipes' positions based on gapY self.setGap = function (gapY) { // gapY is the vertical center of the gap var gapTop = gapY - gapHeight / 2; var gapBottom = gapY + gapHeight / 2; topPipe.x = 0; topPipe.y = gapTop; topPipe.height = gapTop; bottomPipe.x = 0; bottomPipe.y = gapBottom; bottomPipe.height = 2732 - gapBottom - groundHeight; // Place coin in the center of the gap coin.x = pipeWidth / 2; coin.y = gapY; coin.collected = false; coin.visible = true; }; // For collision detection self.getTopBounds = function () { return { x: self.x, y: 0, width: topPipe.width, height: topPipe.y }; }; self.getBottomBounds = function () { return { x: self.x, y: bottomPipe.y, width: bottomPipe.width, height: bottomPipe.height }; }; // Used to check if player has passed this pipe self.passed = false; return self; }); // Player class var Player = Container.expand(function () { var self = Container.call(this); var playerSprite = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); self.radius = playerSprite.width / 2; self.vy = 0; // vertical velocity // For collision detection self.getBounds = function () { return { x: self.x - self.radius, y: self.y - self.radius, width: self.radius * 2, height: self.radius * 2 }; }; // Update position and apply gravity self.update = function () { self.vy += gravity; self.y += self.vy; // Smooth rotation for gliding effect // Clamp rotation between -0.5 (up) and 1.2 (down) var targetRotation = Math.max(-0.5, Math.min(1.2, self.vy / 40)); if (typeof self.rotation === "undefined") self.rotation = 0; // Smoothly interpolate rotation for a natural look self.rotation += (targetRotation - self.rotation) * 0.18; // Apply rotation to sprite if (playerSprite) playerSprite.rotation = self.rotation; // Clamp to top of screen if (self.y - self.radius < 0) { self.y = self.radius; self.vy = 0; } }; // Jump self.flap = function () { self.vy = jumpVelocity; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87ceeb // Sky blue }); /**** * Game Code ****/ // Ground // Pipe (green) // Character (bird/ball) // Constants var gravity = 1.1; // Reduced gravity for slower fall var jumpVelocity = -32; // Slightly less strong jump for smoother arc var pipeSpeed = 16; var pipeInterval = 90; // frames between pipes var gapHeight = 820; var groundHeight = 120; var pipeWidth = 220; // Game state var player; var pipes = []; var coins = []; var ground; var score = 0; var scoreTxt; var lastPipeTick = 0; var gameStarted = false; var gameOver = false; // Create ground ground = LK.getAsset('ground', { anchorX: 0, anchorY: 0, x: 0, y: 2732 - groundHeight }); game.addChild(ground); // Create player player = new Player(); player.x = 420; player.y = 2732 / 2; game.addChild(player); // Score text scoreTxt = new Text2('0', { size: 180, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Reset function function resetGame() { // Remove pipes for (var i = pipes.length - 1; i >= 0; i--) { pipes[i].destroy(); pipes.splice(i, 1); } coins = []; // Reset player player.x = 420; player.y = 2732 / 2; player.vy = 0; // Reset score score = 0; scoreTxt.setText(score); lastPipeTick = LK.ticks; gameStarted = false; gameOver = false; } // Start game on first tap game.down = function (x, y, obj) { if (gameOver) return; player.flap(); if (!gameStarted) { gameStarted = true; } }; // Main update loop game.update = function () { if (gameOver) return; if (gameStarted) { player.update(); // Move pipes and check for collisions for (var i = pipes.length - 1; i >= 0; i--) { var pipePair = pipes[i]; pipePair.x -= pipeSpeed; // Move coin with pipe if (pipePair.coin) { pipePair.coin.x = pipeWidth / 2; pipePair.coin.update && pipePair.coin.update(); } // Remove pipes that have gone off screen if (pipePair.x + pipeWidth < 0) { if (pipePair.coin && coins.indexOf(pipePair.coin) !== -1) { coins.splice(coins.indexOf(pipePair.coin), 1); } pipePair.destroy(); pipes.splice(i, 1); continue; } // Check for collision with player var pBounds = player.getBounds(); var tBounds = pipePair.getTopBounds(); var bBounds = pipePair.getBottomBounds(); if (rectsIntersect(pBounds, tBounds) || rectsIntersect(pBounds, bBounds)) { endGame(); return; } // Check if player passed the pipe for scoring if (!pipePair.passed && pipePair.x + pipeWidth < player.x - player.radius) { pipePair.passed = true; score += 1; scoreTxt.setText(score); // Hide the coin if it exists and is visible (not collected) if (pipePair.coin && !pipePair.coin.collected && pipePair.coin.visible) { pipePair.coin.visible = false; if (pipePair.coin.parent) { pipePair.coin.parent.removeChild(pipePair.coin); } var coinIdx = coins.indexOf(pipePair.coin); if (coinIdx !== -1) { coins.splice(coinIdx, 1); } } } // Coin collection if (pipePair.coin && !pipePair.coin.collected && pipePair.coin.visible) { var coinBounds = pipePair.coin.getBounds(); if (rectsIntersect(pBounds, coinBounds)) { pipePair.coin.collected = true; pipePair.coin.visible = false; // Remove coin from display if (pipePair.coin.parent) { pipePair.coin.parent.removeChild(pipePair.coin); } // Remove from coins array if present var coinIdx = coins.indexOf(pipePair.coin); if (coinIdx !== -1) { coins.splice(coinIdx, 1); } score += 1; scoreTxt.setText(score); } } } // Add new pipes if (LK.ticks - lastPipeTick >= pipeInterval) { var gapY = getRandomGapY(); var pipePair = new PipePair(); pipePair.x = 2048; pipePair.setGap(gapY); pipes.push(pipePair); if (pipePair.coin) { coins.push(pipePair.coin); } game.addChild(pipePair); lastPipeTick = LK.ticks; } // Check collision with ground if (player.y + player.radius >= 2732 - groundHeight) { player.y = 2732 - groundHeight - player.radius; endGame(); return; } } }; // End game function endGame() { gameOver = true; LK.effects.flashScreen(0xff0000, 600); LK.showGameOver(); } // Utility: Rectangle intersection function rectsIntersect(a, b) { return a.x < b.x + b.width && a.x + a.width > b.x && a.y < b.y + b.height && a.y + a.height > b.y; } // Utility: Random gap Y position (avoid too close to top/bottom) function getRandomGapY() { var minY = gapHeight / 2 + 120; var maxY = 2732 - groundHeight - gapHeight / 2 - 120; return minY + Math.floor(Math.random() * (maxY - minY)); } // Reset game on game over LK.on('gameover', function () { resetGame(); }); // Initial reset resetGame();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Coin class
var Coin = Container.expand(function () {
var self = Container.call(this);
var coinSprite = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = coinSprite.width / 2;
self.collected = false;
// For collision detection
self.getBounds = function () {
return {
x: self.x - self.radius,
y: self.y - self.radius,
width: self.radius * 2,
height: self.radius * 2
};
};
// Animate coin (optional: simple rotation)
self.update = function () {
if (coinSprite) coinSprite.rotation += 0.13;
};
return self;
});
// PipePair class
var PipePair = Container.expand(function () {
var self = Container.call(this);
// Top pipe
var topPipe = self.attachAsset('pipe', {
anchorX: 0,
anchorY: 1
});
// Bottom pipe
var bottomPipe = self.attachAsset('pipe', {
anchorX: 0,
anchorY: 0
});
self.topPipe = topPipe;
self.bottomPipe = bottomPipe;
// Add coin in the gap
var coin = new Coin();
self.coin = coin;
self.addChild(coin);
// Set pipes' positions based on gapY
self.setGap = function (gapY) {
// gapY is the vertical center of the gap
var gapTop = gapY - gapHeight / 2;
var gapBottom = gapY + gapHeight / 2;
topPipe.x = 0;
topPipe.y = gapTop;
topPipe.height = gapTop;
bottomPipe.x = 0;
bottomPipe.y = gapBottom;
bottomPipe.height = 2732 - gapBottom - groundHeight;
// Place coin in the center of the gap
coin.x = pipeWidth / 2;
coin.y = gapY;
coin.collected = false;
coin.visible = true;
};
// For collision detection
self.getTopBounds = function () {
return {
x: self.x,
y: 0,
width: topPipe.width,
height: topPipe.y
};
};
self.getBottomBounds = function () {
return {
x: self.x,
y: bottomPipe.y,
width: bottomPipe.width,
height: bottomPipe.height
};
};
// Used to check if player has passed this pipe
self.passed = false;
return self;
});
// Player class
var Player = Container.expand(function () {
var self = Container.call(this);
var playerSprite = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = playerSprite.width / 2;
self.vy = 0; // vertical velocity
// For collision detection
self.getBounds = function () {
return {
x: self.x - self.radius,
y: self.y - self.radius,
width: self.radius * 2,
height: self.radius * 2
};
};
// Update position and apply gravity
self.update = function () {
self.vy += gravity;
self.y += self.vy;
// Smooth rotation for gliding effect
// Clamp rotation between -0.5 (up) and 1.2 (down)
var targetRotation = Math.max(-0.5, Math.min(1.2, self.vy / 40));
if (typeof self.rotation === "undefined") self.rotation = 0;
// Smoothly interpolate rotation for a natural look
self.rotation += (targetRotation - self.rotation) * 0.18;
// Apply rotation to sprite
if (playerSprite) playerSprite.rotation = self.rotation;
// Clamp to top of screen
if (self.y - self.radius < 0) {
self.y = self.radius;
self.vy = 0;
}
};
// Jump
self.flap = function () {
self.vy = jumpVelocity;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87ceeb // Sky blue
});
/****
* Game Code
****/
// Ground
// Pipe (green)
// Character (bird/ball)
// Constants
var gravity = 1.1; // Reduced gravity for slower fall
var jumpVelocity = -32; // Slightly less strong jump for smoother arc
var pipeSpeed = 16;
var pipeInterval = 90; // frames between pipes
var gapHeight = 820;
var groundHeight = 120;
var pipeWidth = 220;
// Game state
var player;
var pipes = [];
var coins = [];
var ground;
var score = 0;
var scoreTxt;
var lastPipeTick = 0;
var gameStarted = false;
var gameOver = false;
// Create ground
ground = LK.getAsset('ground', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 2732 - groundHeight
});
game.addChild(ground);
// Create player
player = new Player();
player.x = 420;
player.y = 2732 / 2;
game.addChild(player);
// Score text
scoreTxt = new Text2('0', {
size: 180,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Reset function
function resetGame() {
// Remove pipes
for (var i = pipes.length - 1; i >= 0; i--) {
pipes[i].destroy();
pipes.splice(i, 1);
}
coins = [];
// Reset player
player.x = 420;
player.y = 2732 / 2;
player.vy = 0;
// Reset score
score = 0;
scoreTxt.setText(score);
lastPipeTick = LK.ticks;
gameStarted = false;
gameOver = false;
}
// Start game on first tap
game.down = function (x, y, obj) {
if (gameOver) return;
player.flap();
if (!gameStarted) {
gameStarted = true;
}
};
// Main update loop
game.update = function () {
if (gameOver) return;
if (gameStarted) {
player.update();
// Move pipes and check for collisions
for (var i = pipes.length - 1; i >= 0; i--) {
var pipePair = pipes[i];
pipePair.x -= pipeSpeed;
// Move coin with pipe
if (pipePair.coin) {
pipePair.coin.x = pipeWidth / 2;
pipePair.coin.update && pipePair.coin.update();
}
// Remove pipes that have gone off screen
if (pipePair.x + pipeWidth < 0) {
if (pipePair.coin && coins.indexOf(pipePair.coin) !== -1) {
coins.splice(coins.indexOf(pipePair.coin), 1);
}
pipePair.destroy();
pipes.splice(i, 1);
continue;
}
// Check for collision with player
var pBounds = player.getBounds();
var tBounds = pipePair.getTopBounds();
var bBounds = pipePair.getBottomBounds();
if (rectsIntersect(pBounds, tBounds) || rectsIntersect(pBounds, bBounds)) {
endGame();
return;
}
// Check if player passed the pipe for scoring
if (!pipePair.passed && pipePair.x + pipeWidth < player.x - player.radius) {
pipePair.passed = true;
score += 1;
scoreTxt.setText(score);
// Hide the coin if it exists and is visible (not collected)
if (pipePair.coin && !pipePair.coin.collected && pipePair.coin.visible) {
pipePair.coin.visible = false;
if (pipePair.coin.parent) {
pipePair.coin.parent.removeChild(pipePair.coin);
}
var coinIdx = coins.indexOf(pipePair.coin);
if (coinIdx !== -1) {
coins.splice(coinIdx, 1);
}
}
}
// Coin collection
if (pipePair.coin && !pipePair.coin.collected && pipePair.coin.visible) {
var coinBounds = pipePair.coin.getBounds();
if (rectsIntersect(pBounds, coinBounds)) {
pipePair.coin.collected = true;
pipePair.coin.visible = false;
// Remove coin from display
if (pipePair.coin.parent) {
pipePair.coin.parent.removeChild(pipePair.coin);
}
// Remove from coins array if present
var coinIdx = coins.indexOf(pipePair.coin);
if (coinIdx !== -1) {
coins.splice(coinIdx, 1);
}
score += 1;
scoreTxt.setText(score);
}
}
}
// Add new pipes
if (LK.ticks - lastPipeTick >= pipeInterval) {
var gapY = getRandomGapY();
var pipePair = new PipePair();
pipePair.x = 2048;
pipePair.setGap(gapY);
pipes.push(pipePair);
if (pipePair.coin) {
coins.push(pipePair.coin);
}
game.addChild(pipePair);
lastPipeTick = LK.ticks;
}
// Check collision with ground
if (player.y + player.radius >= 2732 - groundHeight) {
player.y = 2732 - groundHeight - player.radius;
endGame();
return;
}
}
};
// End game
function endGame() {
gameOver = true;
LK.effects.flashScreen(0xff0000, 600);
LK.showGameOver();
}
// Utility: Rectangle intersection
function rectsIntersect(a, b) {
return a.x < b.x + b.width && a.x + a.width > b.x && a.y < b.y + b.height && a.y + a.height > b.y;
}
// Utility: Random gap Y position (avoid too close to top/bottom)
function getRandomGapY() {
var minY = gapHeight / 2 + 120;
var maxY = 2732 - groundHeight - gapHeight / 2 - 120;
return minY + Math.floor(Math.random() * (maxY - minY));
}
// Reset game on game over
LK.on('gameover', function () {
resetGame();
});
// Initial reset
resetGame();