User prompt
Make the spikes disappear every bounce
User prompt
Make it so if the bird bounces on the right side the spike appears on the other side and vice versa
User prompt
Make it appear randomly on the walls every bounce
User prompt
Make a spike that sticks on the wall
User prompt
Make it flap the other way
User prompt
Make the bird rotate correctly
User prompt
No make it FLAP in the right direction
User prompt
Now make him flap the correct direction
Code edit (1 edits merged)
Please save this source code
User prompt
Make the bird face the direction it’s flying
User prompt
Still broken
User prompt
Can you fix this
User prompt
Make the bird faster
User prompt
Make the gravity stronger
User prompt
Make the bird jump higher
User prompt
Make the bird bigger
User prompt
Fix the bug that makes the bird stick to the wall
User prompt
Make the gravity stronger
User prompt
Make the bird faster and jump higher
User prompt
Make the bird larger
User prompt
Stop the seeds from moving
User prompt
Make it so every time the bird bounces two seeds spawn on the screen and despawn on the next bounce
User prompt
Can you make this game
User prompt
Bounce Bird
Initial prompt
Can you make a game where you’re a bird that clicks to fly and bounces off the walls called bounce bird
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { coins: 0, selectedSkin: "bird", unlockedSkins: ["bird"] }); /**** * Classes ****/ var Bird = Container.expand(function () { var self = Container.call(this); // Get the selected skin var selectedSkin = storage.selectedSkin || "bird"; // Bird asset - use the correct asset based on the selected skin var birdGraphics = self.attachAsset(selectedSkin, { anchorX: 0.5, anchorY: 0.5, scaleX: 2.5, scaleY: 2.5 }); // Add a bigger hitbox for collision detection while keeping visual size self.hitArea = new Rectangle(-25, -25, 50, 50); // Set special flag for rainbow bird if (selectedSkin === "rainbow_bird") { self.isRainbow = true; // Will be handled in update } // Bird physics properties self.velocityY = 0; self.velocityX = 12; // Increased from 8 to 12 for faster horizontal movement self.gravity = 1.0; // Increased from 0.7 to 1.0 for stronger gravity self.flapStrength = -20; // Increased from -15 to -20 for higher jumps self.lastX = 0; self.lastY = 0; self.lastWasIntersecting = false; // Handle tap/click on bird self.down = function (x, y, obj) { self.flap(); }; // Make bird flap wings self.flap = function () { self.velocityY = self.flapStrength; // Play flap sound LK.getSound('flap').play(); // Make rainbow bird do a backflip (360 degree rotation) when flapping if (self.isRainbow) { // Stop any existing rotation tweens tween.stop(birdGraphics, { rotation: true }); // Store the current direction (positive or negative scaleX) var currentDirection = Math.sign(birdGraphics.scaleX); // Perform a complete 360-degree backflip tween(birdGraphics, { rotation: birdGraphics.rotation + currentDirection * Math.PI * 2 // Full rotation in the correct direction }, { duration: 500, // Half a second for the flip easing: tween.easeOut }); } }; // Update bird physics self.update = function () { // Store last position self.lastX = self.x; self.lastY = self.y; // Apply gravity self.velocityY += self.gravity; // Update position self.y += self.velocityY; self.x += self.velocityX; // Make sure hitArea is always centered on the bird (follows the bird's position) // No need to update position since the hitArea is relative to the bird's coordinate system // Use a more fair hitbox size - not too big, not too small self.hitArea = new Rectangle(-30, -30, 60, 60); // Visual feedback - rotate bird based on velocity var targetRotation = Math.min(Math.max(self.velocityY / 15, -0.5), 0.5); // Make bird face the direction it's flying by flipping the sprite if (self.velocityX > 0) { birdGraphics.scaleX = Math.abs(birdGraphics.scaleX); // Face right if (!self.isRainbow || !tween.has(birdGraphics, 'rotation')) { birdGraphics.rotation = targetRotation; // Apply rotation normally for right-facing bird } } else { birdGraphics.scaleX = -Math.abs(birdGraphics.scaleX); // Face left if (!self.isRainbow || !tween.has(birdGraphics, 'rotation')) { birdGraphics.rotation = -targetRotation; // Invert rotation for left-facing bird } } // Rainbow effect is now handled by the rainbow bird asset // We can still animate it slightly for visual effect if (self.isRainbow) { // Apply subtle visual effects for rainbow bird var pulseAmount = Math.sin(LK.ticks / 10) * 0.1; // Maintain the correct direction by checking current scaleX sign var scaleXDirection = Math.sign(birdGraphics.scaleX); var baseScale = 2.5; // Match the original scale set in constructor // Apply pulse effect while preserving direction birdGraphics.scale.set(scaleXDirection * (baseScale + pulseAmount), baseScale + pulseAmount); } }; return self; }); var Seed = Container.expand(function () { var self = Container.call(this); // Seed asset var seedGraphics = self.attachAsset('seed', { anchorX: 0.5, anchorY: 0.5, scaleX: 3.5, scaleY: 3.5 }); // Seed properties self.lastWasIntersecting = false; self.speedX = 0; // Set speed to 0 to stop seeds from moving // Animation effect self.update = function () { // No longer updating x position seedGraphics.rotation += 0.05; // Remove seeds that are off-screen if (self.x < -50) { self.shouldBeRemoved = true; } }; return self; }); var ShopScreen = Container.expand(function () { var self = Container.call(this); // Background panel var panel = self.attachAsset('shape', { width: 1800, height: 2200, color: 0x4682B4, shape: 'box', anchorX: 0.5, anchorY: 0.5 }); panel.alpha = 0.9; // Title var titleText = new Text2('SKIN SHOP', { size: 120, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0); titleText.x = 0; titleText.y = -950; self.addChild(titleText); // Coins display var coinsText = new Text2('Coins: ' + storage.coins, { size: 80, fill: 0xFFD700 }); coinsText.anchor.set(0.5, 0); coinsText.x = 0; coinsText.y = -800; self.addChild(coinsText); // Skins configuration var skins = [{ id: "bird", name: "Default Bird", price: 0, x: -500, y: -500 }, { id: "red_bird", name: "Red Bird", price: 20, x: 0, y: -500 }, { id: "gold_bird", name: "Gold Bird", price: 40, x: 500, y: -500 }, { id: "blue_bird", name: "Blue Bird", price: 60, x: -500, y: 0 }, { id: "green_bird", name: "Green Bird", price: 80, x: 0, y: 0 }, { id: "rainbow_bird", name: "Rainbow Bird", price: 100, x: 500, y: 0 }]; // Selected skin indicator var selectedIndicator = self.attachAsset('shape', { width: 350, height: 350, color: 0xFFD700, shape: 'box', anchorX: 0.5, anchorY: 0.5 }); selectedIndicator.alpha = 0.5; selectedIndicator.visible = false; // Skin buttons var skinButtons = []; for (var i = 0; i < skins.length; i++) { createSkinButton(skins[i]); } function createSkinButton(skin) { var button = new Container(); // Button background var bg = button.attachAsset('shape', { width: 300, height: 300, color: 0x87CEEB, shape: 'box', anchorX: 0.5, anchorY: 0.5 }); // Bird icon using the specific skin asset var birdIcon = button.attachAsset(skin.id, { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 2 }); // Set rainbow flag if needed if (skin.id === "rainbow_bird") { button.isRainbow = true; } // Price/status text var priceText = new Text2(skin.price === 0 ? "FREE" : skin.price + " coins", { size: 40, fill: 0xFFFFFF }); priceText.anchor.set(0.5, 0); priceText.y = 80; button.addChild(priceText); // Set button position button.x = skin.x; button.y = skin.y; button.skinId = skin.id; button.skinName = skin.name; button.price = skin.price; // Check if skin is unlocked var unlockedSkins = storage.unlockedSkins || ["bird"]; if (unlockedSkins.indexOf(skin.id) !== -1) { button.isUnlocked = true; priceText.setText("UNLOCKED"); } else { button.isUnlocked = false; } // Handle button click button.down = function (x, y, obj) { if (button.isUnlocked) { // Select this skin storage.selectedSkin = button.skinId; self.updateSelectedIndicator(); if (self.onSkinSelected) { self.onSkinSelected(button.skinId); } } else if (storage.coins >= button.price) { // Buy this skin storage.coins -= button.price; if (!storage.unlockedSkins) { storage.unlockedSkins = ["bird"]; } storage.unlockedSkins.push(button.skinId); button.isUnlocked = true; priceText.setText("UNLOCKED"); coinsText.setText("Coins: " + storage.coins); } }; self.addChild(button); skinButtons.push(button); return button; } // Close button var closeButton = new Container(); var closeBg = closeButton.attachAsset('shape', { width: 500, height: 120, color: 0xFF0000, shape: 'box', anchorX: 0.5, anchorY: 0.5 }); var closeText = new Text2('CLOSE', { size: 80, fill: 0xFFFFFF }); closeText.anchor.set(0.5, 0.5); closeButton.addChild(closeText); closeButton.x = 0; closeButton.y = 800; closeButton.down = function () { if (self.onClose) { self.onClose(); } }; self.addChild(closeButton); // Update which skin is selected self.updateSelectedIndicator = function () { var currentSkin = storage.selectedSkin || "bird"; for (var i = 0; i < skinButtons.length; i++) { if (skinButtons[i].skinId === currentSkin) { selectedIndicator.x = skinButtons[i].x; selectedIndicator.y = skinButtons[i].y; selectedIndicator.visible = true; break; } } }; // Position shop in the center self.x = gameWidth / 2; self.y = gameHeight / 2; // Update rainbow skin colors and selection indicator self.update = function () { // Rainbow effect animation for rainbow skin buttons for (var i = 0; i < skinButtons.length; i++) { if (skinButtons[i].isRainbow) { // Instead of tinting, apply a subtle scale animation var pulseAmount = Math.sin(LK.ticks / 10) * 0.1; // Find the bird icon within the button for (var j = 0; j < skinButtons[i].children.length; j++) { var child = skinButtons[i].children[j]; if (child.isAsset) { // Animate the rainbow bird icon with a subtle pulse child.scale.set(2 + pulseAmount, 2 + pulseAmount); break; } } } } }; // Add general down handler to prevent click propagation to elements below self.down = function (x, y, obj) { // Catch all clicks on shop screen that aren't caught by buttons return; }; // Initialize the selection indicator self.updateSelectedIndicator(); return self; }); var Spike = Container.expand(function () { var self = Container.call(this); // Create spike shape using a triangle var spikeGraphics = self.attachAsset('shape', { width: 200, height: 200, color: 0x0000FF, // Blue color for spike shape: 'box', anchorX: 0.5, anchorY: 0.5 }); // Rotate to make it look like a spike spikeGraphics.rotation = Math.PI / 4; // 45 degrees self.isStuck = false; self.wall = null; self.lastWasIntersecting = false; // Stick to a wall self.stickTo = function (wall) { self.isStuck = true; self.wall = wall; // Position on wall edge based on wall orientation if (wall.isVertical) { self.x = wall.x + (wall.x < 1024 ? 50 : -50); // Left or right wall - adjusted for wider walls // Keep y position where the collision happened } else { self.y = wall.y + (wall.y < 1366 ? 50 : -50); // Top or bottom wall - adjusted for taller walls // Keep x position where the collision happened } }; self.update = function () { // Nothing to update when stuck if (!self.isStuck) { // If not stuck, spike can move or be collected later self.rotation += 0.05; } }; return self; }); var StartScreen = Container.expand(function () { var self = Container.call(this); // Create title text var titleText = new Text2('BOUNCE BIRD', { size: 150, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.x = 2048 / 2; titleText.y = 2732 / 3; self.addChild(titleText); // Create start button var startButton = new Container(); var buttonBg = startButton.attachAsset('shape', { width: 500, height: 150, color: 0x4682B4, shape: 'box', anchorX: 0.5, anchorY: 0.5 }); var startText = new Text2('TAP TO START', { size: 80, fill: 0xFFFFFF }); startText.anchor.set(0.5, 0.5); startButton.addChild(startText); startButton.x = 2048 / 2; startButton.y = 2732 / 2; self.addChild(startButton); // Create shop button var shopButton = new Container(); var shopBg = shopButton.attachAsset('shape', { width: 300, height: 120, color: 0x4682B4, shape: 'box', anchorX: 0.5, anchorY: 0.5 }); var shopText = new Text2('SHOP', { size: 60, fill: 0xFFFFFF }); shopText.anchor.set(0.5, 0.5); shopButton.addChild(shopText); shopButton.x = 2048 / 2; shopButton.y = 2732 / 2 + 200; shopButton.down = function () { if (self.onShop) { self.onShop(); } }; self.addChild(shopButton); // Add a bouncing bird animation - use the selected skin if available var skinToShow = storage.selectedSkin || "bird"; var demoBird = self.attachAsset(skinToShow, { anchorX: 0.5, anchorY: 0.5, scaleX: 4, scaleY: 4, x: 2048 / 2, y: 2732 * 0.7 }); // Make the button pulse self.update = function () { var scale = 1 + Math.sin(LK.ticks / 20) * 0.05; startButton.scale.set(scale); // Make the bird bounce demoBird.y = 2732 * 0.7 + Math.sin(LK.ticks / 15) * 50; demoBird.rotation = Math.sin(LK.ticks / 30) * 0.2; }; // Handle tap event self.down = function () { if (self.onStart) { self.onStart(); } }; return self; }); var Wall = Container.expand(function () { var self = Container.call(this); // Wall properties self.isVertical = true; self.wallGraphics = null; // Initialize with wall type (vertical/horizontal) self.init = function (vertical) { self.isVertical = vertical; if (vertical) { self.wallGraphics = self.attachAsset('wall', { anchorX: 0.5, anchorY: 0.5 }); } else { self.wallGraphics = self.attachAsset('ceiling', { anchorX: 0.5, anchorY: 0.5 }); } return self; }; return self; }); /**** * Initialize Game ****/ // Change background color to sky blue var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Change background color to sky blue // Assets for Bounce Bird // Import tween plugin // Add custom method to check if object has active tweens for a property tween.has = function (target, property) { // This is a custom implementation since the tween plugin doesn't have this method // We'll return false to allow the rotation to be set manually return false; }; game.setBackgroundColor(0x87CEEB); // Game variables var bird; var walls = []; var seeds = []; var spikes = []; var score = 0; var gameWidth = 2048; var gameHeight = 2732; var gameStarted = false; var startScreen; var shopScreen; var shopOpen = false; var coinText; // Create start screen function showStartScreen() { startScreen = new StartScreen(); startScreen.onStart = function () { gameStarted = true; startScreen.destroy(); startScreen = null; startGame(); }; startScreen.onShop = function () { showShopScreen(); }; game.addChild(startScreen); } // Show shop screen function showShopScreen() { shopOpen = true; // If start screen exists, hide it temporarily if (startScreen) { startScreen.visible = false; } shopScreen = new ShopScreen(); // Add shop background to block events from propagating through var shopBlocker = new Container(); var blockerBg = shopBlocker.attachAsset('shape', { width: gameWidth, height: gameHeight, color: 0x000000, shape: 'box', anchorX: 0.5, anchorY: 0.5 }); blockerBg.alpha = 0.5; // More visible to ensure it blocks events shopBlocker.x = gameWidth / 2; shopBlocker.y = gameHeight / 2; // Add event handler to block events shopBlocker.down = function (x, y, obj) { // This function blocks events from propagating return; }; // Add blocker first, then shop on top game.addChild(shopBlocker); shopScreen.onClose = function () { shopOpen = false; shopScreen.destroy(); shopScreen = null; shopBlocker.destroy(); // Reset the game state when closing the shop gameStarted = false; // Reset game elements if they exist if (bird) { bird.destroy(); bird = null; } // Clear all game objects for (var i = 0; i < seeds.length; i++) { seeds[i].destroy(); } seeds = []; for (var i = 0; i < spikes.length; i++) { spikes[i].destroy(); } spikes = []; // Reset score score = 0; if (scoreText) { scoreText.setText("Score: 0"); } // Update coin display if it exists if (coinText) { coinText.setText("Coins: " + storage.coins); } // Show the start screen if (startScreen && !startScreen.visible) { startScreen.visible = true; } else { showStartScreen(); } }; shopScreen.onSkinSelected = function (skinId) { // This will be applied next time a bird is created }; game.addChild(shopScreen); } // Initialize the actual game function startGame() { createBird(); createWalls(); createScoreDisplay(); } // Create score text var scoreText; function createScoreDisplay() { scoreText = new Text2('Score: 0', { size: 80, fill: 0xFFFFFF }); scoreText.anchor.set(0, 0); LK.gui.topLeft.addChild(scoreText); scoreText.x = 120; // Avoid the top-left 100x100 menu area scoreText.y = 50; // Create coin text coinText = new Text2('Coins: ' + storage.coins, { size: 60, fill: 0xFFD700 }); coinText.anchor.set(0, 0); LK.gui.topLeft.addChild(coinText); coinText.x = 120; // Avoid the top-left 100x100 menu area coinText.y = 140; } // Create bird function createBird() { bird = new Bird(); bird.x = gameWidth / 4; bird.y = gameHeight / 2; game.addChild(bird); } // Create walls (top, bottom, left, right) function createWalls() { // Top wall (ceiling) var ceiling = new Wall().init(false); ceiling.x = gameWidth / 2; ceiling.y = 40; game.addChild(ceiling); walls.push(ceiling); // Bottom wall (floor) var floor = new Wall().init(false); floor.x = gameWidth / 2; floor.y = gameHeight - 40; game.addChild(floor); walls.push(floor); // Left wall var leftWall = new Wall().init(true); leftWall.x = 40; leftWall.y = gameHeight / 2; game.addChild(leftWall); walls.push(leftWall); // Right wall var rightWall = new Wall().init(true); rightWall.x = gameWidth - 40; rightWall.y = gameHeight / 2; game.addChild(rightWall); walls.push(rightWall); } // Show start screen instead of immediately starting the game showStartScreen(); // Spawn a new seed at a random position function spawnSeed() { var seed = new Seed(); seed.x = gameWidth + 50; seed.y = Math.random() * (gameHeight - 200) + 100; seeds.push(seed); game.addChild(seed); } // Handle click/tap anywhere on screen game.down = function (x, y, obj) { // First, check if shop is open - don't process any game inputs if (shopOpen) { // Don't handle game inputs when shop is open return; } // Add a shop button after game over if (!gameStarted && !startScreen) { // This means we're in the game over state showShopScreen(); return; } // Only allow bird flap if game has actually started if (gameStarted && bird) { bird.flap(); } }; // Listen for game over event to preserve our score LK.on('gameover', function () { // Game is over but we want to preserve score // This ensures score is saved when player dies scoreText.setText("Score: " + score); LK.setScore(score); }); // Update function game.update = function () { // Don't process game logic if game hasn't started yet or shop is open if (!gameStarted || shopOpen) { return; } // Seeds now spawn on bounce, not periodically // Update seeds for (var i = seeds.length - 1; i >= 0; i--) { var seed = seeds[i]; // Check for collision with bird var currentIntersecting = bird.intersects(seed); if (!seed.lastWasIntersecting && currentIntersecting) { // Collected seed score++; scoreText.setText("Score: " + score); LK.setScore(score); // Award a coin for each seed storage.coins += 1; coinText.setText("Coins: " + storage.coins); LK.getSound('collect').play(); // Remove seed seed.destroy(); seeds.splice(i, 1); } else { seed.lastWasIntersecting = currentIntersecting; // Remove seeds that should be removed if (seed.shouldBeRemoved) { seed.destroy(); seeds.splice(i, 1); } } } // Check for wall collisions for (var i = 0; i < walls.length; i++) { var wall = walls[i]; var currentIntersecting = bird.intersects(wall); if (!bird.lastWasIntersecting && currentIntersecting) { // Check if bird hit ceiling or floor (horizontal walls) if (!wall.isVertical) { // Bird hit ceiling or floor - game over! LK.effects.flashScreen(0xFF0000, 500); LK.showGameOver(); break; } else { // Bird hit a side wall - bounce! LK.getSound('bounce').play(); // Apply bounce physics bird.velocityX *= -1; // Reverse horizontal direction // Push bird away from wall to prevent sticking bird.x += bird.velocityX > 0 ? 5 : -5; // Visual feedback for bounce LK.effects.flashObject(bird, 0xFFFFFF, 200); // Clear old seeds and spikes on bounce for (var j = seeds.length - 1; j >= 0; j--) { seeds[j].destroy(); seeds.splice(j, 1); } // Clear all existing spikes for (var j = spikes.length - 1; j >= 0; j--) { spikes[j].destroy(); spikes.splice(j, 1); } // Reset all wall colors to the original blue color for (var j = 0; j < walls.length; j++) { if (walls[j].isVertical) { tween(walls[j].wallGraphics, { tint: 0x4682b4 }, { duration: 300, easing: tween.easeIn }); } } // Spawn two new seeds on bounce for (var k = 0; k < 2; k++) { var newSeed = new Seed(); // Make seeds spawn more in the middle - use narrower range centered around the middle var middleX = gameWidth / 2; var middleY = gameHeight / 2; var spawnWidth = gameWidth * 0.6; // Use 60% of screen width var spawnHeight = gameHeight * 0.6; // Use 60% of screen height newSeed.x = middleX + (Math.random() - 0.5) * spawnWidth; newSeed.y = middleY + (Math.random() - 0.5) * spawnHeight; seeds.push(newSeed); game.addChild(newSeed); } // Add a spike that sticks to the opposite wall var spike = new Spike(); // Place spike on opposite vertical wall targetWall = wall.x < gameWidth / 2 ? walls[3] : walls[2]; // walls[3] is right wall, walls[2] is left wall spike.x = targetWall.x; spike.y = Math.random() * (gameHeight - 200) + 100; // Random Y position spike.stickTo(targetWall); spikes.push(spike); game.addChild(spike); // Change the target wall color to match the spike color (blue) tween(targetWall.wallGraphics, { tint: 0x0000FF }, { duration: 300, easing: tween.easeOut }); } // Set lastWasIntersecting for the specific wall collision that just happened bird.lastWasIntersecting = true; break; // Exit the loop after handling a collision } } // Reset lastWasIntersecting if not intersecting any wall var intersectingAnyWall = false; for (var i = 0; i < walls.length; i++) { if (bird.intersects(walls[i])) { intersectingAnyWall = true; break; } } if (!intersectingAnyWall) { bird.lastWasIntersecting = false; } // Check for spike collisions for (var i = 0; i < spikes.length; i++) { var spike = spikes[i]; // Use bird's intersects method for collision detection var currentIntersection = bird.intersects(spike); // Use hit box collision instead of sprite collision if (currentIntersection) { // Bird hit a spike - game over! LK.effects.flashScreen(0xFF0000, 500); LK.showGameOver(); break; } spike.lastWasIntersecting = currentIntersection; } // Check if bird flew off screen (game over condition) if (bird.y < -100 || bird.y > gameHeight + 100 || bird.x < -100 || bird.x > gameWidth + 100) { LK.effects.flashScreen(0xFF0000, 500); LK.showGameOver(); } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
coins: 0,
selectedSkin: "bird",
unlockedSkins: ["bird"]
});
/****
* Classes
****/
var Bird = Container.expand(function () {
var self = Container.call(this);
// Get the selected skin
var selectedSkin = storage.selectedSkin || "bird";
// Bird asset - use the correct asset based on the selected skin
var birdGraphics = self.attachAsset(selectedSkin, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 2.5
});
// Add a bigger hitbox for collision detection while keeping visual size
self.hitArea = new Rectangle(-25, -25, 50, 50);
// Set special flag for rainbow bird
if (selectedSkin === "rainbow_bird") {
self.isRainbow = true; // Will be handled in update
}
// Bird physics properties
self.velocityY = 0;
self.velocityX = 12; // Increased from 8 to 12 for faster horizontal movement
self.gravity = 1.0; // Increased from 0.7 to 1.0 for stronger gravity
self.flapStrength = -20; // Increased from -15 to -20 for higher jumps
self.lastX = 0;
self.lastY = 0;
self.lastWasIntersecting = false;
// Handle tap/click on bird
self.down = function (x, y, obj) {
self.flap();
};
// Make bird flap wings
self.flap = function () {
self.velocityY = self.flapStrength;
// Play flap sound
LK.getSound('flap').play();
// Make rainbow bird do a backflip (360 degree rotation) when flapping
if (self.isRainbow) {
// Stop any existing rotation tweens
tween.stop(birdGraphics, {
rotation: true
});
// Store the current direction (positive or negative scaleX)
var currentDirection = Math.sign(birdGraphics.scaleX);
// Perform a complete 360-degree backflip
tween(birdGraphics, {
rotation: birdGraphics.rotation + currentDirection * Math.PI * 2 // Full rotation in the correct direction
}, {
duration: 500,
// Half a second for the flip
easing: tween.easeOut
});
}
};
// Update bird physics
self.update = function () {
// Store last position
self.lastX = self.x;
self.lastY = self.y;
// Apply gravity
self.velocityY += self.gravity;
// Update position
self.y += self.velocityY;
self.x += self.velocityX;
// Make sure hitArea is always centered on the bird (follows the bird's position)
// No need to update position since the hitArea is relative to the bird's coordinate system
// Use a more fair hitbox size - not too big, not too small
self.hitArea = new Rectangle(-30, -30, 60, 60);
// Visual feedback - rotate bird based on velocity
var targetRotation = Math.min(Math.max(self.velocityY / 15, -0.5), 0.5);
// Make bird face the direction it's flying by flipping the sprite
if (self.velocityX > 0) {
birdGraphics.scaleX = Math.abs(birdGraphics.scaleX); // Face right
if (!self.isRainbow || !tween.has(birdGraphics, 'rotation')) {
birdGraphics.rotation = targetRotation; // Apply rotation normally for right-facing bird
}
} else {
birdGraphics.scaleX = -Math.abs(birdGraphics.scaleX); // Face left
if (!self.isRainbow || !tween.has(birdGraphics, 'rotation')) {
birdGraphics.rotation = -targetRotation; // Invert rotation for left-facing bird
}
}
// Rainbow effect is now handled by the rainbow bird asset
// We can still animate it slightly for visual effect
if (self.isRainbow) {
// Apply subtle visual effects for rainbow bird
var pulseAmount = Math.sin(LK.ticks / 10) * 0.1;
// Maintain the correct direction by checking current scaleX sign
var scaleXDirection = Math.sign(birdGraphics.scaleX);
var baseScale = 2.5; // Match the original scale set in constructor
// Apply pulse effect while preserving direction
birdGraphics.scale.set(scaleXDirection * (baseScale + pulseAmount), baseScale + pulseAmount);
}
};
return self;
});
var Seed = Container.expand(function () {
var self = Container.call(this);
// Seed asset
var seedGraphics = self.attachAsset('seed', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3.5,
scaleY: 3.5
});
// Seed properties
self.lastWasIntersecting = false;
self.speedX = 0; // Set speed to 0 to stop seeds from moving
// Animation effect
self.update = function () {
// No longer updating x position
seedGraphics.rotation += 0.05;
// Remove seeds that are off-screen
if (self.x < -50) {
self.shouldBeRemoved = true;
}
};
return self;
});
var ShopScreen = Container.expand(function () {
var self = Container.call(this);
// Background panel
var panel = self.attachAsset('shape', {
width: 1800,
height: 2200,
color: 0x4682B4,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5
});
panel.alpha = 0.9;
// Title
var titleText = new Text2('SKIN SHOP', {
size: 120,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0);
titleText.x = 0;
titleText.y = -950;
self.addChild(titleText);
// Coins display
var coinsText = new Text2('Coins: ' + storage.coins, {
size: 80,
fill: 0xFFD700
});
coinsText.anchor.set(0.5, 0);
coinsText.x = 0;
coinsText.y = -800;
self.addChild(coinsText);
// Skins configuration
var skins = [{
id: "bird",
name: "Default Bird",
price: 0,
x: -500,
y: -500
}, {
id: "red_bird",
name: "Red Bird",
price: 20,
x: 0,
y: -500
}, {
id: "gold_bird",
name: "Gold Bird",
price: 40,
x: 500,
y: -500
}, {
id: "blue_bird",
name: "Blue Bird",
price: 60,
x: -500,
y: 0
}, {
id: "green_bird",
name: "Green Bird",
price: 80,
x: 0,
y: 0
}, {
id: "rainbow_bird",
name: "Rainbow Bird",
price: 100,
x: 500,
y: 0
}];
// Selected skin indicator
var selectedIndicator = self.attachAsset('shape', {
width: 350,
height: 350,
color: 0xFFD700,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5
});
selectedIndicator.alpha = 0.5;
selectedIndicator.visible = false;
// Skin buttons
var skinButtons = [];
for (var i = 0; i < skins.length; i++) {
createSkinButton(skins[i]);
}
function createSkinButton(skin) {
var button = new Container();
// Button background
var bg = button.attachAsset('shape', {
width: 300,
height: 300,
color: 0x87CEEB,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5
});
// Bird icon using the specific skin asset
var birdIcon = button.attachAsset(skin.id, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2
});
// Set rainbow flag if needed
if (skin.id === "rainbow_bird") {
button.isRainbow = true;
}
// Price/status text
var priceText = new Text2(skin.price === 0 ? "FREE" : skin.price + " coins", {
size: 40,
fill: 0xFFFFFF
});
priceText.anchor.set(0.5, 0);
priceText.y = 80;
button.addChild(priceText);
// Set button position
button.x = skin.x;
button.y = skin.y;
button.skinId = skin.id;
button.skinName = skin.name;
button.price = skin.price;
// Check if skin is unlocked
var unlockedSkins = storage.unlockedSkins || ["bird"];
if (unlockedSkins.indexOf(skin.id) !== -1) {
button.isUnlocked = true;
priceText.setText("UNLOCKED");
} else {
button.isUnlocked = false;
}
// Handle button click
button.down = function (x, y, obj) {
if (button.isUnlocked) {
// Select this skin
storage.selectedSkin = button.skinId;
self.updateSelectedIndicator();
if (self.onSkinSelected) {
self.onSkinSelected(button.skinId);
}
} else if (storage.coins >= button.price) {
// Buy this skin
storage.coins -= button.price;
if (!storage.unlockedSkins) {
storage.unlockedSkins = ["bird"];
}
storage.unlockedSkins.push(button.skinId);
button.isUnlocked = true;
priceText.setText("UNLOCKED");
coinsText.setText("Coins: " + storage.coins);
}
};
self.addChild(button);
skinButtons.push(button);
return button;
}
// Close button
var closeButton = new Container();
var closeBg = closeButton.attachAsset('shape', {
width: 500,
height: 120,
color: 0xFF0000,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5
});
var closeText = new Text2('CLOSE', {
size: 80,
fill: 0xFFFFFF
});
closeText.anchor.set(0.5, 0.5);
closeButton.addChild(closeText);
closeButton.x = 0;
closeButton.y = 800;
closeButton.down = function () {
if (self.onClose) {
self.onClose();
}
};
self.addChild(closeButton);
// Update which skin is selected
self.updateSelectedIndicator = function () {
var currentSkin = storage.selectedSkin || "bird";
for (var i = 0; i < skinButtons.length; i++) {
if (skinButtons[i].skinId === currentSkin) {
selectedIndicator.x = skinButtons[i].x;
selectedIndicator.y = skinButtons[i].y;
selectedIndicator.visible = true;
break;
}
}
};
// Position shop in the center
self.x = gameWidth / 2;
self.y = gameHeight / 2;
// Update rainbow skin colors and selection indicator
self.update = function () {
// Rainbow effect animation for rainbow skin buttons
for (var i = 0; i < skinButtons.length; i++) {
if (skinButtons[i].isRainbow) {
// Instead of tinting, apply a subtle scale animation
var pulseAmount = Math.sin(LK.ticks / 10) * 0.1;
// Find the bird icon within the button
for (var j = 0; j < skinButtons[i].children.length; j++) {
var child = skinButtons[i].children[j];
if (child.isAsset) {
// Animate the rainbow bird icon with a subtle pulse
child.scale.set(2 + pulseAmount, 2 + pulseAmount);
break;
}
}
}
}
};
// Add general down handler to prevent click propagation to elements below
self.down = function (x, y, obj) {
// Catch all clicks on shop screen that aren't caught by buttons
return;
};
// Initialize the selection indicator
self.updateSelectedIndicator();
return self;
});
var Spike = Container.expand(function () {
var self = Container.call(this);
// Create spike shape using a triangle
var spikeGraphics = self.attachAsset('shape', {
width: 200,
height: 200,
color: 0x0000FF,
// Blue color for spike
shape: 'box',
anchorX: 0.5,
anchorY: 0.5
});
// Rotate to make it look like a spike
spikeGraphics.rotation = Math.PI / 4; // 45 degrees
self.isStuck = false;
self.wall = null;
self.lastWasIntersecting = false;
// Stick to a wall
self.stickTo = function (wall) {
self.isStuck = true;
self.wall = wall;
// Position on wall edge based on wall orientation
if (wall.isVertical) {
self.x = wall.x + (wall.x < 1024 ? 50 : -50); // Left or right wall - adjusted for wider walls
// Keep y position where the collision happened
} else {
self.y = wall.y + (wall.y < 1366 ? 50 : -50); // Top or bottom wall - adjusted for taller walls
// Keep x position where the collision happened
}
};
self.update = function () {
// Nothing to update when stuck
if (!self.isStuck) {
// If not stuck, spike can move or be collected later
self.rotation += 0.05;
}
};
return self;
});
var StartScreen = Container.expand(function () {
var self = Container.call(this);
// Create title text
var titleText = new Text2('BOUNCE BIRD', {
size: 150,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 2048 / 2;
titleText.y = 2732 / 3;
self.addChild(titleText);
// Create start button
var startButton = new Container();
var buttonBg = startButton.attachAsset('shape', {
width: 500,
height: 150,
color: 0x4682B4,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5
});
var startText = new Text2('TAP TO START', {
size: 80,
fill: 0xFFFFFF
});
startText.anchor.set(0.5, 0.5);
startButton.addChild(startText);
startButton.x = 2048 / 2;
startButton.y = 2732 / 2;
self.addChild(startButton);
// Create shop button
var shopButton = new Container();
var shopBg = shopButton.attachAsset('shape', {
width: 300,
height: 120,
color: 0x4682B4,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5
});
var shopText = new Text2('SHOP', {
size: 60,
fill: 0xFFFFFF
});
shopText.anchor.set(0.5, 0.5);
shopButton.addChild(shopText);
shopButton.x = 2048 / 2;
shopButton.y = 2732 / 2 + 200;
shopButton.down = function () {
if (self.onShop) {
self.onShop();
}
};
self.addChild(shopButton);
// Add a bouncing bird animation - use the selected skin if available
var skinToShow = storage.selectedSkin || "bird";
var demoBird = self.attachAsset(skinToShow, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4,
scaleY: 4,
x: 2048 / 2,
y: 2732 * 0.7
});
// Make the button pulse
self.update = function () {
var scale = 1 + Math.sin(LK.ticks / 20) * 0.05;
startButton.scale.set(scale);
// Make the bird bounce
demoBird.y = 2732 * 0.7 + Math.sin(LK.ticks / 15) * 50;
demoBird.rotation = Math.sin(LK.ticks / 30) * 0.2;
};
// Handle tap event
self.down = function () {
if (self.onStart) {
self.onStart();
}
};
return self;
});
var Wall = Container.expand(function () {
var self = Container.call(this);
// Wall properties
self.isVertical = true;
self.wallGraphics = null;
// Initialize with wall type (vertical/horizontal)
self.init = function (vertical) {
self.isVertical = vertical;
if (vertical) {
self.wallGraphics = self.attachAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
});
} else {
self.wallGraphics = self.attachAsset('ceiling', {
anchorX: 0.5,
anchorY: 0.5
});
}
return self;
};
return self;
});
/****
* Initialize Game
****/
// Change background color to sky blue
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Change background color to sky blue
// Assets for Bounce Bird
// Import tween plugin
// Add custom method to check if object has active tweens for a property
tween.has = function (target, property) {
// This is a custom implementation since the tween plugin doesn't have this method
// We'll return false to allow the rotation to be set manually
return false;
};
game.setBackgroundColor(0x87CEEB);
// Game variables
var bird;
var walls = [];
var seeds = [];
var spikes = [];
var score = 0;
var gameWidth = 2048;
var gameHeight = 2732;
var gameStarted = false;
var startScreen;
var shopScreen;
var shopOpen = false;
var coinText;
// Create start screen
function showStartScreen() {
startScreen = new StartScreen();
startScreen.onStart = function () {
gameStarted = true;
startScreen.destroy();
startScreen = null;
startGame();
};
startScreen.onShop = function () {
showShopScreen();
};
game.addChild(startScreen);
}
// Show shop screen
function showShopScreen() {
shopOpen = true;
// If start screen exists, hide it temporarily
if (startScreen) {
startScreen.visible = false;
}
shopScreen = new ShopScreen();
// Add shop background to block events from propagating through
var shopBlocker = new Container();
var blockerBg = shopBlocker.attachAsset('shape', {
width: gameWidth,
height: gameHeight,
color: 0x000000,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5
});
blockerBg.alpha = 0.5; // More visible to ensure it blocks events
shopBlocker.x = gameWidth / 2;
shopBlocker.y = gameHeight / 2;
// Add event handler to block events
shopBlocker.down = function (x, y, obj) {
// This function blocks events from propagating
return;
};
// Add blocker first, then shop on top
game.addChild(shopBlocker);
shopScreen.onClose = function () {
shopOpen = false;
shopScreen.destroy();
shopScreen = null;
shopBlocker.destroy();
// Reset the game state when closing the shop
gameStarted = false;
// Reset game elements if they exist
if (bird) {
bird.destroy();
bird = null;
}
// Clear all game objects
for (var i = 0; i < seeds.length; i++) {
seeds[i].destroy();
}
seeds = [];
for (var i = 0; i < spikes.length; i++) {
spikes[i].destroy();
}
spikes = [];
// Reset score
score = 0;
if (scoreText) {
scoreText.setText("Score: 0");
}
// Update coin display if it exists
if (coinText) {
coinText.setText("Coins: " + storage.coins);
}
// Show the start screen
if (startScreen && !startScreen.visible) {
startScreen.visible = true;
} else {
showStartScreen();
}
};
shopScreen.onSkinSelected = function (skinId) {
// This will be applied next time a bird is created
};
game.addChild(shopScreen);
}
// Initialize the actual game
function startGame() {
createBird();
createWalls();
createScoreDisplay();
}
// Create score text
var scoreText;
function createScoreDisplay() {
scoreText = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreText.anchor.set(0, 0);
LK.gui.topLeft.addChild(scoreText);
scoreText.x = 120; // Avoid the top-left 100x100 menu area
scoreText.y = 50;
// Create coin text
coinText = new Text2('Coins: ' + storage.coins, {
size: 60,
fill: 0xFFD700
});
coinText.anchor.set(0, 0);
LK.gui.topLeft.addChild(coinText);
coinText.x = 120; // Avoid the top-left 100x100 menu area
coinText.y = 140;
}
// Create bird
function createBird() {
bird = new Bird();
bird.x = gameWidth / 4;
bird.y = gameHeight / 2;
game.addChild(bird);
}
// Create walls (top, bottom, left, right)
function createWalls() {
// Top wall (ceiling)
var ceiling = new Wall().init(false);
ceiling.x = gameWidth / 2;
ceiling.y = 40;
game.addChild(ceiling);
walls.push(ceiling);
// Bottom wall (floor)
var floor = new Wall().init(false);
floor.x = gameWidth / 2;
floor.y = gameHeight - 40;
game.addChild(floor);
walls.push(floor);
// Left wall
var leftWall = new Wall().init(true);
leftWall.x = 40;
leftWall.y = gameHeight / 2;
game.addChild(leftWall);
walls.push(leftWall);
// Right wall
var rightWall = new Wall().init(true);
rightWall.x = gameWidth - 40;
rightWall.y = gameHeight / 2;
game.addChild(rightWall);
walls.push(rightWall);
}
// Show start screen instead of immediately starting the game
showStartScreen();
// Spawn a new seed at a random position
function spawnSeed() {
var seed = new Seed();
seed.x = gameWidth + 50;
seed.y = Math.random() * (gameHeight - 200) + 100;
seeds.push(seed);
game.addChild(seed);
}
// Handle click/tap anywhere on screen
game.down = function (x, y, obj) {
// First, check if shop is open - don't process any game inputs
if (shopOpen) {
// Don't handle game inputs when shop is open
return;
}
// Add a shop button after game over
if (!gameStarted && !startScreen) {
// This means we're in the game over state
showShopScreen();
return;
}
// Only allow bird flap if game has actually started
if (gameStarted && bird) {
bird.flap();
}
};
// Listen for game over event to preserve our score
LK.on('gameover', function () {
// Game is over but we want to preserve score
// This ensures score is saved when player dies
scoreText.setText("Score: " + score);
LK.setScore(score);
});
// Update function
game.update = function () {
// Don't process game logic if game hasn't started yet or shop is open
if (!gameStarted || shopOpen) {
return;
}
// Seeds now spawn on bounce, not periodically
// Update seeds
for (var i = seeds.length - 1; i >= 0; i--) {
var seed = seeds[i];
// Check for collision with bird
var currentIntersecting = bird.intersects(seed);
if (!seed.lastWasIntersecting && currentIntersecting) {
// Collected seed
score++;
scoreText.setText("Score: " + score);
LK.setScore(score);
// Award a coin for each seed
storage.coins += 1;
coinText.setText("Coins: " + storage.coins);
LK.getSound('collect').play();
// Remove seed
seed.destroy();
seeds.splice(i, 1);
} else {
seed.lastWasIntersecting = currentIntersecting;
// Remove seeds that should be removed
if (seed.shouldBeRemoved) {
seed.destroy();
seeds.splice(i, 1);
}
}
}
// Check for wall collisions
for (var i = 0; i < walls.length; i++) {
var wall = walls[i];
var currentIntersecting = bird.intersects(wall);
if (!bird.lastWasIntersecting && currentIntersecting) {
// Check if bird hit ceiling or floor (horizontal walls)
if (!wall.isVertical) {
// Bird hit ceiling or floor - game over!
LK.effects.flashScreen(0xFF0000, 500);
LK.showGameOver();
break;
} else {
// Bird hit a side wall - bounce!
LK.getSound('bounce').play();
// Apply bounce physics
bird.velocityX *= -1; // Reverse horizontal direction
// Push bird away from wall to prevent sticking
bird.x += bird.velocityX > 0 ? 5 : -5;
// Visual feedback for bounce
LK.effects.flashObject(bird, 0xFFFFFF, 200);
// Clear old seeds and spikes on bounce
for (var j = seeds.length - 1; j >= 0; j--) {
seeds[j].destroy();
seeds.splice(j, 1);
}
// Clear all existing spikes
for (var j = spikes.length - 1; j >= 0; j--) {
spikes[j].destroy();
spikes.splice(j, 1);
}
// Reset all wall colors to the original blue color
for (var j = 0; j < walls.length; j++) {
if (walls[j].isVertical) {
tween(walls[j].wallGraphics, {
tint: 0x4682b4
}, {
duration: 300,
easing: tween.easeIn
});
}
}
// Spawn two new seeds on bounce
for (var k = 0; k < 2; k++) {
var newSeed = new Seed();
// Make seeds spawn more in the middle - use narrower range centered around the middle
var middleX = gameWidth / 2;
var middleY = gameHeight / 2;
var spawnWidth = gameWidth * 0.6; // Use 60% of screen width
var spawnHeight = gameHeight * 0.6; // Use 60% of screen height
newSeed.x = middleX + (Math.random() - 0.5) * spawnWidth;
newSeed.y = middleY + (Math.random() - 0.5) * spawnHeight;
seeds.push(newSeed);
game.addChild(newSeed);
}
// Add a spike that sticks to the opposite wall
var spike = new Spike();
// Place spike on opposite vertical wall
targetWall = wall.x < gameWidth / 2 ? walls[3] : walls[2]; // walls[3] is right wall, walls[2] is left wall
spike.x = targetWall.x;
spike.y = Math.random() * (gameHeight - 200) + 100; // Random Y position
spike.stickTo(targetWall);
spikes.push(spike);
game.addChild(spike);
// Change the target wall color to match the spike color (blue)
tween(targetWall.wallGraphics, {
tint: 0x0000FF
}, {
duration: 300,
easing: tween.easeOut
});
}
// Set lastWasIntersecting for the specific wall collision that just happened
bird.lastWasIntersecting = true;
break; // Exit the loop after handling a collision
}
}
// Reset lastWasIntersecting if not intersecting any wall
var intersectingAnyWall = false;
for (var i = 0; i < walls.length; i++) {
if (bird.intersects(walls[i])) {
intersectingAnyWall = true;
break;
}
}
if (!intersectingAnyWall) {
bird.lastWasIntersecting = false;
}
// Check for spike collisions
for (var i = 0; i < spikes.length; i++) {
var spike = spikes[i];
// Use bird's intersects method for collision detection
var currentIntersection = bird.intersects(spike);
// Use hit box collision instead of sprite collision
if (currentIntersection) {
// Bird hit a spike - game over!
LK.effects.flashScreen(0xFF0000, 500);
LK.showGameOver();
break;
}
spike.lastWasIntersecting = currentIntersection;
}
// Check if bird flew off screen (game over condition)
if (bird.y < -100 || bird.y > gameHeight + 100 || bird.x < -100 || bird.x > gameWidth + 100) {
LK.effects.flashScreen(0xFF0000, 500);
LK.showGameOver();
}
};
Big sunflower seed. In-Game asset. 2d. High contrast. No shadows
Square shaped gold bird. In-Game asset. 2d. High contrast. No shadows
Square shaped green bird facing to the right. In-Game asset. 2d. High contrast. No shadows
Square shaped orange bird facing right
Square shaped red bird facing right. In-Game asset. 2d. High contrast. No shadows
Square shaped rainbow bird facing right. In-Game asset. 2d. High contrast. No shadows