User prompt
Instead of the wires moving, the wires should now remain stationary. The nests will be the moving elements, traversing horizontally along the static power lines. The initial placement of these nests on each wire should be random. However, there should be a constraint on the number of nests per wire: each wire should have a maximum of 3 nests and a minimum of 1 nest."
User prompt
The nests on each individual power line should move horizontally together in a synchronized and fixed manner. All nests on the same wire will oscillate left and right at the same speed and remain at a constant relative distance from each other. The speed of this horizontal movement should be increased. Upon reaching the left or right screen boundaries, the nests should immediately reverse their direction and continue moving in the opposite way."
User prompt
The bird should only perform one jump (a dash upwards) per mouse click." "It shouldn't continue jumping repeatedly with each click. One click, one jump." "The bird should only be able to jump again after it has landed on a nest." "The ability to jump should be disabled until the bird successfully lands on a nest." "We need to implement a cooldown or a landing check before the bird can jump again."
User prompt
"The bird still isn't jumping high enough even after the previous adjustments." "The current jump height remains insufficient for the bird to reach the next nest." "We need to further increase the bird's jump power or duration." "The upward force applied during the jump might need to be stronger." "Perhaps adjusting the gravity or the bird's mass could also influence the jump height." "Let's try increasing the initial upward velocity of the bird when it jumps." "Maybe we should consider a longer jump duration to allow the bird to gain more height."
User prompt
"The bird isn't jumping high enough to reach the nest on the wire above." "The jump height of the bird is insufficient to land on the next nest." "The bird's jump needs to be increased so it can reach the higher nests." "Currently, the bird's upward jump doesn't allow it to settle on the nest above."
User prompt
"Once the first nest touches a wall, it should start moving in the opposite direction." "The initial nest should reverse its movement direction upon colliding with a screen edge." "When the first nest reaches the left or right boundary, its horizontal movement should be inverted."
User prompt
"The nests are going off-screen to the left and right, as if there are no walls. How can I prevent this?" Answer in English (based on the solution above): "To prevent the nests from moving off-screen, you need to implement boundary collision detection for their horizontal movement. Here's the logic you should follow: Determine Screen Boundaries: Get the coordinates of the left and right edges of your game screen. Track Nest Positions: Keep track of the horizontal (x) position of each nest. Check for Collision: In each frame of your game, check if any nest's x-position has reached or gone beyond the screen boundaries. Reverse Movement: If a nest's x-position is less than or equal to the left screen edge, change its horizontal movement direction to the right. If a nest's x-position is greater than or equal to the right screen edge, change its horizontal movement direction to the left."
User prompt
In the game, the left and right edges of the screen should function as solid walls. When the nests reach these boundaries, they should reverse their horizontal movement direction and travel back in the opposite way
User prompt
ekranın sağı ve sol çerçeveli olsun duvar gibi
User prompt
ekranın sağı ve solu sınır olsun oraya ulaştıktan yuvalar geri dönsün
User prompt
The game starts with a single bird instance present on the screen. Vertically stacked power lines are displayed, extending upwards. Each power line will have one bird's nest positioned randomly along its length. These power lines will oscillate horizontally between the left and right screen boundaries at a defined speed. The player's objective is to navigate the bird upwards, landing it on the bird's nest located on the immediately higher power line. Player input is achieved through mouse clicks. A mouse click initiates a jump action, propelling the bird upwards. The jump height should be precisely calibrated to allow the bird to reach the next power line. Failure to land the bird on a nest on the ascending jump will result in the bird falling downwards due to gravity. If the falling bird does not collide with a nest on a lower power line, it will be considered 'dead', ending the game or triggering a 'game over' state. As the player successfully maneuvers the bird upwards to higher nests, the game screen will scroll vertically upwards. Consequently, the lower power lines will gradually move out of the player's view and disappear. Upon each successful mouse click that initiates a jump, a visual effect of small stars emanating from the bird's position should be triggered, providing visual feedback to the player. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
Code edit (1 edits merged)
Please save this source code
User prompt
WireFlyer - Nest Hopper
Initial prompt
Initially, we will have a bird, and there will be power lines arranged vertically upwards on the screen. On these lines, there will be a bird's nest at a random position. These lines will move left and right at a certain speed. Our goal is to try and land our bird on the bird's nest on the wire above by clicking the mouse. If we fail to land on the nest, the bird will fall downwards. If it doesn't land on a nest below, it will fall and die. As we move the bird upwards, the screen will scroll upwards, and the lower wires will gradually disappear. When we click the mouse on the bird, small stars will appear around it
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Bird = Container.expand(function () { var self = Container.call(this); var birdGraphic = self.attachAsset('bird', { anchorX: 0.5, anchorY: 0.5 }); self.velocity = 0; self.gravity = 0.5; self.jumpForce = -20; self.flying = false; self.onWire = true; self.currentNest = null; self.isJumpAllowed = true; self.jump = function () { // Only jump if allowed (when on a nest) if (!self.isJumpAllowed) return; // All jumps handled the same way - no special first jump anymore if (self.onWire) { self.onWire = false; // Bird leaves wire } // Normal jump physics for all jumps self.velocity = self.jumpForce; self.flying = true; // Mark game as started on first jump if (!game.gameStarted) { game.gameStarted = true; // Store safe landing height to prevent immediate game over game.safeJumpHeight = self.y + 300; // Room for falling before game over // Start continuous scrolling game.isScrolling = true; } // Disable jumping until landing on a nest again self.isJumpAllowed = false; // Create stars effect self.createStars(); // Play flying sound LK.getSound('fly').play(); }; self.createStars = function () { for (var i = 0; i < 5; i++) { var star = LK.getAsset('star', { anchorX: 0.5, anchorY: 0.5, x: self.x + (Math.random() * 60 - 30), y: self.y + (Math.random() * 60 - 30), alpha: 1 }); game.addChild(star); // Animate the star tween(star, { alpha: 0, scaleX: 0.2, scaleY: 0.2, x: star.x + (Math.random() * 40 - 20), y: star.y + (Math.random() * 40 - 20) }, { duration: 500, onFinish: function onFinish() { game.removeChild(star); } }); } }; self.update = function () { if (!self.onWire) { // Always apply gravity when bird is flying (both first jump and subsequent jumps) if (self.flying) { self.velocity += self.gravity; self.y += self.velocity; // Check if falling too fast if (self.velocity > 20) { self.velocity = 20; } } } else if (self.currentNest) { // Bird follows the nest self.x = self.currentNest.x; if (self.currentNest.y !== undefined) { self.y = self.currentNest.y - 30; } } else if (self.onWire) { // Safety check: if bird is somehow on wire but has no nest // Find closest nest or place in the center of screen // Don't immediately set flying to true to prevent immediate fall if (self.currentNest === null) { // Try to find the nearest nest var nearestNest = null; var minDistance = Infinity; for (var i = 0; i < nests.length; i++) { var distance = Math.abs(nests[i].x - self.x); if (distance < minDistance) { minDistance = distance; nearestNest = nests[i]; } } if (nearestNest) { self.currentNest = nearestNest; nearestNest.occupied = true; } else { self.onWire = false; self.flying = true; self.velocity = 0; } } } }; self.down = function (x, y, obj) { self.jump(); }; return self; }); var Nest = Container.expand(function () { var self = Container.call(this); var nestGraphic = self.attachAsset('nest', { anchorX: 0.5, anchorY: 0.5 }); self.occupied = false; self.lastX = undefined; self.speed = 4; // Speed for horizontal movement self.direction = 1; // Direction for horizontal movement // Randomize initial direction self.randomizeDirection = function () { self.direction = Math.random() > 0.5 ? 1 : -1; }; // Update method to handle movement and collisions self.update = function () { // Track last position for reference if (self.lastX === undefined) self.lastX = self.x; // Move nest horizontally self.x += self.speed * self.direction; // Check for wall collisions and reverse direction var wallWidth = 30; // Width of the wall assets var nestWidth = 100; // Width of nest if (self.x <= wallWidth + nestWidth / 2) { self.direction = 1; // Move right when hitting left wall self.x = wallWidth + nestWidth / 2; } else if (self.x >= 2048 - wallWidth - nestWidth / 2) { self.direction = -1; // Move left when hitting right wall self.x = 2048 - wallWidth - nestWidth / 2; } // Update last position self.lastX = self.x; }; // Initialize random direction self.randomizeDirection(); return self; }); var PowerLine = Container.expand(function () { var self = Container.call(this); var lineGraphic = self.attachAsset('powerLine', { anchorX: 0, anchorY: 0.5 }); // Power lines are static - no update method needed return self; }); var Star = Container.expand(function () { var self = Container.call(this); var starGraphic = self.attachAsset('star', { anchorX: 0.5, anchorY: 0.5 }); return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ var score = 0; var highScore = storage.highScore || 0; var powerLines = []; var nests = []; var bird; var cameraY = 0; var gameOver = false; var lineSpacing = 300; var maxLines = 10; var nextLineY = 2500; var ascentCount = 0; var lastNestLevel = null; var removedBottomLines = 0; // Initialize score display var scoreTxt = new Text2('Score: 0', { size: 60, fill: 0xFFFFFF }); scoreTxt.anchor.set(0, 0); LK.gui.topRight.addChild(scoreTxt); scoreTxt.x = -scoreTxt.width - 20; scoreTxt.y = 20; // Initialize high score display var highScoreTxt = new Text2('Best: ' + highScore, { size: 40, fill: 0xFFFFFF }); highScoreTxt.anchor.set(0, 0); LK.gui.topRight.addChild(highScoreTxt); highScoreTxt.x = -highScoreTxt.width - 20; highScoreTxt.y = 100; // Create background var background = LK.getAsset('background', { anchorX: 0, anchorY: 0 }); game.addChild(background); // Initialize bird function initBird() { bird = new Bird(); bird.x = 2048 / 2; bird.y = 2500; game.addChild(bird); // Bird will be positioned on a nest in initGame } // Create a power line with nests function createPowerLine(y) { var powerLine = new PowerLine(); powerLine.y = y; powerLine.initialY = y; // Store initial Y position // Set power line to span the whole width, respecting wall boundaries powerLine.x = 30; // Starting from left wall position game.addChild(powerLine); powerLines.push(powerLine); // Calculate nest properties once - more efficient var nestCount = Math.floor(Math.random() * 3) + 1; // Ensure at least one nest on the bottommost power line if (y === 2500) { nestCount = Math.max(nestCount, 2); // Guarantee at least two nests on bottom line for better gameplay } var nestWidth = 100; var wallWidth = 30; // Width of the wall assets var availableWidth = 2048 - nestWidth - wallWidth * 2; // Account for walls // Pre-calculate nest positions for better performance var nestPositions = []; for (var i = 0; i < nestCount; i++) { // If this is the bottom line, ensure at least one nest is near the center var nestX = y === 2500 && i === 0 ? 2048 / 2 // Center the first nest on bottom line : wallWidth + nestWidth / 2 + Math.random() * (availableWidth - nestWidth); nestPositions.push({ x: nestX, y: y - 5 }); } // Add nests in a small timeout to prevent UI blocking LK.setTimeout(function () { // Place nests randomly along the power line for (var i = 0; i < nestCount; i++) { var nest = new Nest(); // Random position along the power line nest.x = nestPositions[i].x; nest.y = nestPositions[i].y; // Slightly above the line nest.initialY = y - 5; // Store initial Y position game.addChild(nest); nests.push(nest); } }, 0); // Even a 0ms timeout helps by deferring to next event loop return powerLine; } // Initialize power lines function initPowerLines() { // Clear existing lines and nests - optimize by checking length first if (powerLines.length > 0) { for (var i = 0; i < powerLines.length; i++) { game.removeChild(powerLines[i]); } } if (nests.length > 0) { for (var i = 0; i < nests.length; i++) { game.removeChild(nests[i]); } } powerLines = []; nests = []; // Create initial power lines - create in batches for better performance nextLineY = 2500; // Create lines in smaller batches to prevent blocking UI var linesBatch = 3; var currentLine = 0; function createLineBatch() { var batchEnd = Math.min(currentLine + linesBatch, maxLines); for (var i = currentLine; i < batchEnd; i++) { createPowerLine(nextLineY); nextLineY -= lineSpacing; } currentLine = batchEnd; if (currentLine < maxLines) { LK.setTimeout(createLineBatch, 1); } } createLineBatch(); } // Initialize the game function initGame() { score = 0; gameOver = false; game.gameStarted = false; // Flag to track if game has started (first jump) game.isFirstJump = false; // No longer needed as we use tween for first jump game.birdHighestPosition = null; // Track bird's highest (lowest y value) position game.safeJumpHeight = null; // Will be set on first jump ascentCount = 0; lastNestLevel = null; removedBottomLines = 0; updateScore(0); // Set canvas size at the beginning only cameraY = 0; // Reset camera position game.cameraOffsetY = 0; // Smoothed camera offset for rendering game.cameraTargetY = 0; // Target camera position game.cameraSmoothness = 0.1; // How quickly camera follows (0-1) game.scrollSpeed = 1; // Base scroll speed game.jumpCount = 0; // Count of successful jumps game.isScrolling = false; // Whether continuous scrolling is active // Defer heavy initialization with a small timeout to allow the UI to render first LK.setTimeout(function () { initPowerLines(); initBird(); // Create walls at screen edges createWalls(); // Give time for nests to be created, then place bird LK.setTimeout(function () { // Find a nest on the bottommost line and place bird on it var bottomNest = null; var bottomY = 0; // First identify the bottommost line by finding the nest with largest Y value for (var i = 0; i < nests.length; i++) { if (nests[i].y > bottomY) { bottomY = nests[i].y; bottomNest = nests[i]; } } // If we found a bottom nest, place bird on it if (bottomNest) { bird.currentNest = bottomNest; bird.x = bottomNest.x; bird.y = bottomNest.y - 30; bird.isJumpAllowed = true; bird.onWire = true; bottomNest.occupied = true; } else { // Ensure bird is properly placed on the bottommost power line if no nest was found var bottomLine = null; var maxY = -Infinity; for (var i = 0; i < powerLines.length; i++) { if (powerLines[i].initialY > maxY) { maxY = powerLines[i].initialY; bottomLine = powerLines[i]; } } if (bottomLine) { // Create a nest on the bottom line var nest = new Nest(); nest.x = 2048 / 2; // Center of screen nest.y = bottomLine.y - 5; // Slightly above the line nest.initialY = bottomLine.initialY - 5; game.addChild(nest); nests.push(nest); // Place bird on this nest bird.currentNest = nest; bird.x = nest.x; bird.y = nest.y - 30; bird.isJumpAllowed = true; bird.onWire = true; nest.occupied = true; } } // Play background music LK.playMusic('gameMusic'); }, 100); // A bit more delay to ensure nests are created }, 50); // Short delay to let the UI render first } // Update score display function updateScore(newScore) { score = newScore; scoreTxt.setText('Score: ' + score); if (score > highScore) { highScore = score; storage.highScore = highScore; highScoreTxt.setText('Best: ' + highScore); } } // Check if bird is on a nest function checkNestCollision() { if (bird.velocity > 0) { // Only check when bird is falling for (var i = 0; i < nests.length; i++) { var nest = nests[i]; if (!nest.occupied && bird.intersects(nest)) { bird.velocity = 0; bird.onWire = true; bird.currentNest = nest; nest.occupied = true; // Re-enable jumping now that bird has landed bird.isJumpAllowed = true; // Update score based on height var newScore = Math.floor((2732 - nest.y) / 100); updateScore(newScore); // Check for successful ascent to a higher wire var currentLevel = Math.floor(nest.initialY / lineSpacing); if (lastNestLevel !== null && currentLevel < lastNestLevel) { ascentCount++; // Increment jump count and increase difficulty every 10 jumps game.jumpCount++; if (game.jumpCount % 10 === 0) { // Increase scroll speed game.scrollSpeed += 0.2; // Flash the screen to indicate difficulty increase LK.effects.flashScreen(0x00ff00, 300); } // Every 3 successful ascents, remove the bottommost wire if (ascentCount % 3 === 0) { removeBottomWire(); } } lastNestLevel = currentLevel; // Play landing sound LK.getSound('land').play(); return true; } } } return false; } // Move camera based on continuous scrolling after first jump function updateCamera() { // Only handle bird position on wire if (bird.onWire && bird.currentNest) { bird.y = bird.currentNest.y - 30; } // Implement continuous scrolling after the game has started if (game.isScrolling) { // Continuously increase camera target position based on scrollSpeed game.cameraTargetY += game.scrollSpeed; // Smoothly move the camera using linear interpolation (lerp) game.cameraOffsetY += (game.cameraTargetY - game.cameraOffsetY) * game.cameraSmoothness; // Apply the camera offset to all game objects that need to move with the camera for (var i = 0; i < powerLines.length; i++) { powerLines[i].y = powerLines[i].initialY + game.cameraOffsetY; } for (var i = 0; i < nests.length; i++) { nests[i].y = nests[i].initialY + game.cameraOffsetY; } // Update background and walls position if they exist var children = game.children; for (var i = 0; i < children.length; i++) { var child = children[i]; if (child.initialY !== undefined && child !== bird) { child.y = child.initialY + game.cameraOffsetY; } } // Update cameraY for game logic (line generation, etc.) cameraY = game.cameraOffsetY; } } // Generate new lines if needed function checkGenerateLines() { if (nextLineY > cameraY - 2732) { createPowerLine(nextLineY); nextLineY -= lineSpacing; } } // Create walls at screen edges function createWalls() { // Prepare wall properties before asset loading var wallProps = { anchorX: 0, anchorY: 0 }; // Using setTimeout to create a small delay before accessing assets LK.setTimeout(function () { var leftWall = LK.getAsset('wall', { anchorX: 0, anchorY: 0 }); leftWall.x = 0; leftWall.y = 0; leftWall.initialY = 0; // Store initial Y position game.addChild(leftWall); var rightWall = LK.getAsset('wall', { anchorX: 0, anchorY: 0 }); rightWall.x = 2048 - rightWall.width; rightWall.y = 0; rightWall.initialY = 0; // Store initial Y position game.addChild(rightWall); }, 5); // Small delay to prevent blocking UI } // Function to remove the bottommost wire and its nests function removeBottomWire() { // Find the bottommost wire (highest y value) var bottomLineIndex = -1; var bottomY = -Infinity; for (var i = 0; i < powerLines.length; i++) { if (powerLines[i].initialY > bottomY) { bottomY = powerLines[i].initialY; bottomLineIndex = i; } } if (bottomLineIndex >= 0) { // Remove the wire game.removeChild(powerLines[bottomLineIndex]); var removedLine = powerLines.splice(bottomLineIndex, 1)[0]; // Remove all nests on this wire for (var i = nests.length - 1; i >= 0; i--) { // Check if the nest is on the removed wire (approximately same initialY) if (Math.abs(nests[i].initialY - (removedLine.initialY - 5)) < 10) { // If bird is on this nest, it will fall if (bird.currentNest === nests[i]) { bird.currentNest = null; bird.onWire = false; bird.isJumpAllowed = false; bird.velocity = 1; // Start falling } game.removeChild(nests[i]); nests.splice(i, 1); } } removedBottomLines++; } } // Remove off-screen power lines and nests function cleanupObjects() { for (var i = powerLines.length - 1; i >= 0; i--) { if (powerLines[i].y > cameraY + 3000) { game.removeChild(powerLines[i]); powerLines.splice(i, 1); } } for (var i = nests.length - 1; i >= 0; i--) { if (nests[i].y > cameraY + 3000) { game.removeChild(nests[i]); nests.splice(i, 1); } } } // Handle game over function handleGameOver() { // Don't check for game over if game hasn't started yet if (!game.gameStarted) return; // Game over condition - bird falls off bottom of screen // With continuous scrolling, we need to check if bird is too far below the camera view var screenBottom = 2732; var visibleBottom = screenBottom + game.cameraOffsetY; var isInGracePeriod = game.gameStarted && LK.ticks < 120; if (!gameOver && !isInGracePeriod && bird.y > visibleBottom) { gameOver = true; LK.getSound('fall').play(); LK.showGameOver(); } } // Main game loop game.update = function () { if (gameOver) return; // Update bird if it exists if (bird) { // Double-check bird is defined before calling update if (typeof bird.update === 'function') { bird.update(); } } // Power lines are static, no need to update them // Update nests for (var i = 0; i < nests.length; i++) { nests[i].update(); } // Check if bird landed on a nest and bird exists if (bird) { checkNestCollision(); // Update camera position updateCamera(); } // Generate new lines if needed checkGenerateLines(); // Remove off-screen objects cleanupObjects(); // Check game over condition if (bird) { handleGameOver(); } }; // Bird jump on screen tap/click game.down = function (x, y, obj) { if (!gameOver && bird && typeof bird.jump === 'function') { bird.jump(); } }; // Show loading indicator var loadingText = new Text2('Loading...', { size: 80, fill: 0xFFFFFF }); loadingText.anchor.set(0.5, 0.5); loadingText.x = 2048 / 2; loadingText.y = 2732 / 2; game.addChild(loadingText); // Create a preloader to load game assets efficiently function startGameWithPreloader() { // Use tween to animate loading text while assets are prepared tween(loadingText, { alpha: 0.5 }, { duration: 500, easing: tween.easeInOutQuad, loop: true, yoyo: true }); // Defer game initialization to prevent startup lag LK.setTimeout(function () { // Remove loading indicator tween(loadingText, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { game.removeChild(loadingText); // Initialize game initGame(); } }); }, 200); // Give enough time for engine to initialize } // Start game with preloader startGameWithPreloader();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Bird = Container.expand(function () {
var self = Container.call(this);
var birdGraphic = self.attachAsset('bird', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocity = 0;
self.gravity = 0.5;
self.jumpForce = -20;
self.flying = false;
self.onWire = true;
self.currentNest = null;
self.isJumpAllowed = true;
self.jump = function () {
// Only jump if allowed (when on a nest)
if (!self.isJumpAllowed) return;
// All jumps handled the same way - no special first jump anymore
if (self.onWire) {
self.onWire = false; // Bird leaves wire
}
// Normal jump physics for all jumps
self.velocity = self.jumpForce;
self.flying = true;
// Mark game as started on first jump
if (!game.gameStarted) {
game.gameStarted = true;
// Store safe landing height to prevent immediate game over
game.safeJumpHeight = self.y + 300; // Room for falling before game over
// Start continuous scrolling
game.isScrolling = true;
}
// Disable jumping until landing on a nest again
self.isJumpAllowed = false;
// Create stars effect
self.createStars();
// Play flying sound
LK.getSound('fly').play();
};
self.createStars = function () {
for (var i = 0; i < 5; i++) {
var star = LK.getAsset('star', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x + (Math.random() * 60 - 30),
y: self.y + (Math.random() * 60 - 30),
alpha: 1
});
game.addChild(star);
// Animate the star
tween(star, {
alpha: 0,
scaleX: 0.2,
scaleY: 0.2,
x: star.x + (Math.random() * 40 - 20),
y: star.y + (Math.random() * 40 - 20)
}, {
duration: 500,
onFinish: function onFinish() {
game.removeChild(star);
}
});
}
};
self.update = function () {
if (!self.onWire) {
// Always apply gravity when bird is flying (both first jump and subsequent jumps)
if (self.flying) {
self.velocity += self.gravity;
self.y += self.velocity;
// Check if falling too fast
if (self.velocity > 20) {
self.velocity = 20;
}
}
} else if (self.currentNest) {
// Bird follows the nest
self.x = self.currentNest.x;
if (self.currentNest.y !== undefined) {
self.y = self.currentNest.y - 30;
}
} else if (self.onWire) {
// Safety check: if bird is somehow on wire but has no nest
// Find closest nest or place in the center of screen
// Don't immediately set flying to true to prevent immediate fall
if (self.currentNest === null) {
// Try to find the nearest nest
var nearestNest = null;
var minDistance = Infinity;
for (var i = 0; i < nests.length; i++) {
var distance = Math.abs(nests[i].x - self.x);
if (distance < minDistance) {
minDistance = distance;
nearestNest = nests[i];
}
}
if (nearestNest) {
self.currentNest = nearestNest;
nearestNest.occupied = true;
} else {
self.onWire = false;
self.flying = true;
self.velocity = 0;
}
}
}
};
self.down = function (x, y, obj) {
self.jump();
};
return self;
});
var Nest = Container.expand(function () {
var self = Container.call(this);
var nestGraphic = self.attachAsset('nest', {
anchorX: 0.5,
anchorY: 0.5
});
self.occupied = false;
self.lastX = undefined;
self.speed = 4; // Speed for horizontal movement
self.direction = 1; // Direction for horizontal movement
// Randomize initial direction
self.randomizeDirection = function () {
self.direction = Math.random() > 0.5 ? 1 : -1;
};
// Update method to handle movement and collisions
self.update = function () {
// Track last position for reference
if (self.lastX === undefined) self.lastX = self.x;
// Move nest horizontally
self.x += self.speed * self.direction;
// Check for wall collisions and reverse direction
var wallWidth = 30; // Width of the wall assets
var nestWidth = 100; // Width of nest
if (self.x <= wallWidth + nestWidth / 2) {
self.direction = 1; // Move right when hitting left wall
self.x = wallWidth + nestWidth / 2;
} else if (self.x >= 2048 - wallWidth - nestWidth / 2) {
self.direction = -1; // Move left when hitting right wall
self.x = 2048 - wallWidth - nestWidth / 2;
}
// Update last position
self.lastX = self.x;
};
// Initialize random direction
self.randomizeDirection();
return self;
});
var PowerLine = Container.expand(function () {
var self = Container.call(this);
var lineGraphic = self.attachAsset('powerLine', {
anchorX: 0,
anchorY: 0.5
});
// Power lines are static - no update method needed
return self;
});
var Star = Container.expand(function () {
var self = Container.call(this);
var starGraphic = self.attachAsset('star', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
var score = 0;
var highScore = storage.highScore || 0;
var powerLines = [];
var nests = [];
var bird;
var cameraY = 0;
var gameOver = false;
var lineSpacing = 300;
var maxLines = 10;
var nextLineY = 2500;
var ascentCount = 0;
var lastNestLevel = null;
var removedBottomLines = 0;
// Initialize score display
var scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(scoreTxt);
scoreTxt.x = -scoreTxt.width - 20;
scoreTxt.y = 20;
// Initialize high score display
var highScoreTxt = new Text2('Best: ' + highScore, {
size: 40,
fill: 0xFFFFFF
});
highScoreTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(highScoreTxt);
highScoreTxt.x = -highScoreTxt.width - 20;
highScoreTxt.y = 100;
// Create background
var background = LK.getAsset('background', {
anchorX: 0,
anchorY: 0
});
game.addChild(background);
// Initialize bird
function initBird() {
bird = new Bird();
bird.x = 2048 / 2;
bird.y = 2500;
game.addChild(bird);
// Bird will be positioned on a nest in initGame
}
// Create a power line with nests
function createPowerLine(y) {
var powerLine = new PowerLine();
powerLine.y = y;
powerLine.initialY = y; // Store initial Y position
// Set power line to span the whole width, respecting wall boundaries
powerLine.x = 30; // Starting from left wall position
game.addChild(powerLine);
powerLines.push(powerLine);
// Calculate nest properties once - more efficient
var nestCount = Math.floor(Math.random() * 3) + 1;
// Ensure at least one nest on the bottommost power line
if (y === 2500) {
nestCount = Math.max(nestCount, 2); // Guarantee at least two nests on bottom line for better gameplay
}
var nestWidth = 100;
var wallWidth = 30; // Width of the wall assets
var availableWidth = 2048 - nestWidth - wallWidth * 2; // Account for walls
// Pre-calculate nest positions for better performance
var nestPositions = [];
for (var i = 0; i < nestCount; i++) {
// If this is the bottom line, ensure at least one nest is near the center
var nestX = y === 2500 && i === 0 ? 2048 / 2 // Center the first nest on bottom line
: wallWidth + nestWidth / 2 + Math.random() * (availableWidth - nestWidth);
nestPositions.push({
x: nestX,
y: y - 5
});
}
// Add nests in a small timeout to prevent UI blocking
LK.setTimeout(function () {
// Place nests randomly along the power line
for (var i = 0; i < nestCount; i++) {
var nest = new Nest();
// Random position along the power line
nest.x = nestPositions[i].x;
nest.y = nestPositions[i].y; // Slightly above the line
nest.initialY = y - 5; // Store initial Y position
game.addChild(nest);
nests.push(nest);
}
}, 0); // Even a 0ms timeout helps by deferring to next event loop
return powerLine;
}
// Initialize power lines
function initPowerLines() {
// Clear existing lines and nests - optimize by checking length first
if (powerLines.length > 0) {
for (var i = 0; i < powerLines.length; i++) {
game.removeChild(powerLines[i]);
}
}
if (nests.length > 0) {
for (var i = 0; i < nests.length; i++) {
game.removeChild(nests[i]);
}
}
powerLines = [];
nests = [];
// Create initial power lines - create in batches for better performance
nextLineY = 2500;
// Create lines in smaller batches to prevent blocking UI
var linesBatch = 3;
var currentLine = 0;
function createLineBatch() {
var batchEnd = Math.min(currentLine + linesBatch, maxLines);
for (var i = currentLine; i < batchEnd; i++) {
createPowerLine(nextLineY);
nextLineY -= lineSpacing;
}
currentLine = batchEnd;
if (currentLine < maxLines) {
LK.setTimeout(createLineBatch, 1);
}
}
createLineBatch();
}
// Initialize the game
function initGame() {
score = 0;
gameOver = false;
game.gameStarted = false; // Flag to track if game has started (first jump)
game.isFirstJump = false; // No longer needed as we use tween for first jump
game.birdHighestPosition = null; // Track bird's highest (lowest y value) position
game.safeJumpHeight = null; // Will be set on first jump
ascentCount = 0;
lastNestLevel = null;
removedBottomLines = 0;
updateScore(0);
// Set canvas size at the beginning only
cameraY = 0; // Reset camera position
game.cameraOffsetY = 0; // Smoothed camera offset for rendering
game.cameraTargetY = 0; // Target camera position
game.cameraSmoothness = 0.1; // How quickly camera follows (0-1)
game.scrollSpeed = 1; // Base scroll speed
game.jumpCount = 0; // Count of successful jumps
game.isScrolling = false; // Whether continuous scrolling is active
// Defer heavy initialization with a small timeout to allow the UI to render first
LK.setTimeout(function () {
initPowerLines();
initBird();
// Create walls at screen edges
createWalls();
// Give time for nests to be created, then place bird
LK.setTimeout(function () {
// Find a nest on the bottommost line and place bird on it
var bottomNest = null;
var bottomY = 0;
// First identify the bottommost line by finding the nest with largest Y value
for (var i = 0; i < nests.length; i++) {
if (nests[i].y > bottomY) {
bottomY = nests[i].y;
bottomNest = nests[i];
}
}
// If we found a bottom nest, place bird on it
if (bottomNest) {
bird.currentNest = bottomNest;
bird.x = bottomNest.x;
bird.y = bottomNest.y - 30;
bird.isJumpAllowed = true;
bird.onWire = true;
bottomNest.occupied = true;
} else {
// Ensure bird is properly placed on the bottommost power line if no nest was found
var bottomLine = null;
var maxY = -Infinity;
for (var i = 0; i < powerLines.length; i++) {
if (powerLines[i].initialY > maxY) {
maxY = powerLines[i].initialY;
bottomLine = powerLines[i];
}
}
if (bottomLine) {
// Create a nest on the bottom line
var nest = new Nest();
nest.x = 2048 / 2; // Center of screen
nest.y = bottomLine.y - 5; // Slightly above the line
nest.initialY = bottomLine.initialY - 5;
game.addChild(nest);
nests.push(nest);
// Place bird on this nest
bird.currentNest = nest;
bird.x = nest.x;
bird.y = nest.y - 30;
bird.isJumpAllowed = true;
bird.onWire = true;
nest.occupied = true;
}
}
// Play background music
LK.playMusic('gameMusic');
}, 100); // A bit more delay to ensure nests are created
}, 50); // Short delay to let the UI render first
}
// Update score display
function updateScore(newScore) {
score = newScore;
scoreTxt.setText('Score: ' + score);
if (score > highScore) {
highScore = score;
storage.highScore = highScore;
highScoreTxt.setText('Best: ' + highScore);
}
}
// Check if bird is on a nest
function checkNestCollision() {
if (bird.velocity > 0) {
// Only check when bird is falling
for (var i = 0; i < nests.length; i++) {
var nest = nests[i];
if (!nest.occupied && bird.intersects(nest)) {
bird.velocity = 0;
bird.onWire = true;
bird.currentNest = nest;
nest.occupied = true;
// Re-enable jumping now that bird has landed
bird.isJumpAllowed = true;
// Update score based on height
var newScore = Math.floor((2732 - nest.y) / 100);
updateScore(newScore);
// Check for successful ascent to a higher wire
var currentLevel = Math.floor(nest.initialY / lineSpacing);
if (lastNestLevel !== null && currentLevel < lastNestLevel) {
ascentCount++;
// Increment jump count and increase difficulty every 10 jumps
game.jumpCount++;
if (game.jumpCount % 10 === 0) {
// Increase scroll speed
game.scrollSpeed += 0.2;
// Flash the screen to indicate difficulty increase
LK.effects.flashScreen(0x00ff00, 300);
}
// Every 3 successful ascents, remove the bottommost wire
if (ascentCount % 3 === 0) {
removeBottomWire();
}
}
lastNestLevel = currentLevel;
// Play landing sound
LK.getSound('land').play();
return true;
}
}
}
return false;
}
// Move camera based on continuous scrolling after first jump
function updateCamera() {
// Only handle bird position on wire
if (bird.onWire && bird.currentNest) {
bird.y = bird.currentNest.y - 30;
}
// Implement continuous scrolling after the game has started
if (game.isScrolling) {
// Continuously increase camera target position based on scrollSpeed
game.cameraTargetY += game.scrollSpeed;
// Smoothly move the camera using linear interpolation (lerp)
game.cameraOffsetY += (game.cameraTargetY - game.cameraOffsetY) * game.cameraSmoothness;
// Apply the camera offset to all game objects that need to move with the camera
for (var i = 0; i < powerLines.length; i++) {
powerLines[i].y = powerLines[i].initialY + game.cameraOffsetY;
}
for (var i = 0; i < nests.length; i++) {
nests[i].y = nests[i].initialY + game.cameraOffsetY;
}
// Update background and walls position if they exist
var children = game.children;
for (var i = 0; i < children.length; i++) {
var child = children[i];
if (child.initialY !== undefined && child !== bird) {
child.y = child.initialY + game.cameraOffsetY;
}
}
// Update cameraY for game logic (line generation, etc.)
cameraY = game.cameraOffsetY;
}
}
// Generate new lines if needed
function checkGenerateLines() {
if (nextLineY > cameraY - 2732) {
createPowerLine(nextLineY);
nextLineY -= lineSpacing;
}
}
// Create walls at screen edges
function createWalls() {
// Prepare wall properties before asset loading
var wallProps = {
anchorX: 0,
anchorY: 0
};
// Using setTimeout to create a small delay before accessing assets
LK.setTimeout(function () {
var leftWall = LK.getAsset('wall', {
anchorX: 0,
anchorY: 0
});
leftWall.x = 0;
leftWall.y = 0;
leftWall.initialY = 0; // Store initial Y position
game.addChild(leftWall);
var rightWall = LK.getAsset('wall', {
anchorX: 0,
anchorY: 0
});
rightWall.x = 2048 - rightWall.width;
rightWall.y = 0;
rightWall.initialY = 0; // Store initial Y position
game.addChild(rightWall);
}, 5); // Small delay to prevent blocking UI
}
// Function to remove the bottommost wire and its nests
function removeBottomWire() {
// Find the bottommost wire (highest y value)
var bottomLineIndex = -1;
var bottomY = -Infinity;
for (var i = 0; i < powerLines.length; i++) {
if (powerLines[i].initialY > bottomY) {
bottomY = powerLines[i].initialY;
bottomLineIndex = i;
}
}
if (bottomLineIndex >= 0) {
// Remove the wire
game.removeChild(powerLines[bottomLineIndex]);
var removedLine = powerLines.splice(bottomLineIndex, 1)[0];
// Remove all nests on this wire
for (var i = nests.length - 1; i >= 0; i--) {
// Check if the nest is on the removed wire (approximately same initialY)
if (Math.abs(nests[i].initialY - (removedLine.initialY - 5)) < 10) {
// If bird is on this nest, it will fall
if (bird.currentNest === nests[i]) {
bird.currentNest = null;
bird.onWire = false;
bird.isJumpAllowed = false;
bird.velocity = 1; // Start falling
}
game.removeChild(nests[i]);
nests.splice(i, 1);
}
}
removedBottomLines++;
}
}
// Remove off-screen power lines and nests
function cleanupObjects() {
for (var i = powerLines.length - 1; i >= 0; i--) {
if (powerLines[i].y > cameraY + 3000) {
game.removeChild(powerLines[i]);
powerLines.splice(i, 1);
}
}
for (var i = nests.length - 1; i >= 0; i--) {
if (nests[i].y > cameraY + 3000) {
game.removeChild(nests[i]);
nests.splice(i, 1);
}
}
}
// Handle game over
function handleGameOver() {
// Don't check for game over if game hasn't started yet
if (!game.gameStarted) return;
// Game over condition - bird falls off bottom of screen
// With continuous scrolling, we need to check if bird is too far below the camera view
var screenBottom = 2732;
var visibleBottom = screenBottom + game.cameraOffsetY;
var isInGracePeriod = game.gameStarted && LK.ticks < 120;
if (!gameOver && !isInGracePeriod && bird.y > visibleBottom) {
gameOver = true;
LK.getSound('fall').play();
LK.showGameOver();
}
}
// Main game loop
game.update = function () {
if (gameOver) return;
// Update bird if it exists
if (bird) {
// Double-check bird is defined before calling update
if (typeof bird.update === 'function') {
bird.update();
}
}
// Power lines are static, no need to update them
// Update nests
for (var i = 0; i < nests.length; i++) {
nests[i].update();
}
// Check if bird landed on a nest and bird exists
if (bird) {
checkNestCollision();
// Update camera position
updateCamera();
}
// Generate new lines if needed
checkGenerateLines();
// Remove off-screen objects
cleanupObjects();
// Check game over condition
if (bird) {
handleGameOver();
}
};
// Bird jump on screen tap/click
game.down = function (x, y, obj) {
if (!gameOver && bird && typeof bird.jump === 'function') {
bird.jump();
}
};
// Show loading indicator
var loadingText = new Text2('Loading...', {
size: 80,
fill: 0xFFFFFF
});
loadingText.anchor.set(0.5, 0.5);
loadingText.x = 2048 / 2;
loadingText.y = 2732 / 2;
game.addChild(loadingText);
// Create a preloader to load game assets efficiently
function startGameWithPreloader() {
// Use tween to animate loading text while assets are prepared
tween(loadingText, {
alpha: 0.5
}, {
duration: 500,
easing: tween.easeInOutQuad,
loop: true,
yoyo: true
});
// Defer game initialization to prevent startup lag
LK.setTimeout(function () {
// Remove loading indicator
tween(loadingText, {
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
game.removeChild(loadingText);
// Initialize game
initGame();
}
});
}, 200); // Give enough time for engine to initialize
}
// Start game with preloader
startGameWithPreloader();