/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // CarOnlyLeft class var CarOnlyLeft = Container.expand(function () { var self = Container.call(this); self.attachAsset('CarOnlyLeft', { anchorX: 0.5, anchorY: 1 }); // Car speed increases with score: base speed + 0.5 per point, capped at +20 var carSpeedBoost = Math.min(game && game.score ? game.score * 0.5 : 0, 20); self.speed = -16 - Math.random() * 8 - carSpeedBoost; // Speed from right to left self.update = function () { self.x += self.speed; // Check for collision with other cars moving in the same direction for (var i = 0; i < cars.length; i++) { var otherCar = cars[i]; if (otherCar !== self && otherCar instanceof CarOnlyLeft) { var selfRect = self.getCollisionRect(); var otherRect = otherCar.getCollisionRect(); if (rectsIntersect(selfRect, otherRect)) { // Adjust position to prevent overlap self.x = otherCar.x + otherCar.width + 10; } } } // Destroy car if it touches or overlaps the top or bottom ground assets var topGroundY = 0; var bottomGroundY = 2732 - 300; if (self.y - 65 <= topGroundY + 10 || self.y + 65 >= bottomGroundY + 300 - 10) { self.destroy(); return; } // Check if the car is off-screen to the left if (self.x < -300) { self.destroy(); } }; self.getCollisionRect = function () { return new Rectangle(self.x - 100, self.y - 65, 200, 130); }; return self; }); // CarOnlyLeft2 class var CarOnlyLeft2 = Container.expand(function () { var self = Container.call(this); self.attachAsset('CarOnlyLeft2', { anchorX: 0.5, anchorY: 1 }); // Car speed increases with score: base speed + 0.5 per point, capped at +20 var carSpeedBoost = Math.min(game && game.score ? game.score * 0.5 : 0, 20); self.speed = -16 - Math.random() * 8 - carSpeedBoost; // Speed from right to left self.update = function () { self.x += self.speed; // Check for collision with other cars moving in the same direction for (var i = 0; i < cars.length; i++) { var otherCar = cars[i]; if (otherCar !== self && otherCar instanceof CarOnlyLeft2) { var selfRect = self.getCollisionRect(); var otherRect = otherCar.getCollisionRect(); if (rectsIntersect(selfRect, otherRect)) { // Adjust position to prevent overlap self.x = otherCar.x + otherCar.width + 10; } } } // Destroy car if it touches or overlaps the top or bottom ground assets var topGroundY = 0; var bottomGroundY = 2732 - 300; if (self.y - 65 <= topGroundY + 10 || self.y + 65 >= bottomGroundY + 300 - 10) { self.destroy(); return; } // Check if the car is off-screen to the left if (self.x < -300) { self.destroy(); } }; self.getCollisionRect = function () { return new Rectangle(self.x - 100, self.y - 65, 200, 130); }; return self; }); // CarOnlyRight class var CarOnlyRight = Container.expand(function () { var self = Container.call(this); self.attachAsset('CarOnlyRight', { anchorX: 0.5, anchorY: 1 }); // Car speed increases with score: base speed + 0.5 per point, capped at +20 var carSpeedBoost = Math.min(game && game.score ? game.score * 0.5 : 0, 20); self.speed = 16 + Math.random() * 8 + carSpeedBoost; // Speed from left to right self.update = function () { self.x += self.speed; // Check for collision with other cars moving in the same direction for (var i = 0; i < cars.length; i++) { var otherCar = cars[i]; if (otherCar !== self && otherCar instanceof CarOnlyRight) { var selfRect = self.getCollisionRect(); var otherRect = otherCar.getCollisionRect(); if (rectsIntersect(selfRect, otherRect)) { // Adjust position to prevent overlap self.x = otherCar.x - self.width - 10; } } } // Destroy car if it touches or overlaps the top or bottom ground assets var topGroundY = 0; var bottomGroundY = 2732 - 300; if (self.y - 65 <= topGroundY + 10 || self.y + 65 >= bottomGroundY + 300 - 10) { self.destroy(); return; } // Check if the car is off-screen to the right if (self.x > 2048 + 300) { self.destroy(); } }; self.getCollisionRect = function () { return new Rectangle(self.x - 100, self.y - 65, 200, 130); }; return self; }); // CarOnlyRight2 class var CarOnlyRight2 = Container.expand(function () { var self = Container.call(this); self.attachAsset('CarOnlyRight2', { anchorX: 0.5, anchorY: 1 }); var carSpeedBoost = Math.min(game && game.score ? game.score * 0.5 : 0, 20); self.speed = 16 + Math.random() * 8 + carSpeedBoost; // Speed from left to right self.update = function () { self.x += self.speed; for (var i = 0; i < cars.length; i++) { var otherCar = cars[i]; if (otherCar !== self && otherCar instanceof CarOnlyRight2) { var selfRect = self.getCollisionRect(); var otherRect = otherCar.getCollisionRect(); if (rectsIntersect(selfRect, otherRect)) { self.x = otherCar.x - self.width - 10; } } } // Destroy car if it touches or overlaps the top or bottom ground assets var topGroundY = 0; var bottomGroundY = 2732 - 300; if (self.y - 65 <= topGroundY + 10 || self.y + 65 >= bottomGroundY + 300 - 10) { self.destroy(); return; } // Check if the car is off-screen to the right if (self.x > 2048 + 300) { self.destroy(); } }; self.getCollisionRect = function () { return new Rectangle(self.x - 100, self.y - 65, 200, 130); }; return self; }); // Coin class var Coin = Container.expand(function () { var self = Container.call(this); self.lane = 1; self.collected = false; self.attachAsset('coin', { anchorX: 0.5, anchorY: 1 }); self.getCollisionRect = function () { return new Rectangle(self.x - 35, self.y - 80, 70, 70); }; self.update = function () { self.y += game.speed; }; return self; }); // CollectableHealth class var CollectableHealth = Container.expand(function () { var self = Container.call(this); self.collected = false; self.timer = 600; // 10 seconds at 60 FPS self.attachAsset('CollectableHealth', { anchorX: 0.5, anchorY: 1 }); self.getCollisionRect = function () { return new Rectangle(self.x - 50, self.y - 100, 100, 100); }; self.update = function () { self.y += game.speed; if (!self.collected) { self.timer--; } }; return self; }); // Shield collectable class (spawns like CollectableHealth, but rarer) var CollectableShield = Container.expand(function () { var self = Container.call(this); self.collected = false; self.timer = 600; // 10 seconds at 60 FPS self.attachAsset('Shield', { anchorX: 0.5, anchorY: 1 }); self.getCollisionRect = function () { return new Rectangle(self.x - 50, self.y - 100, 100, 100); }; self.update = function () { self.y += game.speed; if (!self.collected) { self.timer--; } }; return self; }); // Obstacle class var Obstacle = Container.expand(function () { var self = Container.call(this); self.type = 'CarOnlyLeft'; // CarOnlyLeft self.lane = 1; self.passed = false; self.setType = function (type) { self.type = type; self.removeChildren(); if (type === 'CarOnlyLeft') { self.attachAsset('CarOnlyLeft', { anchorX: 0.5, anchorY: 1 }); self.width = 200; self.height = 130; } }; self.getCollisionRect = function () { return new Rectangle(self.x - 100, self.y - 65, 200, 130); }; self.update = function () { self.y += game.speed; }; return self; }); // Runner class (Penguin for crossing game) var Runner = Container.expand(function () { var self = Container.call(this); var runnerSprite = self.attachAsset('runner', { anchorX: 0.5, anchorY: 1 }); self.width = runnerSprite.width; self.height = runnerSprite.height; // For collision box self.getCollisionRect = function () { var w = self.width * 0.7; var h = self.height * 0.9; return new Rectangle(self.x - w / 2, self.y - h, w, h); }; self.update = function () { // No-op for penguin (no auto movement) }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Play relaxMusic as soon as the game starts LK.playMusic('RelaxMusic'); // New road asset // Sound effects // Jungle ground // Power-up (not yet used, but for future) // Coin // Lane obstacles // Character (runner) // Penguin crossing game setup // Define lanes for cars (3 lanes, horizontal) - make lanes closer together game.laneY = [2732 - 400, // bottom lane (closest to player) 2732 - 650, // middle lane 2732 - 900 // top lane (furthest from player) ]; // X positions for cars (off-screen left/right) game.carStartLeft = -200; game.carStartRight = 2048 + 200; // Penguin start position (bottom center, a bit further down) game.penguinStartX = 2048 / 2; game.penguinStartY = 2732 - 30; // moved even lower // Game state game.ticks = 0; game.score = 0; game.isGameOver = false; // Shield state var shieldActive = false; var shieldTimer = 0; var shieldDuration = 600; // 10 seconds at 60 FPS // Arrays for cars var cars = []; // Add ground to the top of the screen var topGround = LK.getAsset('ground', { anchorX: 0.5, anchorY: 0, x: 2048 / 2, y: 0 }); game.addChild(topGround); // Add ground to the bottom of the screen var bottomGround = LK.getAsset('ground', { anchorX: 0.5, anchorY: 1, x: 2048 / 2, y: 2732 }); game.addChild(bottomGround); // Add multiple road assets to cover the area strictly between the top and bottom ground, with reduced vertical gaps var roadHeight = 300; // Height of each road asset var groundHeight = 300; var roadStartY = groundHeight; // just below top ground var roadEndY = 2732 - groundHeight; // just above bottom ground var numberOfRoads = Math.ceil((roadEndY - roadStartY) / roadHeight); for (var i = 0; i < numberOfRoads; i++) { var roadY = roadEndY - i * roadHeight + 10; // Add slight overlap to remove visible gaps // Clamp roadY so that the bottom of the topmost road does not go above roadStartY if (roadY < roadStartY + roadHeight) roadY = roadStartY + roadHeight; // Clamp roadY so that the top of the bottommost road does not go below roadEndY if (roadY > roadEndY) roadY = roadEndY; // Only add road if its bottom is below top ground and its top is above bottom ground if (roadY <= roadEndY && roadY - roadHeight >= roadStartY) { var road = LK.getAsset('road', { anchorX: 0.5, anchorY: 1, x: 2048 / 2, y: roadY }); game.addChild(road); } } // Add penguin (player) var penguin = new Runner(); penguin.x = game.penguinStartX; penguin.y = game.penguinStartY; penguin.lane = 1; // start in center game.addChild(penguin); // Shield countdown text above penguin var shieldCountdownTxt = new Text2('', { size: 60, fill: 0x00EAFF, font: "Arial Black" }); shieldCountdownTxt.anchor.set(0.5, 1); shieldCountdownTxt.x = penguin.x; shieldCountdownTxt.y = penguin.y - penguin.height - 10; game.addChild(shieldCountdownTxt); // Shield icon to show next to countdown var shieldIcon = LK.getAsset('Shield', { anchorX: 0.5, anchorY: 1, scaleX: 0.7, scaleY: 0.7, x: shieldCountdownTxt.x + 60, y: shieldCountdownTxt.y }); shieldIcon.visible = false; game.addChild(shieldIcon); // Health bar setup var maxHealth = 3; var penguinHealth = maxHealth; // Health bar background (red) var healthBarBg = LK.getAsset('ground', { anchorX: 0, anchorY: 0, x: 120, y: 30, scaleX: 0.1, scaleY: 0.1, tint: 0xff0000 // red }); LK.gui.top.addChild(healthBarBg); // Health bar foreground (dynamic hearts) var healthHearts = []; for (var i = 0; i < maxHealth; i++) { var heart = LK.getAsset('coin', { anchorX: 0, anchorY: 0, x: 130 + i * 90, y: 40, scaleX: 0.8, scaleY: 0.8 }); LK.gui.top.addChild(heart); healthHearts.push(heart); } // Helper to update health bar function updateHealthBar() { for (var i = 0; i < maxHealth; i++) { healthHearts[i].alpha = i < penguinHealth ? 1 : 0.2; } } updateHealthBar(); // Score text var scoreTxt = new Text2('0', { size: 120, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Helper: spawn car function spawnCar() { // Randomly pick car type: 'log', 'rock', or 'pit' // Adjusted carTypes to decrease CarOnlyLeft and increase CarOnlyLeft2 frequency var carTypes = ['CarOnlyLeft', 'CarOnlyLeft2', 'CarOnlyLeft2', 'CarOnlyRight2', 'CarOnlyRight2', 'CarOnlyRight']; var carType = carTypes[Math.floor(Math.random() * carTypes.length)]; // Calculate Y spawn range to use all road assets (between top and bottom ground) var groundHeight = 300; var minY = groundHeight; // 300 var maxY = 2732 - groundHeight; // 2432 // Random Y position between minY and maxY, covering all roads var carY = minY + Math.random() * (maxY - minY); if (carType === 'CarOnlyLeft') { var car = new CarOnlyLeft(); car.x = game.carStartRight; car.y = carY; game.addChild(car); cars.push(car); } else if (carType === 'CarOnlyLeft2') { var car = new CarOnlyLeft2(); car.x = game.carStartRight; car.y = carY; game.addChild(car); cars.push(car); } else if (carType === 'CarOnlyRight') { var car = new CarOnlyRight(); car.x = game.carStartLeft; car.y = carY; game.addChild(car); cars.push(car); } else if (carType === 'CarOnlyRight2') { var car = new CarOnlyRight2(); car.x = game.carStartLeft; car.y = carY; game.addChild(car); cars.push(car); } } // Touch/Swipe handling for penguin movement (up/down/left/right) var touchStartX = 0, touchStartY = 0, touchStartTime = 0; var swipeThreshold = 80; // px var swipeTime = 400; // ms game.down = function (x, y, obj) { touchStartX = x; touchStartY = y; touchStartTime = Date.now(); }; game.up = function (x, y, obj) { var dx = x - touchStartX; var dy = y - touchStartY; var dt = Date.now() - touchStartTime; if (dt > swipeTime) return; // Too slow if (Math.abs(dx) > Math.abs(dy)) { // Horizontal swipe if (dx > swipeThreshold) { // Move right if (penguin.x < 2048 - 200) penguin.x += 220; } else if (dx < -swipeThreshold) { // Move left if (penguin.x > 200) { penguin.x -= 320; if (penguin.x < 200) penguin.x = 200; } } } else { // Vertical swipe if (dy < -swipeThreshold) { // Move up if (penguin.y > road.y - road.height + 200) penguin.y -= 250; // Adjust to ensure penguin stays on the road and matches new lane distance } else if (dy > swipeThreshold) { // Move down if (penguin.y < game.penguinStartY) penguin.y += 250; } } }; // Main update loop var collectableHealths = []; game.update = function () { if (game.isGameOver) return; game.ticks++; // Spawn cars at intervals // Car spawn interval decreases as time passes (minimum 12 ticks, max 36 at start) var minInterval = 12; var maxInterval = 36; var interval = Math.max(minInterval, maxInterval - Math.floor(game.ticks / 300)); if (game.ticks % interval === 0) { spawnCar(); } // Only spawn a new CollectableHealth or Shield if there are none currently on screen or being collected if (collectableHealths.length === 0) { if (typeof game.nextHealthCanSpawn === "undefined") game.nextHealthCanSpawn = true; // First, try to spawn Shield (much lower chance) if (game.nextHealthCanSpawn && Math.random() < 0.0007) { var shield = new CollectableShield(); // Pick a random lane var laneIndex = Math.floor(Math.random() * game.laneY.length); // Penguin possible X positions var penguinPossibleX = []; for (var px = 200; px <= 2048 - 200; px += 320) { penguinPossibleX.push(px); } var xIndex = Math.floor(Math.random() * penguinPossibleX.length); shield.x = penguinPossibleX[xIndex]; shield.y = game.laneY[laneIndex]; game.addChild(shield); collectableHealths.push(shield); game.nextHealthCanSpawn = false; } else if (game.nextHealthCanSpawn && Math.random() < 0.0015) { var health = new CollectableHealth(); // Pick a random lane var laneIndex = Math.floor(Math.random() * game.laneY.length); var penguinPossibleX = []; for (var px = 200; px <= 2048 - 200; px += 320) { penguinPossibleX.push(px); } var xIndex = Math.floor(Math.random() * penguinPossibleX.length); health.x = penguinPossibleX[xIndex]; health.y = game.laneY[laneIndex]; game.addChild(health); collectableHealths.push(health); game.nextHealthCanSpawn = false; } } else { game.nextHealthCanSpawn = false; } if (collectableHealths.length === 0) { game.nextHealthCanSpawn = true; } // Update cars for (var i = cars.length - 1; i >= 0; i--) { var car = cars[i]; // Update all cars regardless of Y position (so they use all road assets) car.update(); // Remove if off screen if (car.speed > 0 && car.x > 2048 + 300 || car.speed < 0 && car.x < -300) { car.destroy(); cars.splice(i, 1); continue; } // Collision with penguin var pRect = penguin.getCollisionRect(); var cRect = car.getCollisionRect(); if (rectsIntersect(pRect, cRect)) { if (shieldActive) { // Shield blocks damage, just destroy car and continue car.destroy(); cars.splice(i, 1); continue; } LK.getSound('hitSound').play(); LK.getSound('chickenSound').play(); LK.effects.flashScreen(0xff0000, 800); // Remove car after hit car.destroy(); cars.splice(i, 1); // Reduce health penguinHealth--; updateHealthBar(); if (penguinHealth <= 0) { // Game over: play sound, then show game over popup and show score game.isGameOver = true; LK.getSound('GameOver').play(); LK.setTimeout(function () { LK.showGameOver({ score: game.score }); }, 700); // Wait 700ms for sound to play before showing game over return; } // Brief invulnerability: move penguin back to start Y (optional, or just flash) // penguin.y = game.penguinStartY; continue; } } // Update and check CollectableHealths for (var i = collectableHealths.length - 1; i >= 0; i--) { var health = collectableHealths[i]; health.update(); // Remove if off screen or timer expired if (health.y > 2732 || !health.collected && typeof health.timer !== "undefined" && health.timer <= 0) { health.destroy(); collectableHealths.splice(i, 1); continue; } // Check collision with penguin if (!health.collected && rectsIntersect(penguin.getCollisionRect(), health.getCollisionRect())) { health.collected = true; health.destroy(); collectableHealths.splice(i, 1); // If it's a shield, activate shield if (health instanceof CollectableShield) { shieldActive = true; shieldTimer = shieldDuration; } else { // Only increase health if not at max if (penguinHealth < maxHealth) { penguinHealth++; updateHealthBar(); } } continue; } } // Check if penguin touches the top ground asset (give point and respawn at start) var penguinRect = penguin.getCollisionRect(); var topGroundRect = new Rectangle(topGround.x - topGround.width / 2, topGround.y, topGround.width, topGround.height); if (rectsIntersect(penguinRect, topGroundRect)) { // Give 1 point game.score++; scoreTxt.setText(game.score.toString()); LK.getSound('PointGained').play(); // Respawn penguin at start position penguin.x = game.penguinStartX; penguin.y = game.penguinStartY; // Prevent double point if penguin is still colliding next frame // (move penguin just below top ground) // (No need to snap, since respawn is at start) } // Update shield timer if (shieldActive) { shieldTimer--; // Show countdown above penguin shieldCountdownTxt.visible = true; shieldCountdownTxt.x = penguin.x; shieldCountdownTxt.y = penguin.y - penguin.height - 10; var secondsLeft = Math.ceil(shieldTimer / 60); shieldCountdownTxt.setText(secondsLeft.toString()); // Show and position shield icon next to countdown shieldIcon.visible = true; shieldIcon.x = shieldCountdownTxt.x + shieldCountdownTxt.width / 2 + 40; shieldIcon.y = shieldCountdownTxt.y - 10; if (shieldTimer <= 0) { shieldActive = false; shieldCountdownTxt.visible = false; shieldIcon.visible = false; } } else { shieldCountdownTxt.visible = false; shieldIcon.visible = false; } // No win for reaching the bottom, only top is the goal }; // Rectangle intersection helper function rectsIntersect(r1, r2) { return !(r2.x > r1.x + r1.width || r2.x + r2.width < r1.x || r2.y > r1.y + r1.height || r2.y + r2.height < r1.y); } // Reset on game restart game.on('reset', function () { // Play relaxMusic again on reset LK.playMusic('RelaxMusic'); // Remove all cars for (var i = 0; i < cars.length; i++) cars[i].destroy(); cars = []; // Remove all collectable healths if (typeof collectableHealths !== "undefined") { for (var i = 0; i < collectableHealths.length; i++) collectableHealths[i].destroy(); collectableHealths = []; } game.ticks = 0; game.score = 0; game.isGameOver = false; scoreTxt.setText('0'); penguin.x = game.penguinStartX; penguin.y = game.penguinStartY; // Reset health penguinHealth = maxHealth; updateHealthBar(); // Reset shield shieldActive = false; shieldTimer = 0; });
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// CarOnlyLeft class
var CarOnlyLeft = Container.expand(function () {
var self = Container.call(this);
self.attachAsset('CarOnlyLeft', {
anchorX: 0.5,
anchorY: 1
});
// Car speed increases with score: base speed + 0.5 per point, capped at +20
var carSpeedBoost = Math.min(game && game.score ? game.score * 0.5 : 0, 20);
self.speed = -16 - Math.random() * 8 - carSpeedBoost; // Speed from right to left
self.update = function () {
self.x += self.speed;
// Check for collision with other cars moving in the same direction
for (var i = 0; i < cars.length; i++) {
var otherCar = cars[i];
if (otherCar !== self && otherCar instanceof CarOnlyLeft) {
var selfRect = self.getCollisionRect();
var otherRect = otherCar.getCollisionRect();
if (rectsIntersect(selfRect, otherRect)) {
// Adjust position to prevent overlap
self.x = otherCar.x + otherCar.width + 10;
}
}
}
// Destroy car if it touches or overlaps the top or bottom ground assets
var topGroundY = 0;
var bottomGroundY = 2732 - 300;
if (self.y - 65 <= topGroundY + 10 || self.y + 65 >= bottomGroundY + 300 - 10) {
self.destroy();
return;
}
// Check if the car is off-screen to the left
if (self.x < -300) {
self.destroy();
}
};
self.getCollisionRect = function () {
return new Rectangle(self.x - 100, self.y - 65, 200, 130);
};
return self;
});
// CarOnlyLeft2 class
var CarOnlyLeft2 = Container.expand(function () {
var self = Container.call(this);
self.attachAsset('CarOnlyLeft2', {
anchorX: 0.5,
anchorY: 1
});
// Car speed increases with score: base speed + 0.5 per point, capped at +20
var carSpeedBoost = Math.min(game && game.score ? game.score * 0.5 : 0, 20);
self.speed = -16 - Math.random() * 8 - carSpeedBoost; // Speed from right to left
self.update = function () {
self.x += self.speed;
// Check for collision with other cars moving in the same direction
for (var i = 0; i < cars.length; i++) {
var otherCar = cars[i];
if (otherCar !== self && otherCar instanceof CarOnlyLeft2) {
var selfRect = self.getCollisionRect();
var otherRect = otherCar.getCollisionRect();
if (rectsIntersect(selfRect, otherRect)) {
// Adjust position to prevent overlap
self.x = otherCar.x + otherCar.width + 10;
}
}
}
// Destroy car if it touches or overlaps the top or bottom ground assets
var topGroundY = 0;
var bottomGroundY = 2732 - 300;
if (self.y - 65 <= topGroundY + 10 || self.y + 65 >= bottomGroundY + 300 - 10) {
self.destroy();
return;
}
// Check if the car is off-screen to the left
if (self.x < -300) {
self.destroy();
}
};
self.getCollisionRect = function () {
return new Rectangle(self.x - 100, self.y - 65, 200, 130);
};
return self;
});
// CarOnlyRight class
var CarOnlyRight = Container.expand(function () {
var self = Container.call(this);
self.attachAsset('CarOnlyRight', {
anchorX: 0.5,
anchorY: 1
});
// Car speed increases with score: base speed + 0.5 per point, capped at +20
var carSpeedBoost = Math.min(game && game.score ? game.score * 0.5 : 0, 20);
self.speed = 16 + Math.random() * 8 + carSpeedBoost; // Speed from left to right
self.update = function () {
self.x += self.speed;
// Check for collision with other cars moving in the same direction
for (var i = 0; i < cars.length; i++) {
var otherCar = cars[i];
if (otherCar !== self && otherCar instanceof CarOnlyRight) {
var selfRect = self.getCollisionRect();
var otherRect = otherCar.getCollisionRect();
if (rectsIntersect(selfRect, otherRect)) {
// Adjust position to prevent overlap
self.x = otherCar.x - self.width - 10;
}
}
}
// Destroy car if it touches or overlaps the top or bottom ground assets
var topGroundY = 0;
var bottomGroundY = 2732 - 300;
if (self.y - 65 <= topGroundY + 10 || self.y + 65 >= bottomGroundY + 300 - 10) {
self.destroy();
return;
}
// Check if the car is off-screen to the right
if (self.x > 2048 + 300) {
self.destroy();
}
};
self.getCollisionRect = function () {
return new Rectangle(self.x - 100, self.y - 65, 200, 130);
};
return self;
});
// CarOnlyRight2 class
var CarOnlyRight2 = Container.expand(function () {
var self = Container.call(this);
self.attachAsset('CarOnlyRight2', {
anchorX: 0.5,
anchorY: 1
});
var carSpeedBoost = Math.min(game && game.score ? game.score * 0.5 : 0, 20);
self.speed = 16 + Math.random() * 8 + carSpeedBoost; // Speed from left to right
self.update = function () {
self.x += self.speed;
for (var i = 0; i < cars.length; i++) {
var otherCar = cars[i];
if (otherCar !== self && otherCar instanceof CarOnlyRight2) {
var selfRect = self.getCollisionRect();
var otherRect = otherCar.getCollisionRect();
if (rectsIntersect(selfRect, otherRect)) {
self.x = otherCar.x - self.width - 10;
}
}
}
// Destroy car if it touches or overlaps the top or bottom ground assets
var topGroundY = 0;
var bottomGroundY = 2732 - 300;
if (self.y - 65 <= topGroundY + 10 || self.y + 65 >= bottomGroundY + 300 - 10) {
self.destroy();
return;
}
// Check if the car is off-screen to the right
if (self.x > 2048 + 300) {
self.destroy();
}
};
self.getCollisionRect = function () {
return new Rectangle(self.x - 100, self.y - 65, 200, 130);
};
return self;
});
// Coin class
var Coin = Container.expand(function () {
var self = Container.call(this);
self.lane = 1;
self.collected = false;
self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 1
});
self.getCollisionRect = function () {
return new Rectangle(self.x - 35, self.y - 80, 70, 70);
};
self.update = function () {
self.y += game.speed;
};
return self;
});
// CollectableHealth class
var CollectableHealth = Container.expand(function () {
var self = Container.call(this);
self.collected = false;
self.timer = 600; // 10 seconds at 60 FPS
self.attachAsset('CollectableHealth', {
anchorX: 0.5,
anchorY: 1
});
self.getCollisionRect = function () {
return new Rectangle(self.x - 50, self.y - 100, 100, 100);
};
self.update = function () {
self.y += game.speed;
if (!self.collected) {
self.timer--;
}
};
return self;
});
// Shield collectable class (spawns like CollectableHealth, but rarer)
var CollectableShield = Container.expand(function () {
var self = Container.call(this);
self.collected = false;
self.timer = 600; // 10 seconds at 60 FPS
self.attachAsset('Shield', {
anchorX: 0.5,
anchorY: 1
});
self.getCollisionRect = function () {
return new Rectangle(self.x - 50, self.y - 100, 100, 100);
};
self.update = function () {
self.y += game.speed;
if (!self.collected) {
self.timer--;
}
};
return self;
});
// Obstacle class
var Obstacle = Container.expand(function () {
var self = Container.call(this);
self.type = 'CarOnlyLeft'; // CarOnlyLeft
self.lane = 1;
self.passed = false;
self.setType = function (type) {
self.type = type;
self.removeChildren();
if (type === 'CarOnlyLeft') {
self.attachAsset('CarOnlyLeft', {
anchorX: 0.5,
anchorY: 1
});
self.width = 200;
self.height = 130;
}
};
self.getCollisionRect = function () {
return new Rectangle(self.x - 100, self.y - 65, 200, 130);
};
self.update = function () {
self.y += game.speed;
};
return self;
});
// Runner class (Penguin for crossing game)
var Runner = Container.expand(function () {
var self = Container.call(this);
var runnerSprite = self.attachAsset('runner', {
anchorX: 0.5,
anchorY: 1
});
self.width = runnerSprite.width;
self.height = runnerSprite.height;
// For collision box
self.getCollisionRect = function () {
var w = self.width * 0.7;
var h = self.height * 0.9;
return new Rectangle(self.x - w / 2, self.y - h, w, h);
};
self.update = function () {
// No-op for penguin (no auto movement)
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Play relaxMusic as soon as the game starts
LK.playMusic('RelaxMusic');
// New road asset
// Sound effects
// Jungle ground
// Power-up (not yet used, but for future)
// Coin
// Lane obstacles
// Character (runner)
// Penguin crossing game setup
// Define lanes for cars (3 lanes, horizontal) - make lanes closer together
game.laneY = [2732 - 400,
// bottom lane (closest to player)
2732 - 650,
// middle lane
2732 - 900 // top lane (furthest from player)
];
// X positions for cars (off-screen left/right)
game.carStartLeft = -200;
game.carStartRight = 2048 + 200;
// Penguin start position (bottom center, a bit further down)
game.penguinStartX = 2048 / 2;
game.penguinStartY = 2732 - 30; // moved even lower
// Game state
game.ticks = 0;
game.score = 0;
game.isGameOver = false;
// Shield state
var shieldActive = false;
var shieldTimer = 0;
var shieldDuration = 600; // 10 seconds at 60 FPS
// Arrays for cars
var cars = [];
// Add ground to the top of the screen
var topGround = LK.getAsset('ground', {
anchorX: 0.5,
anchorY: 0,
x: 2048 / 2,
y: 0
});
game.addChild(topGround);
// Add ground to the bottom of the screen
var bottomGround = LK.getAsset('ground', {
anchorX: 0.5,
anchorY: 1,
x: 2048 / 2,
y: 2732
});
game.addChild(bottomGround);
// Add multiple road assets to cover the area strictly between the top and bottom ground, with reduced vertical gaps
var roadHeight = 300; // Height of each road asset
var groundHeight = 300;
var roadStartY = groundHeight; // just below top ground
var roadEndY = 2732 - groundHeight; // just above bottom ground
var numberOfRoads = Math.ceil((roadEndY - roadStartY) / roadHeight);
for (var i = 0; i < numberOfRoads; i++) {
var roadY = roadEndY - i * roadHeight + 10; // Add slight overlap to remove visible gaps
// Clamp roadY so that the bottom of the topmost road does not go above roadStartY
if (roadY < roadStartY + roadHeight) roadY = roadStartY + roadHeight;
// Clamp roadY so that the top of the bottommost road does not go below roadEndY
if (roadY > roadEndY) roadY = roadEndY;
// Only add road if its bottom is below top ground and its top is above bottom ground
if (roadY <= roadEndY && roadY - roadHeight >= roadStartY) {
var road = LK.getAsset('road', {
anchorX: 0.5,
anchorY: 1,
x: 2048 / 2,
y: roadY
});
game.addChild(road);
}
}
// Add penguin (player)
var penguin = new Runner();
penguin.x = game.penguinStartX;
penguin.y = game.penguinStartY;
penguin.lane = 1; // start in center
game.addChild(penguin);
// Shield countdown text above penguin
var shieldCountdownTxt = new Text2('', {
size: 60,
fill: 0x00EAFF,
font: "Arial Black"
});
shieldCountdownTxt.anchor.set(0.5, 1);
shieldCountdownTxt.x = penguin.x;
shieldCountdownTxt.y = penguin.y - penguin.height - 10;
game.addChild(shieldCountdownTxt);
// Shield icon to show next to countdown
var shieldIcon = LK.getAsset('Shield', {
anchorX: 0.5,
anchorY: 1,
scaleX: 0.7,
scaleY: 0.7,
x: shieldCountdownTxt.x + 60,
y: shieldCountdownTxt.y
});
shieldIcon.visible = false;
game.addChild(shieldIcon);
// Health bar setup
var maxHealth = 3;
var penguinHealth = maxHealth;
// Health bar background (red)
var healthBarBg = LK.getAsset('ground', {
anchorX: 0,
anchorY: 0,
x: 120,
y: 30,
scaleX: 0.1,
scaleY: 0.1,
tint: 0xff0000 // red
});
LK.gui.top.addChild(healthBarBg);
// Health bar foreground (dynamic hearts)
var healthHearts = [];
for (var i = 0; i < maxHealth; i++) {
var heart = LK.getAsset('coin', {
anchorX: 0,
anchorY: 0,
x: 130 + i * 90,
y: 40,
scaleX: 0.8,
scaleY: 0.8
});
LK.gui.top.addChild(heart);
healthHearts.push(heart);
}
// Helper to update health bar
function updateHealthBar() {
for (var i = 0; i < maxHealth; i++) {
healthHearts[i].alpha = i < penguinHealth ? 1 : 0.2;
}
}
updateHealthBar();
// Score text
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Helper: spawn car
function spawnCar() {
// Randomly pick car type: 'log', 'rock', or 'pit'
// Adjusted carTypes to decrease CarOnlyLeft and increase CarOnlyLeft2 frequency
var carTypes = ['CarOnlyLeft', 'CarOnlyLeft2', 'CarOnlyLeft2', 'CarOnlyRight2', 'CarOnlyRight2', 'CarOnlyRight'];
var carType = carTypes[Math.floor(Math.random() * carTypes.length)];
// Calculate Y spawn range to use all road assets (between top and bottom ground)
var groundHeight = 300;
var minY = groundHeight; // 300
var maxY = 2732 - groundHeight; // 2432
// Random Y position between minY and maxY, covering all roads
var carY = minY + Math.random() * (maxY - minY);
if (carType === 'CarOnlyLeft') {
var car = new CarOnlyLeft();
car.x = game.carStartRight;
car.y = carY;
game.addChild(car);
cars.push(car);
} else if (carType === 'CarOnlyLeft2') {
var car = new CarOnlyLeft2();
car.x = game.carStartRight;
car.y = carY;
game.addChild(car);
cars.push(car);
} else if (carType === 'CarOnlyRight') {
var car = new CarOnlyRight();
car.x = game.carStartLeft;
car.y = carY;
game.addChild(car);
cars.push(car);
} else if (carType === 'CarOnlyRight2') {
var car = new CarOnlyRight2();
car.x = game.carStartLeft;
car.y = carY;
game.addChild(car);
cars.push(car);
}
}
// Touch/Swipe handling for penguin movement (up/down/left/right)
var touchStartX = 0,
touchStartY = 0,
touchStartTime = 0;
var swipeThreshold = 80; // px
var swipeTime = 400; // ms
game.down = function (x, y, obj) {
touchStartX = x;
touchStartY = y;
touchStartTime = Date.now();
};
game.up = function (x, y, obj) {
var dx = x - touchStartX;
var dy = y - touchStartY;
var dt = Date.now() - touchStartTime;
if (dt > swipeTime) return; // Too slow
if (Math.abs(dx) > Math.abs(dy)) {
// Horizontal swipe
if (dx > swipeThreshold) {
// Move right
if (penguin.x < 2048 - 200) penguin.x += 220;
} else if (dx < -swipeThreshold) {
// Move left
if (penguin.x > 200) {
penguin.x -= 320;
if (penguin.x < 200) penguin.x = 200;
}
}
} else {
// Vertical swipe
if (dy < -swipeThreshold) {
// Move up
if (penguin.y > road.y - road.height + 200) penguin.y -= 250; // Adjust to ensure penguin stays on the road and matches new lane distance
} else if (dy > swipeThreshold) {
// Move down
if (penguin.y < game.penguinStartY) penguin.y += 250;
}
}
};
// Main update loop
var collectableHealths = [];
game.update = function () {
if (game.isGameOver) return;
game.ticks++;
// Spawn cars at intervals
// Car spawn interval decreases as time passes (minimum 12 ticks, max 36 at start)
var minInterval = 12;
var maxInterval = 36;
var interval = Math.max(minInterval, maxInterval - Math.floor(game.ticks / 300));
if (game.ticks % interval === 0) {
spawnCar();
}
// Only spawn a new CollectableHealth or Shield if there are none currently on screen or being collected
if (collectableHealths.length === 0) {
if (typeof game.nextHealthCanSpawn === "undefined") game.nextHealthCanSpawn = true;
// First, try to spawn Shield (much lower chance)
if (game.nextHealthCanSpawn && Math.random() < 0.0007) {
var shield = new CollectableShield();
// Pick a random lane
var laneIndex = Math.floor(Math.random() * game.laneY.length);
// Penguin possible X positions
var penguinPossibleX = [];
for (var px = 200; px <= 2048 - 200; px += 320) {
penguinPossibleX.push(px);
}
var xIndex = Math.floor(Math.random() * penguinPossibleX.length);
shield.x = penguinPossibleX[xIndex];
shield.y = game.laneY[laneIndex];
game.addChild(shield);
collectableHealths.push(shield);
game.nextHealthCanSpawn = false;
} else if (game.nextHealthCanSpawn && Math.random() < 0.0015) {
var health = new CollectableHealth();
// Pick a random lane
var laneIndex = Math.floor(Math.random() * game.laneY.length);
var penguinPossibleX = [];
for (var px = 200; px <= 2048 - 200; px += 320) {
penguinPossibleX.push(px);
}
var xIndex = Math.floor(Math.random() * penguinPossibleX.length);
health.x = penguinPossibleX[xIndex];
health.y = game.laneY[laneIndex];
game.addChild(health);
collectableHealths.push(health);
game.nextHealthCanSpawn = false;
}
} else {
game.nextHealthCanSpawn = false;
}
if (collectableHealths.length === 0) {
game.nextHealthCanSpawn = true;
}
// Update cars
for (var i = cars.length - 1; i >= 0; i--) {
var car = cars[i];
// Update all cars regardless of Y position (so they use all road assets)
car.update();
// Remove if off screen
if (car.speed > 0 && car.x > 2048 + 300 || car.speed < 0 && car.x < -300) {
car.destroy();
cars.splice(i, 1);
continue;
}
// Collision with penguin
var pRect = penguin.getCollisionRect();
var cRect = car.getCollisionRect();
if (rectsIntersect(pRect, cRect)) {
if (shieldActive) {
// Shield blocks damage, just destroy car and continue
car.destroy();
cars.splice(i, 1);
continue;
}
LK.getSound('hitSound').play();
LK.getSound('chickenSound').play();
LK.effects.flashScreen(0xff0000, 800);
// Remove car after hit
car.destroy();
cars.splice(i, 1);
// Reduce health
penguinHealth--;
updateHealthBar();
if (penguinHealth <= 0) {
// Game over: play sound, then show game over popup and show score
game.isGameOver = true;
LK.getSound('GameOver').play();
LK.setTimeout(function () {
LK.showGameOver({
score: game.score
});
}, 700); // Wait 700ms for sound to play before showing game over
return;
}
// Brief invulnerability: move penguin back to start Y (optional, or just flash)
// penguin.y = game.penguinStartY;
continue;
}
}
// Update and check CollectableHealths
for (var i = collectableHealths.length - 1; i >= 0; i--) {
var health = collectableHealths[i];
health.update();
// Remove if off screen or timer expired
if (health.y > 2732 || !health.collected && typeof health.timer !== "undefined" && health.timer <= 0) {
health.destroy();
collectableHealths.splice(i, 1);
continue;
}
// Check collision with penguin
if (!health.collected && rectsIntersect(penguin.getCollisionRect(), health.getCollisionRect())) {
health.collected = true;
health.destroy();
collectableHealths.splice(i, 1);
// If it's a shield, activate shield
if (health instanceof CollectableShield) {
shieldActive = true;
shieldTimer = shieldDuration;
} else {
// Only increase health if not at max
if (penguinHealth < maxHealth) {
penguinHealth++;
updateHealthBar();
}
}
continue;
}
}
// Check if penguin touches the top ground asset (give point and respawn at start)
var penguinRect = penguin.getCollisionRect();
var topGroundRect = new Rectangle(topGround.x - topGround.width / 2, topGround.y, topGround.width, topGround.height);
if (rectsIntersect(penguinRect, topGroundRect)) {
// Give 1 point
game.score++;
scoreTxt.setText(game.score.toString());
LK.getSound('PointGained').play();
// Respawn penguin at start position
penguin.x = game.penguinStartX;
penguin.y = game.penguinStartY;
// Prevent double point if penguin is still colliding next frame
// (move penguin just below top ground)
// (No need to snap, since respawn is at start)
}
// Update shield timer
if (shieldActive) {
shieldTimer--;
// Show countdown above penguin
shieldCountdownTxt.visible = true;
shieldCountdownTxt.x = penguin.x;
shieldCountdownTxt.y = penguin.y - penguin.height - 10;
var secondsLeft = Math.ceil(shieldTimer / 60);
shieldCountdownTxt.setText(secondsLeft.toString());
// Show and position shield icon next to countdown
shieldIcon.visible = true;
shieldIcon.x = shieldCountdownTxt.x + shieldCountdownTxt.width / 2 + 40;
shieldIcon.y = shieldCountdownTxt.y - 10;
if (shieldTimer <= 0) {
shieldActive = false;
shieldCountdownTxt.visible = false;
shieldIcon.visible = false;
}
} else {
shieldCountdownTxt.visible = false;
shieldIcon.visible = false;
}
// No win for reaching the bottom, only top is the goal
};
// Rectangle intersection helper
function rectsIntersect(r1, r2) {
return !(r2.x > r1.x + r1.width || r2.x + r2.width < r1.x || r2.y > r1.y + r1.height || r2.y + r2.height < r1.y);
}
// Reset on game restart
game.on('reset', function () {
// Play relaxMusic again on reset
LK.playMusic('RelaxMusic');
// Remove all cars
for (var i = 0; i < cars.length; i++) cars[i].destroy();
cars = [];
// Remove all collectable healths
if (typeof collectableHealths !== "undefined") {
for (var i = 0; i < collectableHealths.length; i++) collectableHealths[i].destroy();
collectableHealths = [];
}
game.ticks = 0;
game.score = 0;
game.isGameOver = false;
scoreTxt.setText('0');
penguin.x = game.penguinStartX;
penguin.y = game.penguinStartY;
// Reset health
penguinHealth = maxHealth;
updateHealthBar();
// Reset shield
shieldActive = false;
shieldTimer = 0;
});
make a cobblestone sidewalk butt be sure that the image is competible with 2048x300. In-Game asset. 2d. High contrast. No shadows
make the runner as crazy chicken. In-Game asset. 2d. High contrast. No shadows
make a fancy car. In-Game asset. 2d. High contrast. No shadows
pixel hearth. In-Game asset. 2d. High contrast. No shadows
Car image but its head should be on right side not left. Make a fancy car. In-Game asset. 2d. High contrast. No shadows
Make a freeway with image ratio of 2048x2732 but freeway should be seem like top view and should go from left to right. In-Game asset. 2d. High contrast. No shadows
A pixel shield image. In-Game asset. 2d. High contrast. No shadows
Car image but its head should be on right side not left. Make a fancy car colored green.. In-Game asset. 2d. High contrast. No shadows