/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ // Chicken class var Chicken = Container.expand(function () { var self = Container.call(this); var chickenSprite = self.attachAsset('chicken', { anchorX: 0.5, anchorY: 0.5 }); self.radius = chickenSprite.width * 0.5; self.vy = 0; self.gravity = 2.2; self.flapStrength = -38; self.maxFall = 38; self.alive = true; // Flap upward self.flap = function () { if (!self.alive) return; self.vy = self.flapStrength; LK.getSound('flap').play(); // Animate chicken (slight scale for feedback) tween.stop(self, { scaleY: true }); self.scaleY = 1.0; tween(self, { scaleY: 0.8 }, { duration: 80, easing: tween.cubicOut, onFinish: function onFinish() { tween(self, { scaleY: 1.0 }, { duration: 120, easing: tween.cubicIn }); } }); }; // Update position self.update = function () { if (!self.alive) return; self.vy += self.gravity; if (self.vy > self.maxFall) self.vy = self.maxFall; self.y += self.vy; // Rotate chicken based on velocity var maxAngle = Math.PI / 3; var minAngle = -Math.PI / 6; var t = (self.vy + 38) / (self.maxFall + 38); chickenSprite.rotation = minAngle + (maxAngle - minAngle) * t; }; // Die self.die = function () { self.alive = false; // Fall fast self.vy = 18; // Flash red LK.effects.flashObject(self, 0xff0000, 400); }; return self; }); // PipePair class (top and bottom pipes) var PipePair = Container.expand(function () { var self = Container.call(this); // Top pipe var topPipe = self.attachAsset('pipe', { anchorX: 0.5, anchorY: 1.0 }); var topCap = self.attachAsset('pipecap', { anchorX: 0.5, anchorY: 1.0, y: -topPipe.height }); // Bottom pipe var bottomPipe = self.attachAsset('pipe', { anchorX: 0.5, anchorY: 0.0, y: 0 }); var bottomCap = self.attachAsset('pipecap', { anchorX: 0.5, anchorY: 0.0, y: bottomPipe.height }); // For collision self.topPipe = topPipe; self.bottomPipe = bottomPipe; // Has the chicken passed this pipe? self.passed = false; // Set gap and position self.setGap = function (gapY, gapHeight) { // Top pipe: bottom at gapY - gapHeight/2 topPipe.height = Math.max(120, gapY - gapHeight / 2); topCap.y = -topPipe.height; // Bottom pipe: top at gapY + gapHeight/2 bottomPipe.y = gapY + gapHeight / 2; bottomPipe.height = Math.max(120, 2732 - bottomPipe.y - 120); bottomCap.y = bottomPipe.y + bottomPipe.height; }; // Move pipes left self.update = function () { self.x -= pipeSpeed; }; // Destroy self.destroyPair = function () { self.destroy(); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87ceeb // Sky blue }); /**** * Game Code ****/ // Sound for hit // Sound for point // Sound for flap // Ground // Obstacle (pipe cap) // Obstacle (pipe) // Chicken (player) // Game constants var GROUND_HEIGHT = 120; var CHICKEN_START_X = 600; var CHICKEN_START_Y = 1200; var PIPE_INTERVAL = 1100; // px between pipes var PIPE_MIN_GAP = 340; var PIPE_MAX_GAP = 520; var PIPE_MIN_Y = 420; var PIPE_MAX_Y = 2732 - GROUND_HEIGHT - 420; var pipeSpeed = 16; var pipePairs = []; var score = 0; var bestScore = storage.flyingChickenBestScore || 0; var gameStarted = false; var gameOver = false; var dragNode = null; var lastPassedPipe = null; // Score text var scoreTxt = new Text2('0', { size: 180, fill: 0xFFF700 }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Best score text (bottom center) var bestScoreTxt = new Text2('', { size: 70, fill: 0xFFFFFF }); bestScoreTxt.anchor.set(0.5, 1); LK.gui.bottom.addChild(bestScoreTxt); // Ground var ground = LK.getAsset('ground', { anchorX: 0, anchorY: 0, x: 0, y: 2732 - GROUND_HEIGHT }); game.addChild(ground); // Chicken var chicken = new Chicken(); game.addChild(chicken); chicken.x = CHICKEN_START_X; chicken.y = CHICKEN_START_Y; // Helper: reset game state function resetGame() { // Remove pipes for (var i = 0; i < pipePairs.length; i++) { pipePairs[i].destroyPair(); } pipePairs = []; score = 0; scoreTxt.setText('0'); chicken.x = CHICKEN_START_X; chicken.y = CHICKEN_START_Y; chicken.vy = 0; chicken.alive = true; chicken.rotation = 0; gameStarted = false; gameOver = false; pipeSpeed = 16; lastPassedPipe = null; // Show best score if (bestScore > 0) { bestScoreTxt.setText('Best: ' + bestScore); } else { bestScoreTxt.setText(''); } // Start 8-bit background music LK.playMusic('8-bit'); } // Helper: spawn a new pipe pair function spawnPipePair() { // Gap size decreases as score increases var gapHeight = Math.max(PIPE_MIN_GAP, PIPE_MAX_GAP - score * 8); // Gap Y position var gapY = PIPE_MIN_Y + Math.random() * (PIPE_MAX_Y - PIPE_MIN_Y); var pair = new PipePair(); pair.x = 2048 + 200; pair.setGap(gapY, gapHeight); game.addChild(pair); pipePairs.push(pair); } // Helper: check collision between chicken and a pipe function checkPipeCollision(chicken, pipePair) { // Chicken bounding circle var cx = chicken.x; var cy = chicken.y; var r = chicken.radius * 0.92; // Top pipe rect var tp = pipePair.topPipe; var tpx = pipePair.x; var tpy = tp.y + pipePair.y; var tpw = tp.width; var tph = tp.height; // Bottom pipe rect var bp = pipePair.bottomPipe; var bpx = pipePair.x; var bpy = bp.y + pipePair.y; var bpw = bp.width; var bph = bp.height; // Circle-rect collision function circleRectCollide(cx, cy, r, rx, ry, rw, rh) { var closestX = Math.max(rx - rw / 2, Math.min(cx, rx + rw / 2)); var closestY = Math.max(ry, Math.min(cy, ry + rh)); var dx = cx - closestX; var dy = cy - closestY; return dx * dx + dy * dy < r * r; } // Top pipe if (circleRectCollide(cx, cy, r, tpx, tpy - tph, tpw, tph)) return true; // Bottom pipe if (circleRectCollide(cx, cy, r, bpx, bpy, bpw, bph)) return true; return false; } // Helper: check collision with ground or ceiling function checkGroundCeiling(chicken) { if (chicken.y + chicken.radius > 2732 - GROUND_HEIGHT) return true; if (chicken.y - chicken.radius < 0) return true; return false; } // Start game on first tap function startGame() { if (gameStarted) return; resetGame(); gameStarted = true; // Spawn initial pipes for (var i = 0; i < 3; i++) { var pair = new PipePair(); pair.x = 2048 + 600 + i * PIPE_INTERVAL; var gapHeight = Math.max(PIPE_MIN_GAP, PIPE_MAX_GAP - score * 8); var gapY = PIPE_MIN_Y + Math.random() * (PIPE_MAX_Y - PIPE_MIN_Y); pair.setGap(gapY, gapHeight); game.addChild(pair); pipePairs.push(pair); } } // Game tap: chicken flaps game.down = function (x, y, obj) { if (gameOver) return; if (!gameStarted) { startGame(); } chicken.flap(); dragNode = chicken; }; // Release: nothing (but required for dragNode) game.up = function (x, y, obj) { dragNode = null; }; // Move: not used for chicken, but required for dragNode game.move = function (x, y, obj) { // No drag for chicken, but required for completeness }; // Main game loop game.update = function () { if (!gameStarted) return; // Chicken update chicken.update(); // Pipes update for (var i = pipePairs.length - 1; i >= 0; i--) { var pair = pipePairs[i]; pair.update(); // Remove pipes off screen if (pair.x < -300) { pair.destroyPair(); pipePairs.splice(i, 1); continue; } // Check for passing pipe (score) if (!pair.passed && pair.x + 110 < chicken.x) { pair.passed = true; score += 1; scoreTxt.setText(score + ''); LK.getSound('point').play(); // Increase speed every 10 points if (score % 10 === 0) { pipeSpeed += 1.5; } } } // Spawn new pipes if (pipePairs.length === 0 || pipePairs[pipePairs.length - 1].x < 2048 - PIPE_INTERVAL) { spawnPipePair(); } // Collision detection var collided = false; for (var i = 0; i < pipePairs.length; i++) { if (checkPipeCollision(chicken, pipePairs[i])) { collided = true; break; } } if (checkGroundCeiling(chicken)) { collided = true; } if (collided && chicken.alive) { chicken.die(); LK.getSound('hit').play(); gameOver = true; // Stop music on game over LK.stopMusic(); // Update best score if (score > bestScore) { bestScore = score; storage.flyingChickenBestScore = bestScore; } // Flash screen LK.effects.flashScreen(0xff0000, 800); // Show game over (auto resets) LK.setTimeout(function () { // Show best score in the bestScoreTxt (bottom center) before game over popup if (bestScore > 0) { bestScoreTxt.setText('Best: ' + bestScore); } else { bestScoreTxt.setText(''); } LK.showGameOver(); }, 600); } }; // On game over, reset state LK.on('gameover', function () { resetGame(); }); // On you win (not used, but for completeness) LK.on('youwin', function () { resetGame(); }); // Initial state resetGame();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// Chicken class
var Chicken = Container.expand(function () {
var self = Container.call(this);
var chickenSprite = self.attachAsset('chicken', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = chickenSprite.width * 0.5;
self.vy = 0;
self.gravity = 2.2;
self.flapStrength = -38;
self.maxFall = 38;
self.alive = true;
// Flap upward
self.flap = function () {
if (!self.alive) return;
self.vy = self.flapStrength;
LK.getSound('flap').play();
// Animate chicken (slight scale for feedback)
tween.stop(self, {
scaleY: true
});
self.scaleY = 1.0;
tween(self, {
scaleY: 0.8
}, {
duration: 80,
easing: tween.cubicOut,
onFinish: function onFinish() {
tween(self, {
scaleY: 1.0
}, {
duration: 120,
easing: tween.cubicIn
});
}
});
};
// Update position
self.update = function () {
if (!self.alive) return;
self.vy += self.gravity;
if (self.vy > self.maxFall) self.vy = self.maxFall;
self.y += self.vy;
// Rotate chicken based on velocity
var maxAngle = Math.PI / 3;
var minAngle = -Math.PI / 6;
var t = (self.vy + 38) / (self.maxFall + 38);
chickenSprite.rotation = minAngle + (maxAngle - minAngle) * t;
};
// Die
self.die = function () {
self.alive = false;
// Fall fast
self.vy = 18;
// Flash red
LK.effects.flashObject(self, 0xff0000, 400);
};
return self;
});
// PipePair class (top and bottom pipes)
var PipePair = Container.expand(function () {
var self = Container.call(this);
// Top pipe
var topPipe = self.attachAsset('pipe', {
anchorX: 0.5,
anchorY: 1.0
});
var topCap = self.attachAsset('pipecap', {
anchorX: 0.5,
anchorY: 1.0,
y: -topPipe.height
});
// Bottom pipe
var bottomPipe = self.attachAsset('pipe', {
anchorX: 0.5,
anchorY: 0.0,
y: 0
});
var bottomCap = self.attachAsset('pipecap', {
anchorX: 0.5,
anchorY: 0.0,
y: bottomPipe.height
});
// For collision
self.topPipe = topPipe;
self.bottomPipe = bottomPipe;
// Has the chicken passed this pipe?
self.passed = false;
// Set gap and position
self.setGap = function (gapY, gapHeight) {
// Top pipe: bottom at gapY - gapHeight/2
topPipe.height = Math.max(120, gapY - gapHeight / 2);
topCap.y = -topPipe.height;
// Bottom pipe: top at gapY + gapHeight/2
bottomPipe.y = gapY + gapHeight / 2;
bottomPipe.height = Math.max(120, 2732 - bottomPipe.y - 120);
bottomCap.y = bottomPipe.y + bottomPipe.height;
};
// Move pipes left
self.update = function () {
self.x -= pipeSpeed;
};
// Destroy
self.destroyPair = function () {
self.destroy();
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87ceeb // Sky blue
});
/****
* Game Code
****/
// Sound for hit
// Sound for point
// Sound for flap
// Ground
// Obstacle (pipe cap)
// Obstacle (pipe)
// Chicken (player)
// Game constants
var GROUND_HEIGHT = 120;
var CHICKEN_START_X = 600;
var CHICKEN_START_Y = 1200;
var PIPE_INTERVAL = 1100; // px between pipes
var PIPE_MIN_GAP = 340;
var PIPE_MAX_GAP = 520;
var PIPE_MIN_Y = 420;
var PIPE_MAX_Y = 2732 - GROUND_HEIGHT - 420;
var pipeSpeed = 16;
var pipePairs = [];
var score = 0;
var bestScore = storage.flyingChickenBestScore || 0;
var gameStarted = false;
var gameOver = false;
var dragNode = null;
var lastPassedPipe = null;
// Score text
var scoreTxt = new Text2('0', {
size: 180,
fill: 0xFFF700
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Best score text (bottom center)
var bestScoreTxt = new Text2('', {
size: 70,
fill: 0xFFFFFF
});
bestScoreTxt.anchor.set(0.5, 1);
LK.gui.bottom.addChild(bestScoreTxt);
// Ground
var ground = LK.getAsset('ground', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 2732 - GROUND_HEIGHT
});
game.addChild(ground);
// Chicken
var chicken = new Chicken();
game.addChild(chicken);
chicken.x = CHICKEN_START_X;
chicken.y = CHICKEN_START_Y;
// Helper: reset game state
function resetGame() {
// Remove pipes
for (var i = 0; i < pipePairs.length; i++) {
pipePairs[i].destroyPair();
}
pipePairs = [];
score = 0;
scoreTxt.setText('0');
chicken.x = CHICKEN_START_X;
chicken.y = CHICKEN_START_Y;
chicken.vy = 0;
chicken.alive = true;
chicken.rotation = 0;
gameStarted = false;
gameOver = false;
pipeSpeed = 16;
lastPassedPipe = null;
// Show best score
if (bestScore > 0) {
bestScoreTxt.setText('Best: ' + bestScore);
} else {
bestScoreTxt.setText('');
}
// Start 8-bit background music
LK.playMusic('8-bit');
}
// Helper: spawn a new pipe pair
function spawnPipePair() {
// Gap size decreases as score increases
var gapHeight = Math.max(PIPE_MIN_GAP, PIPE_MAX_GAP - score * 8);
// Gap Y position
var gapY = PIPE_MIN_Y + Math.random() * (PIPE_MAX_Y - PIPE_MIN_Y);
var pair = new PipePair();
pair.x = 2048 + 200;
pair.setGap(gapY, gapHeight);
game.addChild(pair);
pipePairs.push(pair);
}
// Helper: check collision between chicken and a pipe
function checkPipeCollision(chicken, pipePair) {
// Chicken bounding circle
var cx = chicken.x;
var cy = chicken.y;
var r = chicken.radius * 0.92;
// Top pipe rect
var tp = pipePair.topPipe;
var tpx = pipePair.x;
var tpy = tp.y + pipePair.y;
var tpw = tp.width;
var tph = tp.height;
// Bottom pipe rect
var bp = pipePair.bottomPipe;
var bpx = pipePair.x;
var bpy = bp.y + pipePair.y;
var bpw = bp.width;
var bph = bp.height;
// Circle-rect collision
function circleRectCollide(cx, cy, r, rx, ry, rw, rh) {
var closestX = Math.max(rx - rw / 2, Math.min(cx, rx + rw / 2));
var closestY = Math.max(ry, Math.min(cy, ry + rh));
var dx = cx - closestX;
var dy = cy - closestY;
return dx * dx + dy * dy < r * r;
}
// Top pipe
if (circleRectCollide(cx, cy, r, tpx, tpy - tph, tpw, tph)) return true;
// Bottom pipe
if (circleRectCollide(cx, cy, r, bpx, bpy, bpw, bph)) return true;
return false;
}
// Helper: check collision with ground or ceiling
function checkGroundCeiling(chicken) {
if (chicken.y + chicken.radius > 2732 - GROUND_HEIGHT) return true;
if (chicken.y - chicken.radius < 0) return true;
return false;
}
// Start game on first tap
function startGame() {
if (gameStarted) return;
resetGame();
gameStarted = true;
// Spawn initial pipes
for (var i = 0; i < 3; i++) {
var pair = new PipePair();
pair.x = 2048 + 600 + i * PIPE_INTERVAL;
var gapHeight = Math.max(PIPE_MIN_GAP, PIPE_MAX_GAP - score * 8);
var gapY = PIPE_MIN_Y + Math.random() * (PIPE_MAX_Y - PIPE_MIN_Y);
pair.setGap(gapY, gapHeight);
game.addChild(pair);
pipePairs.push(pair);
}
}
// Game tap: chicken flaps
game.down = function (x, y, obj) {
if (gameOver) return;
if (!gameStarted) {
startGame();
}
chicken.flap();
dragNode = chicken;
};
// Release: nothing (but required for dragNode)
game.up = function (x, y, obj) {
dragNode = null;
};
// Move: not used for chicken, but required for dragNode
game.move = function (x, y, obj) {
// No drag for chicken, but required for completeness
};
// Main game loop
game.update = function () {
if (!gameStarted) return;
// Chicken update
chicken.update();
// Pipes update
for (var i = pipePairs.length - 1; i >= 0; i--) {
var pair = pipePairs[i];
pair.update();
// Remove pipes off screen
if (pair.x < -300) {
pair.destroyPair();
pipePairs.splice(i, 1);
continue;
}
// Check for passing pipe (score)
if (!pair.passed && pair.x + 110 < chicken.x) {
pair.passed = true;
score += 1;
scoreTxt.setText(score + '');
LK.getSound('point').play();
// Increase speed every 10 points
if (score % 10 === 0) {
pipeSpeed += 1.5;
}
}
}
// Spawn new pipes
if (pipePairs.length === 0 || pipePairs[pipePairs.length - 1].x < 2048 - PIPE_INTERVAL) {
spawnPipePair();
}
// Collision detection
var collided = false;
for (var i = 0; i < pipePairs.length; i++) {
if (checkPipeCollision(chicken, pipePairs[i])) {
collided = true;
break;
}
}
if (checkGroundCeiling(chicken)) {
collided = true;
}
if (collided && chicken.alive) {
chicken.die();
LK.getSound('hit').play();
gameOver = true;
// Stop music on game over
LK.stopMusic();
// Update best score
if (score > bestScore) {
bestScore = score;
storage.flyingChickenBestScore = bestScore;
}
// Flash screen
LK.effects.flashScreen(0xff0000, 800);
// Show game over (auto resets)
LK.setTimeout(function () {
// Show best score in the bestScoreTxt (bottom center) before game over popup
if (bestScore > 0) {
bestScoreTxt.setText('Best: ' + bestScore);
} else {
bestScoreTxt.setText('');
}
LK.showGameOver();
}, 600);
}
};
// On game over, reset state
LK.on('gameover', function () {
resetGame();
});
// On you win (not used, but for completeness)
LK.on('youwin', function () {
resetGame();
});
// Initial state
resetGame();