/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Bird class
var Bird = Container.expand(function () {
var self = Container.call(this);
// Attach a yellow ellipse as the bird
var birdAsset = self.attachAsset('bird', {
anchorX: 0.5,
anchorY: 0.5
});
// Physics properties
self.velocityY = 0;
self.gravity = 0.9; // Gravity per frame (reduced for easier play)
self.flapStrength = -28; // Negative for upward movement
// Bird size for collision
self.radius = birdAsset.width * 0.45;
// Flap method
self.flap = function () {
self.velocityY = self.flapStrength;
};
// Update method called every frame
self.update = function () {
self.velocityY += self.gravity;
self.y += self.velocityY;
// Clamp rotation for visual feedback
var maxAngle = Math.PI / 4;
var minAngle = -Math.PI / 3;
var angle = self.velocityY * 0.03;
if (angle > maxAngle) angle = maxAngle;
if (angle < minAngle) angle = minAngle;
birdAsset.rotation = angle;
};
return self;
});
// PipePair class (top and bottom pipes)
var PipePair = Container.expand(function () {
var self = Container.call(this);
// Pipe properties
self.pipeWidth = 220;
self.gapHeight = 720;
self.speed = 12; // Speed at which pipes move left
// Randomize gap position
var minGapY = 400;
var maxGapY = 2732 - 400 - self.gapHeight;
self.gapY = minGapY + Math.floor(Math.random() * (maxGapY - minGapY + 1));
// Top pipe
var topPipe = self.attachAsset('pipe', {
anchorX: 0,
anchorY: 1,
width: self.pipeWidth,
height: self.gapY,
color: 0x4ec04e,
shape: 'box'
});
topPipe.x = 0;
topPipe.y = self.gapY;
// Bottom pipe
var bottomPipe = self.attachAsset('pipe', {
anchorX: 0,
anchorY: 0,
width: self.pipeWidth,
height: 2732 - (self.gapY + self.gapHeight),
color: 0x4ec04e,
shape: 'box'
});
bottomPipe.x = 0;
bottomPipe.y = self.gapY + self.gapHeight;
// For scoring: has the bird passed this pipe?
self.passed = false;
// Update method
self.update = function () {
self.x -= self.speed;
};
// Helper for collision rectangles
self.getRects = function () {
return [
// Top pipe
{
x: self.x,
y: 0,
width: self.pipeWidth,
height: self.gapY
},
// Bottom pipe
{
x: self.x,
y: self.gapY + self.gapHeight,
width: self.pipeWidth,
height: 2732 - (self.gapY + self.gapHeight)
}];
};
return self;
});
// Token class (collectible between pipes)
var Token = Container.expand(function () {
var self = Container.call(this);
// Attach the token asset (yellow token image)
var tokenAsset = self.attachAsset('token', {
anchorX: 0.5,
anchorY: 0.5,
width: 90,
height: 90
});
self.radius = tokenAsset.width * 0.45;
self.collected = false;
self.update = function () {
// Token moves left at the same speed as pipes
self.x -= 12;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87ceeb // Sky blue
});
/****
* Game Code
****/
// Tween plugin for smooth animations (optional, but included for future use)
// Centered ground height
var groundHeight = 220;
// Add a fixed cloud image to the upper section
var cloud = LK.getAsset('bulut', {
anchorX: 0.5,
anchorY: 0.0,
width: 320,
height: 180,
x: 2048 * 0.7,
y: 120
});
game.addChild(cloud);
// Add a second fixed cloud to the upper section (different position)
var cloud2 = LK.getAsset('bulut', {
anchorX: 0.5,
anchorY: 0.0,
width: 260,
height: 140,
x: 2048 * 0.32,
y: 220
});
game.addChild(cloud2);
// Bird setup
var bird = new Bird();
bird.x = 2048 * 0.32;
bird.y = 2732 * 0.5;
game.addChild(bird);
// Pipes array
var pipes = [];
// Tokens array (collectibles between pipes)
var tokens = [];
// Score
var score = 0;
var scoreTxt = new Text2('0', {
size: 180,
fill: 0xFFF700
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Ground (invisible, for collision)
var groundY = 2732 - groundHeight;
// Add ground visual (optional, but makes it clear)
var ground = LK.getAsset('ground', {
anchorX: 0,
anchorY: 0,
width: 2048,
height: groundHeight,
color: 0xeecb7a,
shape: 'box',
y: groundY,
x: 0
});
game.addChild(ground);
// Add green grass strip on top of the ground
var grassHeight = 48;
var grass = LK.getAsset('ground', {
anchorX: 0,
anchorY: 0,
width: 2048,
height: grassHeight,
color: 0x4ec04e,
shape: 'box',
y: groundY,
x: 0
});
game.addChild(grass);
// Add many grass tufts ("ot" asset) along the ground for a lush effect
var grassTuftWidth = 100;
var grassTuftHeight = 100;
var grassTuftY = groundY - grassTuftHeight + 24; // Slightly overlap the green strip
var grassTuftSpacing = 60; // Overlap for density
for (var gx = 0; gx < 2048; gx += grassTuftSpacing) {
var tuft = LK.getAsset('ot', {
anchorX: 0,
anchorY: 0,
width: grassTuftWidth,
height: grassTuftHeight,
x: gx,
y: grassTuftY
});
game.addChild(tuft);
}
// Pipe spawn timer
var pipeInterval = 90; // Frames between pipes
var pipeTimer = 0;
// Game state
var isAlive = true;
var started = false;
// Tap/flap handler
function flapHandler(x, y, obj) {
if (!isAlive) return;
if (!started) {
started = true;
}
bird.flap();
LK.getSound('click').play();
}
game.down = flapHandler;
// Main update loop
game.update = function () {
if (!isAlive) return;
// Only update bird physics if started
if (started) {
bird.update();
}
// Spawn pipes
if (started) {
pipeTimer++;
if (pipeTimer >= pipeInterval) {
pipeTimer = 0;
var pipe = new PipePair();
pipe.x = 2048;
game.addChild(pipe);
pipes.push(pipe);
// Spawn a token between the pipes
var token = new Token();
// Place token in the center of the gap
token.x = pipe.x + pipe.pipeWidth / 2;
token.y = pipe.gapY + pipe.gapHeight / 2;
game.addChild(token);
if (!tokens) tokens = [];
tokens.push(token);
}
}
// Update pipes and check for off-screen
for (var i = pipes.length - 1; i >= 0; i--) {
var pipe = pipes[i];
pipe.update();
// Remove pipes that are off screen
if (pipe.x + pipe.pipeWidth < 0) {
pipe.destroy();
pipes.splice(i, 1);
continue;
}
// Scoring: if bird passed the pipe
if (!pipe.passed && pipe.x + pipe.pipeWidth < bird.x) {
pipe.passed = true;
score++;
scoreTxt.setText(score);
// Removed score sound here; will play only on token collection
}
}
// Collision detection (pipes)
for (var i = 0; i < pipes.length; i++) {
var rects = pipes[i].getRects();
for (var j = 0; j < rects.length; j++) {
var r = rects[j];
// Circle-rectangle collision
var cx = bird.x;
var cy = bird.y;
var rx = r.x;
var ry = r.y;
var rw = r.width;
var rh = r.height;
// Find closest point to circle within rectangle
var closestX = cx;
if (cx < rx) closestX = rx;else if (cx > rx + rw) closestX = rx + rw;
var closestY = cy;
if (cy < ry) closestY = ry;else if (cy > ry + rh) closestY = ry + rh;
var dx = cx - closestX;
var dy = cy - closestY;
var distSq = dx * dx + dy * dy;
if (distSq < bird.radius * bird.radius) {
// Collision!
isAlive = false;
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
return;
}
}
}
// Collision with ground
if (bird.y + bird.radius > groundY) {
isAlive = false;
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
return;
}
// Collision with ceiling
if (bird.y - bird.radius < 0) {
bird.y = bird.radius;
bird.velocityY = 0;
}
// --- Token update, off-screen removal, and collection ---
for (var i = tokens.length - 1; i >= 0; i--) {
var token = tokens[i];
token.update();
// Remove token if off screen
if (token.x + token.radius < 0) {
token.destroy();
tokens.splice(i, 1);
continue;
}
// Check for collection (circle-circle collision)
var dx = bird.x - token.x;
var dy = bird.y - token.y;
var distSq = dx * dx + dy * dy;
var minDist = bird.radius + token.radius;
if (!token.collected && distSq < minDist * minDist) {
token.collected = true;
LK.getSound('score').play(); // Play score sound on token collection
token.destroy();
tokens.splice(i, 1);
// Optionally increment score or trigger effect
// score++;
// scoreTxt.setText(score);
}
}
};
// Reset game state on game over (handled by LK, but for safety)
game.on('destroy', function () {
pipes = [];
isAlive = false;
started = false;
score = 0;
});
/****
* Asset Initialization (for static analysis)
****/
// Bird asset (yellow ellipse)
// Pipe asset (green box)
// Ground asset (brown box) /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Bird class
var Bird = Container.expand(function () {
var self = Container.call(this);
// Attach a yellow ellipse as the bird
var birdAsset = self.attachAsset('bird', {
anchorX: 0.5,
anchorY: 0.5
});
// Physics properties
self.velocityY = 0;
self.gravity = 0.9; // Gravity per frame (reduced for easier play)
self.flapStrength = -28; // Negative for upward movement
// Bird size for collision
self.radius = birdAsset.width * 0.45;
// Flap method
self.flap = function () {
self.velocityY = self.flapStrength;
};
// Update method called every frame
self.update = function () {
self.velocityY += self.gravity;
self.y += self.velocityY;
// Clamp rotation for visual feedback
var maxAngle = Math.PI / 4;
var minAngle = -Math.PI / 3;
var angle = self.velocityY * 0.03;
if (angle > maxAngle) angle = maxAngle;
if (angle < minAngle) angle = minAngle;
birdAsset.rotation = angle;
};
return self;
});
// PipePair class (top and bottom pipes)
var PipePair = Container.expand(function () {
var self = Container.call(this);
// Pipe properties
self.pipeWidth = 220;
self.gapHeight = 720;
self.speed = 12; // Speed at which pipes move left
// Randomize gap position
var minGapY = 400;
var maxGapY = 2732 - 400 - self.gapHeight;
self.gapY = minGapY + Math.floor(Math.random() * (maxGapY - minGapY + 1));
// Top pipe
var topPipe = self.attachAsset('pipe', {
anchorX: 0,
anchorY: 1,
width: self.pipeWidth,
height: self.gapY,
color: 0x4ec04e,
shape: 'box'
});
topPipe.x = 0;
topPipe.y = self.gapY;
// Bottom pipe
var bottomPipe = self.attachAsset('pipe', {
anchorX: 0,
anchorY: 0,
width: self.pipeWidth,
height: 2732 - (self.gapY + self.gapHeight),
color: 0x4ec04e,
shape: 'box'
});
bottomPipe.x = 0;
bottomPipe.y = self.gapY + self.gapHeight;
// For scoring: has the bird passed this pipe?
self.passed = false;
// Update method
self.update = function () {
self.x -= self.speed;
};
// Helper for collision rectangles
self.getRects = function () {
return [
// Top pipe
{
x: self.x,
y: 0,
width: self.pipeWidth,
height: self.gapY
},
// Bottom pipe
{
x: self.x,
y: self.gapY + self.gapHeight,
width: self.pipeWidth,
height: 2732 - (self.gapY + self.gapHeight)
}];
};
return self;
});
// Token class (collectible between pipes)
var Token = Container.expand(function () {
var self = Container.call(this);
// Attach the token asset (yellow token image)
var tokenAsset = self.attachAsset('token', {
anchorX: 0.5,
anchorY: 0.5,
width: 90,
height: 90
});
self.radius = tokenAsset.width * 0.45;
self.collected = false;
self.update = function () {
// Token moves left at the same speed as pipes
self.x -= 12;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87ceeb // Sky blue
});
/****
* Game Code
****/
// Tween plugin for smooth animations (optional, but included for future use)
// Centered ground height
var groundHeight = 220;
// Add a fixed cloud image to the upper section
var cloud = LK.getAsset('bulut', {
anchorX: 0.5,
anchorY: 0.0,
width: 320,
height: 180,
x: 2048 * 0.7,
y: 120
});
game.addChild(cloud);
// Add a second fixed cloud to the upper section (different position)
var cloud2 = LK.getAsset('bulut', {
anchorX: 0.5,
anchorY: 0.0,
width: 260,
height: 140,
x: 2048 * 0.32,
y: 220
});
game.addChild(cloud2);
// Bird setup
var bird = new Bird();
bird.x = 2048 * 0.32;
bird.y = 2732 * 0.5;
game.addChild(bird);
// Pipes array
var pipes = [];
// Tokens array (collectibles between pipes)
var tokens = [];
// Score
var score = 0;
var scoreTxt = new Text2('0', {
size: 180,
fill: 0xFFF700
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Ground (invisible, for collision)
var groundY = 2732 - groundHeight;
// Add ground visual (optional, but makes it clear)
var ground = LK.getAsset('ground', {
anchorX: 0,
anchorY: 0,
width: 2048,
height: groundHeight,
color: 0xeecb7a,
shape: 'box',
y: groundY,
x: 0
});
game.addChild(ground);
// Add green grass strip on top of the ground
var grassHeight = 48;
var grass = LK.getAsset('ground', {
anchorX: 0,
anchorY: 0,
width: 2048,
height: grassHeight,
color: 0x4ec04e,
shape: 'box',
y: groundY,
x: 0
});
game.addChild(grass);
// Add many grass tufts ("ot" asset) along the ground for a lush effect
var grassTuftWidth = 100;
var grassTuftHeight = 100;
var grassTuftY = groundY - grassTuftHeight + 24; // Slightly overlap the green strip
var grassTuftSpacing = 60; // Overlap for density
for (var gx = 0; gx < 2048; gx += grassTuftSpacing) {
var tuft = LK.getAsset('ot', {
anchorX: 0,
anchorY: 0,
width: grassTuftWidth,
height: grassTuftHeight,
x: gx,
y: grassTuftY
});
game.addChild(tuft);
}
// Pipe spawn timer
var pipeInterval = 90; // Frames between pipes
var pipeTimer = 0;
// Game state
var isAlive = true;
var started = false;
// Tap/flap handler
function flapHandler(x, y, obj) {
if (!isAlive) return;
if (!started) {
started = true;
}
bird.flap();
LK.getSound('click').play();
}
game.down = flapHandler;
// Main update loop
game.update = function () {
if (!isAlive) return;
// Only update bird physics if started
if (started) {
bird.update();
}
// Spawn pipes
if (started) {
pipeTimer++;
if (pipeTimer >= pipeInterval) {
pipeTimer = 0;
var pipe = new PipePair();
pipe.x = 2048;
game.addChild(pipe);
pipes.push(pipe);
// Spawn a token between the pipes
var token = new Token();
// Place token in the center of the gap
token.x = pipe.x + pipe.pipeWidth / 2;
token.y = pipe.gapY + pipe.gapHeight / 2;
game.addChild(token);
if (!tokens) tokens = [];
tokens.push(token);
}
}
// Update pipes and check for off-screen
for (var i = pipes.length - 1; i >= 0; i--) {
var pipe = pipes[i];
pipe.update();
// Remove pipes that are off screen
if (pipe.x + pipe.pipeWidth < 0) {
pipe.destroy();
pipes.splice(i, 1);
continue;
}
// Scoring: if bird passed the pipe
if (!pipe.passed && pipe.x + pipe.pipeWidth < bird.x) {
pipe.passed = true;
score++;
scoreTxt.setText(score);
// Removed score sound here; will play only on token collection
}
}
// Collision detection (pipes)
for (var i = 0; i < pipes.length; i++) {
var rects = pipes[i].getRects();
for (var j = 0; j < rects.length; j++) {
var r = rects[j];
// Circle-rectangle collision
var cx = bird.x;
var cy = bird.y;
var rx = r.x;
var ry = r.y;
var rw = r.width;
var rh = r.height;
// Find closest point to circle within rectangle
var closestX = cx;
if (cx < rx) closestX = rx;else if (cx > rx + rw) closestX = rx + rw;
var closestY = cy;
if (cy < ry) closestY = ry;else if (cy > ry + rh) closestY = ry + rh;
var dx = cx - closestX;
var dy = cy - closestY;
var distSq = dx * dx + dy * dy;
if (distSq < bird.radius * bird.radius) {
// Collision!
isAlive = false;
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
return;
}
}
}
// Collision with ground
if (bird.y + bird.radius > groundY) {
isAlive = false;
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
return;
}
// Collision with ceiling
if (bird.y - bird.radius < 0) {
bird.y = bird.radius;
bird.velocityY = 0;
}
// --- Token update, off-screen removal, and collection ---
for (var i = tokens.length - 1; i >= 0; i--) {
var token = tokens[i];
token.update();
// Remove token if off screen
if (token.x + token.radius < 0) {
token.destroy();
tokens.splice(i, 1);
continue;
}
// Check for collection (circle-circle collision)
var dx = bird.x - token.x;
var dy = bird.y - token.y;
var distSq = dx * dx + dy * dy;
var minDist = bird.radius + token.radius;
if (!token.collected && distSq < minDist * minDist) {
token.collected = true;
LK.getSound('score').play(); // Play score sound on token collection
token.destroy();
tokens.splice(i, 1);
// Optionally increment score or trigger effect
// score++;
// scoreTxt.setText(score);
}
}
};
// Reset game state on game over (handled by LK, but for safety)
game.on('destroy', function () {
pipes = [];
isAlive = false;
started = false;
score = 0;
});
/****
* Asset Initialization (for static analysis)
****/
// Bird asset (yellow ellipse)
// Pipe asset (green box)
// Ground asset (brown box)
brown soil 2d. In-Game asset. 2d. High contrast. No shadows
cloud 2d. In-Game asset. 2d. High contrast. No shadows
green grass 2d. In-Game asset. 2d. High contrast. No shadows
football cup. In-Game asset. 2d. High contrast. No shadows
2d flappy bird pipe . No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat