/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Background = Container.expand(function () { var self = Container.call(this); self.backgroundLayers = []; self.currentTheme = 0; self.themes = ['background1', 'background2', 'background3']; self.transitionTimer = 0; self.transitionDuration = 1800; // 30 seconds at 60fps self.decorativeElements = []; // Create initial background var bg1 = self.attachAsset(self.themes[self.currentTheme], { anchorX: 0, anchorY: 0 }); self.backgroundLayers.push(bg1); // Add decorative elements based on theme self.addDecorations = function (theme) { // Clear existing decorations for (var i = 0; i < self.decorativeElements.length; i++) { self.decorativeElements[i].destroy(); } self.decorativeElements = []; if (theme === 0) { // Day theme - add sun var sun = self.addChild(new Sun()); sun.x = 1600; sun.y = 300; self.decorativeElements.push(sun); } else if (theme === 2) { // Night theme - add stars for (var j = 0; j < 8; j++) { var star = self.addChild(new Star()); star.x = Math.random() * 1800 + 100; star.y = Math.random() * 600 + 100; self.decorativeElements.push(star); } } }; // Add initial decorations self.addDecorations(self.currentTheme); self.update = function () { self.transitionTimer++; // Update decorative elements for (var k = 0; k < self.decorativeElements.length; k++) { if (self.decorativeElements[k].update) { self.decorativeElements[k].update(); } } if (self.transitionTimer >= self.transitionDuration) { self.transitionTimer = 0; self.currentTheme = (self.currentTheme + 1) % self.themes.length; // Create new background for transition var newBg = LK.getAsset(self.themes[self.currentTheme], { anchorX: 0, anchorY: 0, alpha: 0 }); self.addChild(newBg); self.backgroundLayers.push(newBg); // Add new decorations for the new theme self.addDecorations(self.currentTheme); // Fade in new background tween(newBg, { alpha: 1 }, { duration: 2000 }); // Fade out old backgrounds for (var i = 0; i < self.backgroundLayers.length - 1; i++) { var oldBg = self.backgroundLayers[i]; tween(oldBg, { alpha: 0 }, { duration: 2000, onFinish: function onFinish() { oldBg.destroy(); } }); } // Keep only the new background in array self.backgroundLayers = [newBg]; } }; return self; }); var Bird = Container.expand(function () { var self = Container.call(this); var birdGraphics = self.attachAsset('bird', { anchorX: 0.5, anchorY: 0.5 }); // Add subtle shadow effect by scaling slightly birdGraphics.scaleX = 1.2; birdGraphics.scaleY = 1.2; self.velocityY = 0; self.gravity = 0.8; self.flapPower = -15; self.maxFallSpeed = 15; self.isDead = false; self.flap = function () { if (!self.isDead) { self.velocityY = self.flapPower; LK.getSound('flap').play(); // Wing flap animation tween(birdGraphics, { scaleY: 1.2 }, { duration: 100, onFinish: function onFinish() { tween(birdGraphics, { scaleY: 1.0 }, { duration: 100 }); } }); } }; self.update = function () { if (!self.isDead) { // Apply gravity self.velocityY += self.gravity; // Limit fall speed if (self.velocityY > self.maxFallSpeed) { self.velocityY = self.maxFallSpeed; } // Update position self.y += self.velocityY; // Rotate bird based on velocity var targetRotation = Math.min(Math.max(self.velocityY * 0.1, -0.5), 1.5); birdGraphics.rotation = targetRotation; // Allow bird to pass through top of screen if (self.y < -30) { self.y = 2732 + 30; // Wrap to bottom of screen } // Check collision with ground if (self.y > 2732 - 150 - 30) { self.y = 2732 - 150 - 30; self.die(); } } }; self.die = function () { if (!self.isDead) { self.isDead = true; LK.getSound('hit').play(); // Death animation tween(birdGraphics, { rotation: Math.PI, alpha: 0.7 }, { duration: 1000 }); tween(self, { y: 2732 - 150 - 30 }, { duration: 1000, easing: tween.bounceOut }); } }; return self; }); var Cloud = Container.expand(function () { var self = Container.call(this); var cloudGraphics = self.attachAsset('cloud', { anchorX: 0.5, anchorY: 0.5, alpha: 0.8 }); // Add gentle floating animation self.floatTimer = Math.random() * Math.PI * 2; cloudGraphics.scaleX = 0.8 + Math.random() * 0.4; cloudGraphics.scaleY = 0.8 + Math.random() * 0.4; self.speed = -1; self.update = function () { self.x += self.speed; self.floatTimer += 0.02; cloudGraphics.y = Math.sin(self.floatTimer) * 3; }; return self; }); var Pipe = Container.expand(function () { var self = Container.call(this); self.gapSize = 480; self.speed = -4; self.scored = false; // Top pipe - extends from top of screen to gap var topPipe = self.attachAsset('pipe', { anchorX: 0.5, anchorY: 0 }); // Bottom pipe - extends from gap to bottom of screen var bottomPipe = self.attachAsset('pipe', { anchorX: 0.5, anchorY: 0 }); // Create hitbox containers that will match the visible pipe areas self.topHitbox = self.addChild(LK.getAsset('pipe', { anchorX: 0.5, anchorY: 0, alpha: 0 // Invisible hitbox })); self.bottomHitbox = self.addChild(LK.getAsset('pipe', { anchorX: 0.5, anchorY: 0, alpha: 0 // Invisible hitbox })); self.setGapPosition = function (gapY) { // Position top pipe to end at gap start topPipe.y = 0; topPipe.scaleY = (gapY - self.gapSize / 2) / topPipe.height; // Position bottom pipe to start at gap end bottomPipe.y = gapY + self.gapSize / 2; bottomPipe.scaleY = (2732 - 150 - (gapY + self.gapSize / 2)) / bottomPipe.height; // Update hitboxes to match visible pipe areas self.topHitbox.y = 0; self.topHitbox.scaleY = (gapY - self.gapSize / 2) / self.topHitbox.height; self.topHitbox.scaleX = topPipe.scaleX; self.bottomHitbox.y = gapY + self.gapSize / 2; self.bottomHitbox.scaleY = (2732 - 150 - (gapY + self.gapSize / 2)) / self.bottomHitbox.height; self.bottomHitbox.scaleX = bottomPipe.scaleX; }; // Custom intersects method that checks both hitboxes self.intersects = function (other) { return self.topHitbox.intersects(other) || self.bottomHitbox.intersects(other); }; self.update = function () { self.x += self.speed; }; return self; }); var Star = Container.expand(function () { var self = Container.call(this); var starGraphics = self.attachAsset('star', { anchorX: 0.5, anchorY: 0.5 }); // Twinkling effect self.twinkleTimer = Math.random() * Math.PI * 2; self.update = function () { self.twinkleTimer += 0.1; starGraphics.alpha = 0.5 + Math.sin(self.twinkleTimer) * 0.5; }; return self; }); var Sun = Container.expand(function () { var self = Container.call(this); var sunGraphics = self.attachAsset('sun', { anchorX: 0.5, anchorY: 0.5 }); // Gentle pulsing animation self.pulseTimer = 0; self.update = function () { self.pulseTimer += 0.05; sunGraphics.scaleX = 1 + Math.sin(self.pulseTimer) * 0.1; sunGraphics.scaleY = 1 + Math.sin(self.pulseTimer) * 0.1; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87CEEB }); /**** * Game Code ****/ // Game variables // Sky backgrounds with gradient-like colors // Deep sky blue // Sunset pink // Night dark blue // Bird with better proportions and color // Bright orange bird // Fluffy white clouds // Soft white clouds // Textured ground // Grass green ground // Pipes with better color scheme // Teal pipe color // Additional decorative elements // Yellow sun // Small stars for night var bird; var pipes = []; var clouds = []; var ground; var backgroundSystem; var gameStarted = false; var gameWaitingToStart = true; var pipeSpawnTimer = 0; var cloudSpawnTimer = 0; var difficultyTimer = 0; // UI var scoreTxt = new Text2('0', { size: 120, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); scoreTxt.y = 100; var instructionTxt = new Text2('TAP TO FLAP', { size: 80, fill: 0xFFFFFF }); instructionTxt.anchor.set(0.5, 0.5); LK.gui.center.addChild(instructionTxt); // Initialize background system backgroundSystem = game.addChild(new Background()); // Create ground ground = game.addChild(LK.getAsset('ground', { anchorX: 0, anchorY: 0, x: 0, y: 2732 - 150 })); // Create bird bird = game.addChild(new Bird()); bird.x = 300; bird.y = 1366; bird.isDead = true; // Start in idle state until game begins // Initial clouds for (var i = 0; i < 3; i++) { var cloud = game.addChild(new Cloud()); cloud.x = Math.random() * 2048; cloud.y = Math.random() * 800 + 200; cloud.alpha = 0.7; clouds.push(cloud); } function startGame() { if (!gameStarted && gameWaitingToStart) { gameStarted = true; gameWaitingToStart = false; instructionTxt.visible = false; pipeSpawnTimer = 0; // Reset timer so first pipe appears at correct timing LK.playMusic('background_music'); } } function spawnPipe() { var pipe = game.addChild(new Pipe()); pipe.x = 2048 + 60; // Dynamic difficulty - reduce gap size over time var difficultyFactor = Math.min(LK.getScore() * 0.02, 0.3); pipe.gapSize = Math.max(430 - difficultyFactor * 100, 360); // Randomize gap position with more variation - ensure gap stays within playable area var minGapY = pipe.gapSize / 2 + 200; // Minimum distance from top var maxGapY = 2732 - 150 - pipe.gapSize / 2 - 200; // Maximum distance from ground var gapY = Math.random() * (maxGapY - minGapY) + minGapY; pipe.setGapPosition(gapY); pipes.push(pipe); } function spawnCloud() { var cloud = game.addChild(new Cloud()); cloud.x = 2048 + 100; cloud.y = Math.random() * 1000 + 200; cloud.alpha = 0.6; cloud.scaleX = Math.random() * 0.5 + 0.5; cloud.scaleY = Math.random() * 0.5 + 0.5; clouds.push(cloud); } // Game input game.down = function (x, y, obj) { if (!gameStarted) { startGame(); bird.isDead = false; // Activate bird when game starts } if (gameStarted) { bird.flap(); } }; // Game update loop game.update = function () { // Update background system backgroundSystem.update(); if (gameStarted && !bird.isDead && !gameWaitingToStart) { // Spawn pipes pipeSpawnTimer++; if (pipeSpawnTimer >= 210) { // Every 3.5 seconds pipeSpawnTimer = 0; spawnPipe(); } // Spawn clouds cloudSpawnTimer++; if (cloudSpawnTimer >= 240) { // Every 4 seconds cloudSpawnTimer = 0; spawnCloud(); } // Update and check pipes for (var i = pipes.length - 1; i >= 0; i--) { var pipe = pipes[i]; // Check for scoring if (!pipe.scored && pipe.x + 60 < bird.x) { pipe.scored = true; LK.setScore(LK.getScore() + 1); scoreTxt.setText(LK.getScore()); LK.getSound('score').play(); } // Check collision with bird if (pipe.intersects(bird)) { bird.die(); LK.effects.flashScreen(0xff0000, 500); LK.showGameOver(); // Immediate game over when touching pipe } // Remove off-screen pipes if (pipe.x < -150) { pipe.destroy(); pipes.splice(i, 1); } } // Update and manage clouds for (var j = clouds.length - 1; j >= 0; j--) { var cloud = clouds[j]; if (cloud.x < -250) { cloud.destroy(); clouds.splice(j, 1); } } } // Show game over immediately when bird dies from hitting ground if (bird.isDead && bird.y >= 2732 - 150 - 30) { LK.effects.flashScreen(0xff0000, 500); LK.showGameOver(); } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Background = Container.expand(function () {
var self = Container.call(this);
self.backgroundLayers = [];
self.currentTheme = 0;
self.themes = ['background1', 'background2', 'background3'];
self.transitionTimer = 0;
self.transitionDuration = 1800; // 30 seconds at 60fps
self.decorativeElements = [];
// Create initial background
var bg1 = self.attachAsset(self.themes[self.currentTheme], {
anchorX: 0,
anchorY: 0
});
self.backgroundLayers.push(bg1);
// Add decorative elements based on theme
self.addDecorations = function (theme) {
// Clear existing decorations
for (var i = 0; i < self.decorativeElements.length; i++) {
self.decorativeElements[i].destroy();
}
self.decorativeElements = [];
if (theme === 0) {
// Day theme - add sun
var sun = self.addChild(new Sun());
sun.x = 1600;
sun.y = 300;
self.decorativeElements.push(sun);
} else if (theme === 2) {
// Night theme - add stars
for (var j = 0; j < 8; j++) {
var star = self.addChild(new Star());
star.x = Math.random() * 1800 + 100;
star.y = Math.random() * 600 + 100;
self.decorativeElements.push(star);
}
}
};
// Add initial decorations
self.addDecorations(self.currentTheme);
self.update = function () {
self.transitionTimer++;
// Update decorative elements
for (var k = 0; k < self.decorativeElements.length; k++) {
if (self.decorativeElements[k].update) {
self.decorativeElements[k].update();
}
}
if (self.transitionTimer >= self.transitionDuration) {
self.transitionTimer = 0;
self.currentTheme = (self.currentTheme + 1) % self.themes.length;
// Create new background for transition
var newBg = LK.getAsset(self.themes[self.currentTheme], {
anchorX: 0,
anchorY: 0,
alpha: 0
});
self.addChild(newBg);
self.backgroundLayers.push(newBg);
// Add new decorations for the new theme
self.addDecorations(self.currentTheme);
// Fade in new background
tween(newBg, {
alpha: 1
}, {
duration: 2000
});
// Fade out old backgrounds
for (var i = 0; i < self.backgroundLayers.length - 1; i++) {
var oldBg = self.backgroundLayers[i];
tween(oldBg, {
alpha: 0
}, {
duration: 2000,
onFinish: function onFinish() {
oldBg.destroy();
}
});
}
// Keep only the new background in array
self.backgroundLayers = [newBg];
}
};
return self;
});
var Bird = Container.expand(function () {
var self = Container.call(this);
var birdGraphics = self.attachAsset('bird', {
anchorX: 0.5,
anchorY: 0.5
});
// Add subtle shadow effect by scaling slightly
birdGraphics.scaleX = 1.2;
birdGraphics.scaleY = 1.2;
self.velocityY = 0;
self.gravity = 0.8;
self.flapPower = -15;
self.maxFallSpeed = 15;
self.isDead = false;
self.flap = function () {
if (!self.isDead) {
self.velocityY = self.flapPower;
LK.getSound('flap').play();
// Wing flap animation
tween(birdGraphics, {
scaleY: 1.2
}, {
duration: 100,
onFinish: function onFinish() {
tween(birdGraphics, {
scaleY: 1.0
}, {
duration: 100
});
}
});
}
};
self.update = function () {
if (!self.isDead) {
// Apply gravity
self.velocityY += self.gravity;
// Limit fall speed
if (self.velocityY > self.maxFallSpeed) {
self.velocityY = self.maxFallSpeed;
}
// Update position
self.y += self.velocityY;
// Rotate bird based on velocity
var targetRotation = Math.min(Math.max(self.velocityY * 0.1, -0.5), 1.5);
birdGraphics.rotation = targetRotation;
// Allow bird to pass through top of screen
if (self.y < -30) {
self.y = 2732 + 30; // Wrap to bottom of screen
}
// Check collision with ground
if (self.y > 2732 - 150 - 30) {
self.y = 2732 - 150 - 30;
self.die();
}
}
};
self.die = function () {
if (!self.isDead) {
self.isDead = true;
LK.getSound('hit').play();
// Death animation
tween(birdGraphics, {
rotation: Math.PI,
alpha: 0.7
}, {
duration: 1000
});
tween(self, {
y: 2732 - 150 - 30
}, {
duration: 1000,
easing: tween.bounceOut
});
}
};
return self;
});
var Cloud = Container.expand(function () {
var self = Container.call(this);
var cloudGraphics = self.attachAsset('cloud', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8
});
// Add gentle floating animation
self.floatTimer = Math.random() * Math.PI * 2;
cloudGraphics.scaleX = 0.8 + Math.random() * 0.4;
cloudGraphics.scaleY = 0.8 + Math.random() * 0.4;
self.speed = -1;
self.update = function () {
self.x += self.speed;
self.floatTimer += 0.02;
cloudGraphics.y = Math.sin(self.floatTimer) * 3;
};
return self;
});
var Pipe = Container.expand(function () {
var self = Container.call(this);
self.gapSize = 480;
self.speed = -4;
self.scored = false;
// Top pipe - extends from top of screen to gap
var topPipe = self.attachAsset('pipe', {
anchorX: 0.5,
anchorY: 0
});
// Bottom pipe - extends from gap to bottom of screen
var bottomPipe = self.attachAsset('pipe', {
anchorX: 0.5,
anchorY: 0
});
// Create hitbox containers that will match the visible pipe areas
self.topHitbox = self.addChild(LK.getAsset('pipe', {
anchorX: 0.5,
anchorY: 0,
alpha: 0 // Invisible hitbox
}));
self.bottomHitbox = self.addChild(LK.getAsset('pipe', {
anchorX: 0.5,
anchorY: 0,
alpha: 0 // Invisible hitbox
}));
self.setGapPosition = function (gapY) {
// Position top pipe to end at gap start
topPipe.y = 0;
topPipe.scaleY = (gapY - self.gapSize / 2) / topPipe.height;
// Position bottom pipe to start at gap end
bottomPipe.y = gapY + self.gapSize / 2;
bottomPipe.scaleY = (2732 - 150 - (gapY + self.gapSize / 2)) / bottomPipe.height;
// Update hitboxes to match visible pipe areas
self.topHitbox.y = 0;
self.topHitbox.scaleY = (gapY - self.gapSize / 2) / self.topHitbox.height;
self.topHitbox.scaleX = topPipe.scaleX;
self.bottomHitbox.y = gapY + self.gapSize / 2;
self.bottomHitbox.scaleY = (2732 - 150 - (gapY + self.gapSize / 2)) / self.bottomHitbox.height;
self.bottomHitbox.scaleX = bottomPipe.scaleX;
};
// Custom intersects method that checks both hitboxes
self.intersects = function (other) {
return self.topHitbox.intersects(other) || self.bottomHitbox.intersects(other);
};
self.update = function () {
self.x += self.speed;
};
return self;
});
var Star = Container.expand(function () {
var self = Container.call(this);
var starGraphics = self.attachAsset('star', {
anchorX: 0.5,
anchorY: 0.5
});
// Twinkling effect
self.twinkleTimer = Math.random() * Math.PI * 2;
self.update = function () {
self.twinkleTimer += 0.1;
starGraphics.alpha = 0.5 + Math.sin(self.twinkleTimer) * 0.5;
};
return self;
});
var Sun = Container.expand(function () {
var self = Container.call(this);
var sunGraphics = self.attachAsset('sun', {
anchorX: 0.5,
anchorY: 0.5
});
// Gentle pulsing animation
self.pulseTimer = 0;
self.update = function () {
self.pulseTimer += 0.05;
sunGraphics.scaleX = 1 + Math.sin(self.pulseTimer) * 0.1;
sunGraphics.scaleY = 1 + Math.sin(self.pulseTimer) * 0.1;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
// Game variables
// Sky backgrounds with gradient-like colors
// Deep sky blue
// Sunset pink
// Night dark blue
// Bird with better proportions and color
// Bright orange bird
// Fluffy white clouds
// Soft white clouds
// Textured ground
// Grass green ground
// Pipes with better color scheme
// Teal pipe color
// Additional decorative elements
// Yellow sun
// Small stars for night
var bird;
var pipes = [];
var clouds = [];
var ground;
var backgroundSystem;
var gameStarted = false;
var gameWaitingToStart = true;
var pipeSpawnTimer = 0;
var cloudSpawnTimer = 0;
var difficultyTimer = 0;
// UI
var scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
scoreTxt.y = 100;
var instructionTxt = new Text2('TAP TO FLAP', {
size: 80,
fill: 0xFFFFFF
});
instructionTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(instructionTxt);
// Initialize background system
backgroundSystem = game.addChild(new Background());
// Create ground
ground = game.addChild(LK.getAsset('ground', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 2732 - 150
}));
// Create bird
bird = game.addChild(new Bird());
bird.x = 300;
bird.y = 1366;
bird.isDead = true; // Start in idle state until game begins
// Initial clouds
for (var i = 0; i < 3; i++) {
var cloud = game.addChild(new Cloud());
cloud.x = Math.random() * 2048;
cloud.y = Math.random() * 800 + 200;
cloud.alpha = 0.7;
clouds.push(cloud);
}
function startGame() {
if (!gameStarted && gameWaitingToStart) {
gameStarted = true;
gameWaitingToStart = false;
instructionTxt.visible = false;
pipeSpawnTimer = 0; // Reset timer so first pipe appears at correct timing
LK.playMusic('background_music');
}
}
function spawnPipe() {
var pipe = game.addChild(new Pipe());
pipe.x = 2048 + 60;
// Dynamic difficulty - reduce gap size over time
var difficultyFactor = Math.min(LK.getScore() * 0.02, 0.3);
pipe.gapSize = Math.max(430 - difficultyFactor * 100, 360);
// Randomize gap position with more variation - ensure gap stays within playable area
var minGapY = pipe.gapSize / 2 + 200; // Minimum distance from top
var maxGapY = 2732 - 150 - pipe.gapSize / 2 - 200; // Maximum distance from ground
var gapY = Math.random() * (maxGapY - minGapY) + minGapY;
pipe.setGapPosition(gapY);
pipes.push(pipe);
}
function spawnCloud() {
var cloud = game.addChild(new Cloud());
cloud.x = 2048 + 100;
cloud.y = Math.random() * 1000 + 200;
cloud.alpha = 0.6;
cloud.scaleX = Math.random() * 0.5 + 0.5;
cloud.scaleY = Math.random() * 0.5 + 0.5;
clouds.push(cloud);
}
// Game input
game.down = function (x, y, obj) {
if (!gameStarted) {
startGame();
bird.isDead = false; // Activate bird when game starts
}
if (gameStarted) {
bird.flap();
}
};
// Game update loop
game.update = function () {
// Update background system
backgroundSystem.update();
if (gameStarted && !bird.isDead && !gameWaitingToStart) {
// Spawn pipes
pipeSpawnTimer++;
if (pipeSpawnTimer >= 210) {
// Every 3.5 seconds
pipeSpawnTimer = 0;
spawnPipe();
}
// Spawn clouds
cloudSpawnTimer++;
if (cloudSpawnTimer >= 240) {
// Every 4 seconds
cloudSpawnTimer = 0;
spawnCloud();
}
// Update and check pipes
for (var i = pipes.length - 1; i >= 0; i--) {
var pipe = pipes[i];
// Check for scoring
if (!pipe.scored && pipe.x + 60 < bird.x) {
pipe.scored = true;
LK.setScore(LK.getScore() + 1);
scoreTxt.setText(LK.getScore());
LK.getSound('score').play();
}
// Check collision with bird
if (pipe.intersects(bird)) {
bird.die();
LK.effects.flashScreen(0xff0000, 500);
LK.showGameOver(); // Immediate game over when touching pipe
}
// Remove off-screen pipes
if (pipe.x < -150) {
pipe.destroy();
pipes.splice(i, 1);
}
}
// Update and manage clouds
for (var j = clouds.length - 1; j >= 0; j--) {
var cloud = clouds[j];
if (cloud.x < -250) {
cloud.destroy();
clouds.splice(j, 1);
}
}
}
// Show game over immediately when bird dies from hitting ground
if (bird.isDead && bird.y >= 2732 - 150 - 30) {
LK.effects.flashScreen(0xff0000, 500);
LK.showGameOver();
}
};