/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ // Coin class var Coin = Container.expand(function () { var self = Container.call(this); var coinSprite = self.attachAsset('coin', { anchorX: 0.5, anchorY: 0.5 }); self.collected = false; // Move left every frame self.update = function () { self.x -= obstacleSpeed; }; return self; }); // Obstacle (coral/rock) class var Obstacle = Container.expand(function () { var self = Container.call(this); // Top or bottom self.isTop = false; self.passed = false; // Attach asset var obs = self.attachAsset('obstacle', { anchorX: 0.5, anchorY: 0.0 }); // For top obstacles, anchor to top instead of flipping self.setTop = function () { self.isTop = true; obs.anchorY = 0.0; }; // Move left every frame self.update = function () { self.x -= obstacleSpeed; }; return self; }); // Penguin class var Penguin = Container.expand(function () { var self = Container.call(this); var penguinSprite = self.attachAsset('penguin', { anchorX: 0.5, anchorY: 0.5 }); self.velocity = 0; self.gravity = 1.1; // Halved for 50% slower fall self.lift = -24; // Halved for 50% slower upward movement self.maxFall = 18; // Halved for 50% slower max fall speed self.alive = true; // Flap animation self.flap = function () { if (!self.alive) return; self.velocity = self.lift; // Animate penguin up a bit tween(self, { rotation: -0.35 }, { duration: 120, easing: tween.cubicOut }); }; // Call every frame self.update = function () { if (!self.alive || !gameStarted) return; self.velocity += self.gravity; if (self.velocity > self.maxFall) self.velocity = self.maxFall; self.y += self.velocity; // Rotate penguin based on velocity var targetRot = Math.max(-0.4, Math.min(0.7, self.velocity / 50)); tween(self, { rotation: targetRot }, { duration: 120, easing: tween.linear }); }; // On death self.die = function () { self.alive = false; tween(self, { rotation: 1.2 }, { duration: 400, easing: tween.cubicIn }); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x6ec6f7 }); /**** * Game Code ****/ // Background (ocean blue, for parallax effect) // Sea floor/ceiling // Obstacle (coral/rock) - top and bottom // Penguin (player) // Game constants var GAP_SIZE = 420; // Gap between top and bottom obstacles var OBSTACLE_INTERVAL = 120; // Frames between obstacles (doubled for 50% slower spawn) var obstacleSpeed = 9; // Halved for 50% slower movement var penguinStartX = 600; var penguinStartY = 1366; var floorY = 2732 - 80; var ceilingY = 80; // Background var bg = LK.getAsset('bgwave', { anchorX: 0, anchorY: 0, x: 0, y: 0 }); game.addChild(bg); // Sea floor var floor = LK.getAsset('seabound', { anchorX: 0, anchorY: 0, x: 0, y: floorY }); game.addChild(floor); // Sea ceiling var ceiling = LK.getAsset('seabound', { anchorX: 0, anchorY: 0, x: 0, y: 0 }); game.addChild(ceiling); // Penguin var penguin = new Penguin(); penguin.x = penguinStartX; penguin.y = penguinStartY; penguin.visible = false; // Hide initially game.addChild(penguin); // Obstacles array var obstacles = []; // Coins array var coins = []; // Score var score = 0; var highScore = storage.highScore || 0; var scoreTxt = new Text2('0', { size: 150, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); scoreTxt.visible = false; // Hide initially LK.gui.top.addChild(scoreTxt); // Game states var gameStarted = false; var gameOver = false; var showStartScreen = true; // Start screen var startScreen = new Container(); var startBg = LK.getAsset('bgwave', { anchorX: 0, anchorY: 0, x: 0, y: 0 }); startScreen.addChild(startBg); var startText = new Text2('START', { size: 200, fill: 0xFFFFFF }); startText.anchor.set(0.5, 0.5); startText.x = 2048 / 2; startText.y = 2732 / 2; startScreen.addChild(startText); game.addChild(startScreen); // Play music on start screen LK.playMusic('OyunMuzigi'); // Start the game on first tap function startGame() { if (!gameStarted) { gameStarted = true; // Hide start screen and show game elements if (showStartScreen) { startScreen.visible = false; showStartScreen = false; penguin.visible = true; scoreTxt.visible = true; // Stop music when game starts LK.stopMusic(); } } penguin.flap(); // Play penguin sound on tap LK.getSound('PenguenSesi').play(); } // Touch/click events game.down = function (x, y, obj) { if (gameOver) return; startGame(); }; // No drag, so no move/up needed // Main update loop game.update = function () { if (gameOver || !gameStarted) return; // Penguin physics penguin.update(); // Prevent penguin from going off top/ceiling if (penguin.y - 60 < ceilingY) { penguin.y = ceilingY + 60; penguin.velocity = 0; } // Prevent penguin from going below floor if (penguin.y + 60 > floorY) { penguin.y = floorY - 60; penguin.velocity = 0; penguin.die(); endGame(); return; } // Spawn obstacles as pairs: one at the very top, one at the very bottom, with a gap in between if (gameStarted && LK.ticks % OBSTACLE_INTERVAL === 0) { // Randomize the vertical position of the gap (gapY is the center of the gap) var minGapY = ceilingY + GAP_SIZE / 2 + 40; var maxGapY = floorY - GAP_SIZE / 2 - 40; var gapY = Math.floor(minGapY + Math.random() * (maxGapY - minGapY)); // Top obstacle: anchored at the very top, height is up to the start of the gap var topObs = new Obstacle(); topObs.setTop(); topObs.x = 2048 + 90; // Place at y=ceilingY, and scale the obstacle to reach the gap topObs.y = ceilingY; // Set the height of the top obstacle so its bottom is at gapY - GAP_SIZE/2 var topHeight = gapY - GAP_SIZE / 2 - ceilingY; if (topHeight < 40) topHeight = 40; // minimum height topObs.children[0].height = topHeight; obstacles.push(topObs); game.addChild(topObs); // Bottom obstacle: anchored at the very bottom, height is from gap to floor var botObs = new Obstacle(); botObs.x = 2048 + 90; // Place at y = gapY + GAP_SIZE/2 botObs.y = gapY + GAP_SIZE / 2; // Set the height of the bottom obstacle so its top is at gapY + GAP_SIZE/2, and bottom is at floorY var botHeight = floorY - (gapY + GAP_SIZE / 2); if (botHeight < 40) botHeight = 40; // minimum height botObs.children[0].height = botHeight; obstacles.push(botObs); game.addChild(botObs); // Spawn coin in the center of the gap with 85% probability if (Math.random() < 0.85) { var coin = new Coin(); coin.x = 2048 + 90; coin.y = gapY; // Center of the gap coins.push(coin); game.addChild(coin); } } // Move and manage obstacles for (var i = obstacles.length - 1; i >= 0; i--) { var obs = obstacles[i]; obs.update(); // Remove if off screen if (obs.x < -200) { obs.destroy(); obstacles.splice(i, 1); continue; } // Track last intersection state for each obstacle if (obs.lastWasIntersecting === undefined) obs.lastWasIntersecting = false; var isIntersecting = penguin.intersects(obs); // Collision with penguin: only trigger on first contact (track per obstacle, not on penguin) if (penguin.alive && !obs.lastWasIntersecting && isIntersecting) { penguin.die(); endGame(); return; } obs.lastWasIntersecting = isIntersecting; // Track passing for obstacles and increase thickness if (!obs.isTop && !obs.passed && obs.x + 90 < penguin.x) { obs.passed = true; // Increase thickness of both top and bottom obstacles in the pair // Find the matching obstacle pair for (var k = 0; k < obstacles.length; k++) { var pairedObs = obstacles[k]; // Same x position means they're part of the same obstacle pair if (Math.abs(pairedObs.x - obs.x) < 10) { pairedObs.children[0].width += 20; // Increase width by 20 pixels } } } } // Move and manage coins for (var j = coins.length - 1; j >= 0; j--) { var coin = coins[j]; coin.update(); // Remove if off screen if (coin.x < -200) { coin.destroy(); coins.splice(j, 1); continue; } // Check collision with penguin if (!coin.collected && penguin.alive && penguin.intersects(coin)) { coin.collected = true; score += 1; scoreTxt.setText(score); // Remove coin coin.destroy(); coins.splice(j, 1); } } }; // End game function endGame() { gameOver = true; // Check and update high score if (score > highScore) { highScore = score; storage.highScore = highScore; } // Flash red LK.effects.flashScreen(0xff0000, 800); // Show custom game over screen LK.setTimeout(function () { showGameOverScreen(); }, 800); } // Custom game over screen function showGameOverScreen() { // Create game over container var gameOverScreen = new Container(); // Semi-transparent background var gameOverBg = LK.getAsset('bgwave', { anchorX: 0, anchorY: 0, x: 0, y: 0 }); gameOverBg.alpha = 0.8; gameOverScreen.addChild(gameOverBg); // Game Over text var gameOverText = new Text2('GAME OVER', { size: 180, fill: 0xFFFFFF }); gameOverText.anchor.set(0.5, 0.5); gameOverText.x = 2048 / 2; gameOverText.y = 2732 / 2 - 200; gameOverScreen.addChild(gameOverText); // Restart instruction var restartText = new Text2('TAP TO RESTART', { size: 100, fill: 0xFFFFFF }); restartText.anchor.set(0.5, 0.5); restartText.x = 2048 / 2; restartText.y = 2732 / 2 - 50; gameOverScreen.addChild(restartText); // High score text var highScoreText = new Text2('Best: ' + highScore, { size: 120, fill: 0xFFD700 }); highScoreText.anchor.set(0.5, 0.5); highScoreText.x = 2048 / 2; highScoreText.y = 2732 / 2 + 200; gameOverScreen.addChild(highScoreText); // Current score text var currentScoreText = new Text2('Score: ' + score, { size: 120, fill: 0xFFFFFF }); currentScoreText.anchor.set(0.5, 0.5); currentScoreText.x = 2048 / 2; currentScoreText.y = 2732 / 2 + 320; gameOverScreen.addChild(currentScoreText); game.addChild(gameOverScreen); // Add tap to restart functionality gameOverScreen.down = function (x, y, obj) { // Reset game gameOver = false; gameStarted = false; showStartScreen = true; score = 0; scoreTxt.setText('0'); penguin.x = penguinStartX; penguin.y = penguinStartY; penguin.velocity = 0; penguin.alive = true; penguin.rotation = 0; penguin.visible = false; scoreTxt.visible = false; // Clear obstacles for (var i = obstacles.length - 1; i >= 0; i--) { obstacles[i].destroy(); obstacles.splice(i, 1); } // Clear coins for (var j = coins.length - 1; j >= 0; j--) { coins[j].destroy(); coins.splice(j, 1); } // Remove game over screen and show start screen gameOverScreen.destroy(); startScreen.visible = true; // Play music again when returning to start screen LK.playMusic('OyunMuzigi'); }; } ;
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// Coin class
var Coin = Container.expand(function () {
var self = Container.call(this);
var coinSprite = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
self.collected = false;
// Move left every frame
self.update = function () {
self.x -= obstacleSpeed;
};
return self;
});
// Obstacle (coral/rock) class
var Obstacle = Container.expand(function () {
var self = Container.call(this);
// Top or bottom
self.isTop = false;
self.passed = false;
// Attach asset
var obs = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.0
});
// For top obstacles, anchor to top instead of flipping
self.setTop = function () {
self.isTop = true;
obs.anchorY = 0.0;
};
// Move left every frame
self.update = function () {
self.x -= obstacleSpeed;
};
return self;
});
// Penguin class
var Penguin = Container.expand(function () {
var self = Container.call(this);
var penguinSprite = self.attachAsset('penguin', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocity = 0;
self.gravity = 1.1; // Halved for 50% slower fall
self.lift = -24; // Halved for 50% slower upward movement
self.maxFall = 18; // Halved for 50% slower max fall speed
self.alive = true;
// Flap animation
self.flap = function () {
if (!self.alive) return;
self.velocity = self.lift;
// Animate penguin up a bit
tween(self, {
rotation: -0.35
}, {
duration: 120,
easing: tween.cubicOut
});
};
// Call every frame
self.update = function () {
if (!self.alive || !gameStarted) return;
self.velocity += self.gravity;
if (self.velocity > self.maxFall) self.velocity = self.maxFall;
self.y += self.velocity;
// Rotate penguin based on velocity
var targetRot = Math.max(-0.4, Math.min(0.7, self.velocity / 50));
tween(self, {
rotation: targetRot
}, {
duration: 120,
easing: tween.linear
});
};
// On death
self.die = function () {
self.alive = false;
tween(self, {
rotation: 1.2
}, {
duration: 400,
easing: tween.cubicIn
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x6ec6f7
});
/****
* Game Code
****/
// Background (ocean blue, for parallax effect)
// Sea floor/ceiling
// Obstacle (coral/rock) - top and bottom
// Penguin (player)
// Game constants
var GAP_SIZE = 420; // Gap between top and bottom obstacles
var OBSTACLE_INTERVAL = 120; // Frames between obstacles (doubled for 50% slower spawn)
var obstacleSpeed = 9; // Halved for 50% slower movement
var penguinStartX = 600;
var penguinStartY = 1366;
var floorY = 2732 - 80;
var ceilingY = 80;
// Background
var bg = LK.getAsset('bgwave', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
game.addChild(bg);
// Sea floor
var floor = LK.getAsset('seabound', {
anchorX: 0,
anchorY: 0,
x: 0,
y: floorY
});
game.addChild(floor);
// Sea ceiling
var ceiling = LK.getAsset('seabound', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
game.addChild(ceiling);
// Penguin
var penguin = new Penguin();
penguin.x = penguinStartX;
penguin.y = penguinStartY;
penguin.visible = false; // Hide initially
game.addChild(penguin);
// Obstacles array
var obstacles = [];
// Coins array
var coins = [];
// Score
var score = 0;
var highScore = storage.highScore || 0;
var scoreTxt = new Text2('0', {
size: 150,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
scoreTxt.visible = false; // Hide initially
LK.gui.top.addChild(scoreTxt);
// Game states
var gameStarted = false;
var gameOver = false;
var showStartScreen = true;
// Start screen
var startScreen = new Container();
var startBg = LK.getAsset('bgwave', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
startScreen.addChild(startBg);
var startText = new Text2('START', {
size: 200,
fill: 0xFFFFFF
});
startText.anchor.set(0.5, 0.5);
startText.x = 2048 / 2;
startText.y = 2732 / 2;
startScreen.addChild(startText);
game.addChild(startScreen);
// Play music on start screen
LK.playMusic('OyunMuzigi');
// Start the game on first tap
function startGame() {
if (!gameStarted) {
gameStarted = true;
// Hide start screen and show game elements
if (showStartScreen) {
startScreen.visible = false;
showStartScreen = false;
penguin.visible = true;
scoreTxt.visible = true;
// Stop music when game starts
LK.stopMusic();
}
}
penguin.flap();
// Play penguin sound on tap
LK.getSound('PenguenSesi').play();
}
// Touch/click events
game.down = function (x, y, obj) {
if (gameOver) return;
startGame();
};
// No drag, so no move/up needed
// Main update loop
game.update = function () {
if (gameOver || !gameStarted) return;
// Penguin physics
penguin.update();
// Prevent penguin from going off top/ceiling
if (penguin.y - 60 < ceilingY) {
penguin.y = ceilingY + 60;
penguin.velocity = 0;
}
// Prevent penguin from going below floor
if (penguin.y + 60 > floorY) {
penguin.y = floorY - 60;
penguin.velocity = 0;
penguin.die();
endGame();
return;
}
// Spawn obstacles as pairs: one at the very top, one at the very bottom, with a gap in between
if (gameStarted && LK.ticks % OBSTACLE_INTERVAL === 0) {
// Randomize the vertical position of the gap (gapY is the center of the gap)
var minGapY = ceilingY + GAP_SIZE / 2 + 40;
var maxGapY = floorY - GAP_SIZE / 2 - 40;
var gapY = Math.floor(minGapY + Math.random() * (maxGapY - minGapY));
// Top obstacle: anchored at the very top, height is up to the start of the gap
var topObs = new Obstacle();
topObs.setTop();
topObs.x = 2048 + 90;
// Place at y=ceilingY, and scale the obstacle to reach the gap
topObs.y = ceilingY;
// Set the height of the top obstacle so its bottom is at gapY - GAP_SIZE/2
var topHeight = gapY - GAP_SIZE / 2 - ceilingY;
if (topHeight < 40) topHeight = 40; // minimum height
topObs.children[0].height = topHeight;
obstacles.push(topObs);
game.addChild(topObs);
// Bottom obstacle: anchored at the very bottom, height is from gap to floor
var botObs = new Obstacle();
botObs.x = 2048 + 90;
// Place at y = gapY + GAP_SIZE/2
botObs.y = gapY + GAP_SIZE / 2;
// Set the height of the bottom obstacle so its top is at gapY + GAP_SIZE/2, and bottom is at floorY
var botHeight = floorY - (gapY + GAP_SIZE / 2);
if (botHeight < 40) botHeight = 40; // minimum height
botObs.children[0].height = botHeight;
obstacles.push(botObs);
game.addChild(botObs);
// Spawn coin in the center of the gap with 85% probability
if (Math.random() < 0.85) {
var coin = new Coin();
coin.x = 2048 + 90;
coin.y = gapY; // Center of the gap
coins.push(coin);
game.addChild(coin);
}
}
// Move and manage obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
var obs = obstacles[i];
obs.update();
// Remove if off screen
if (obs.x < -200) {
obs.destroy();
obstacles.splice(i, 1);
continue;
}
// Track last intersection state for each obstacle
if (obs.lastWasIntersecting === undefined) obs.lastWasIntersecting = false;
var isIntersecting = penguin.intersects(obs);
// Collision with penguin: only trigger on first contact (track per obstacle, not on penguin)
if (penguin.alive && !obs.lastWasIntersecting && isIntersecting) {
penguin.die();
endGame();
return;
}
obs.lastWasIntersecting = isIntersecting;
// Track passing for obstacles and increase thickness
if (!obs.isTop && !obs.passed && obs.x + 90 < penguin.x) {
obs.passed = true;
// Increase thickness of both top and bottom obstacles in the pair
// Find the matching obstacle pair
for (var k = 0; k < obstacles.length; k++) {
var pairedObs = obstacles[k];
// Same x position means they're part of the same obstacle pair
if (Math.abs(pairedObs.x - obs.x) < 10) {
pairedObs.children[0].width += 20; // Increase width by 20 pixels
}
}
}
}
// Move and manage coins
for (var j = coins.length - 1; j >= 0; j--) {
var coin = coins[j];
coin.update();
// Remove if off screen
if (coin.x < -200) {
coin.destroy();
coins.splice(j, 1);
continue;
}
// Check collision with penguin
if (!coin.collected && penguin.alive && penguin.intersects(coin)) {
coin.collected = true;
score += 1;
scoreTxt.setText(score);
// Remove coin
coin.destroy();
coins.splice(j, 1);
}
}
};
// End game
function endGame() {
gameOver = true;
// Check and update high score
if (score > highScore) {
highScore = score;
storage.highScore = highScore;
}
// Flash red
LK.effects.flashScreen(0xff0000, 800);
// Show custom game over screen
LK.setTimeout(function () {
showGameOverScreen();
}, 800);
}
// Custom game over screen
function showGameOverScreen() {
// Create game over container
var gameOverScreen = new Container();
// Semi-transparent background
var gameOverBg = LK.getAsset('bgwave', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
gameOverBg.alpha = 0.8;
gameOverScreen.addChild(gameOverBg);
// Game Over text
var gameOverText = new Text2('GAME OVER', {
size: 180,
fill: 0xFFFFFF
});
gameOverText.anchor.set(0.5, 0.5);
gameOverText.x = 2048 / 2;
gameOverText.y = 2732 / 2 - 200;
gameOverScreen.addChild(gameOverText);
// Restart instruction
var restartText = new Text2('TAP TO RESTART', {
size: 100,
fill: 0xFFFFFF
});
restartText.anchor.set(0.5, 0.5);
restartText.x = 2048 / 2;
restartText.y = 2732 / 2 - 50;
gameOverScreen.addChild(restartText);
// High score text
var highScoreText = new Text2('Best: ' + highScore, {
size: 120,
fill: 0xFFD700
});
highScoreText.anchor.set(0.5, 0.5);
highScoreText.x = 2048 / 2;
highScoreText.y = 2732 / 2 + 200;
gameOverScreen.addChild(highScoreText);
// Current score text
var currentScoreText = new Text2('Score: ' + score, {
size: 120,
fill: 0xFFFFFF
});
currentScoreText.anchor.set(0.5, 0.5);
currentScoreText.x = 2048 / 2;
currentScoreText.y = 2732 / 2 + 320;
gameOverScreen.addChild(currentScoreText);
game.addChild(gameOverScreen);
// Add tap to restart functionality
gameOverScreen.down = function (x, y, obj) {
// Reset game
gameOver = false;
gameStarted = false;
showStartScreen = true;
score = 0;
scoreTxt.setText('0');
penguin.x = penguinStartX;
penguin.y = penguinStartY;
penguin.velocity = 0;
penguin.alive = true;
penguin.rotation = 0;
penguin.visible = false;
scoreTxt.visible = false;
// Clear obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
obstacles[i].destroy();
obstacles.splice(i, 1);
}
// Clear coins
for (var j = coins.length - 1; j >= 0; j--) {
coins[j].destroy();
coins.splice(j, 1);
}
// Remove game over screen and show start screen
gameOverScreen.destroy();
startScreen.visible = true;
// Play music again when returning to start screen
LK.playMusic('OyunMuzigi');
};
}
;