/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Bubble = Container.expand(function () { var self = Container.call(this); var bubbleGraphics = self.attachAsset('bubble', { anchorX: 0.5, anchorY: 0.5 }); self.speed = -5; self.collected = false; // Add a small bobbing motion to the bubble self.bobSpeed = 0.5 + Math.random() * 0.5; self.bobAmount = 10; self.bobOffset = Math.random() * Math.PI * 2; self.startY = 0; self.update = function () { self.x += self.speed; // Bob up and down if (self.startY) { self.y = self.startY + Math.sin(LK.ticks * 0.02 * self.bobSpeed + self.bobOffset) * self.bobAmount; } else { self.startY = self.y; } // Remove when off screen if (self.x < -50) { self.destroy(); var index = game.bubbles.indexOf(self); if (index !== -1) { game.bubbles.splice(index, 1); } } }; self.collect = function () { if (self.collected) { return; } self.collected = true; // Play collect sound LK.getSound('collect').play(); // Add score game.score += 5; scoreTxt.setText(game.score.toString()); // Show visual effect LK.effects.flashObject(self, 0xffffff, 200); // Animate bubble scaling up and fading out tween(self, { scaleX: 2, scaleY: 2, alpha: 0 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { self.destroy(); var index = game.bubbles.indexOf(self); if (index !== -1) { game.bubbles.splice(index, 1); } } }); }; return self; }); var Coral = Container.expand(function () { var self = Container.call(this); var coralGraphics = self.attachAsset('coral', { anchorX: 0.5, anchorY: 0.5 }); self.speed = -5; self.passed = false; self.update = function () { self.x += self.speed; // Mark as passed for scoring if (!self.passed && self.x < 600) { self.passed = true; if (!game.gameOver) { game.score++; scoreTxt.setText(game.score.toString()); } } // Remove when off screen if (self.x < -100) { self.destroy(); var index = game.obstacles.indexOf(self); if (index !== -1) { game.obstacles.splice(index, 1); } } }; return self; }); var Fish = Container.expand(function () { var self = Container.call(this); var fishGraphics = self.attachAsset('fish', { anchorX: 0.5, anchorY: 0.5 }); self.velocity = 0; self.gravity = 0.4; // Reduced gravity for smoother falling self.jumpPower = -8; // Lower jump power for more controlled swimming self.rotation = 0; self.isDead = false; self.lastY = 0; // Track last position for collision detection self.jump = function () { if (self.isDead) { return; } // Apply jump/swim impulse self.velocity = self.jumpPower; // Play swimming sound LK.getSound('swim').play(); // Add a slight upward animation using tween tween(fishGraphics, { scaleX: 1.2, scaleY: 0.8 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { tween(fishGraphics, { scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.easeOut }); } }); }; self.update = function () { if (self.isDead) { return; } // Track last position for collision detection self.lastY = self.y; // Apply physics self.velocity += self.gravity; self.y += self.velocity; // Add water resistance (makes movement more fish-like) self.velocity *= 0.95; // Rotate fish based on velocity - smoother rotation for swimming feel var targetRotation = Math.min(Math.max(self.velocity * 0.04, -0.5), 0.5); self.rotation += (targetRotation - self.rotation) * 0.2; fishGraphics.rotation = self.rotation; // Keep fish within bounds if (self.y < 50) { self.y = 50; self.velocity = 0; } if (self.y > 2732 - 50) { self.y = 2732 - 50; self.velocity = 0; self.die(); } }; self.die = function () { if (self.isDead) { return; } self.isDead = true; LK.getSound('hit').play(); LK.effects.flashObject(self, 0xff0000, 500); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x0066aa }); /**** * Game Code ****/ // Create background var background = game.addChild(LK.getAsset('background', { anchorX: 0, anchorY: 0, x: 0, y: 0 })); // Game variables game.speed = 5; game.score = 0; game.gameOver = false; game.obstacles = []; game.bubbles = []; game.difficulty = 1; game.lastObstacleTime = 0; game.lastBubbleTime = 0; // Create score text var scoreTxt = new Text2('0', { size: 100, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); scoreTxt.y = 50; // Create instructions text (disappears after game starts) var instructionsTxt = new Text2('Tap to make the fish swim!', { size: 60, fill: 0xFFFFFF }); instructionsTxt.anchor.set(0.5, 0.5); LK.gui.center.addChild(instructionsTxt); // Create fish var fish = new Fish(); fish.x = 400; fish.y = 2732 / 2; game.addChild(fish); // Game has started flag var gameStarted = false; // Function to start game function startGame() { if (gameStarted) { return; } gameStarted = true; instructionsTxt.visible = false; // Start background music LK.playMusic('underwater'); } // Function to spawn a new coral obstacle function spawnObstacle() { // Calculate gap size based on difficulty var gapSize = 500 - game.difficulty * 30; gapSize = Math.max(gapSize, 200); // Minimum gap size // Calculate random position for the gap var gapPosition = 300 + Math.random() * (2732 - 600); // Create top coral var topCoral = new Coral(); topCoral.x = 2200; topCoral.y = gapPosition - gapSize / 2; topCoral.scaleY = (gapPosition - gapSize / 2) / 250; // Scale to reach from top to gap game.addChild(topCoral); game.obstacles.push(topCoral); // Create bottom coral var bottomCoral = new Coral(); bottomCoral.x = 2200; bottomCoral.y = gapPosition + gapSize / 2 + 250; bottomCoral.scaleY = (2732 - (gapPosition + gapSize / 2)) / 250; // Scale to reach from gap to bottom game.addChild(bottomCoral); game.obstacles.push(bottomCoral); // Increase difficulty as game progresses game.difficulty += 0.1; // Increase speed slightly game.speed = Math.min(game.speed + 0.1, 12); // Update all obstacle speeds for (var i = 0; i < game.obstacles.length; i++) { game.obstacles[i].speed = -game.speed; } // Update all bubble speeds for (var i = 0; i < game.bubbles.length; i++) { game.bubbles[i].speed = -game.speed; } } // Function to spawn a bubble function spawnBubble() { var bubble = new Bubble(); bubble.x = 2200; bubble.y = 300 + Math.random() * (2732 - 600); bubble.speed = -game.speed; game.addChild(bubble); game.bubbles.push(bubble); } // Check for collisions function checkCollisions() { if (fish.isDead) { return; } // Check collision with obstacles for (var i = 0; i < game.obstacles.length; i++) { if (fish.intersects(game.obstacles[i])) { fish.die(); gameOver(); return; } } // Check collision with bubbles for (var i = game.bubbles.length - 1; i >= 0; i--) { if (!game.bubbles[i].collected && fish.intersects(game.bubbles[i])) { game.bubbles[i].collect(); } } } // Game over function function gameOver() { game.gameOver = true; // Save high score var highScore = storage.highScore || 0; if (game.score > highScore) { storage.highScore = game.score; } // Show game over screen LK.setTimeout(function () { LK.showGameOver(); }, 1000); } // Track if player is holding down for continuous swimming var isHolding = false; var holdTimer = 0; var lastJumpTime = 0; // Handle input down game.down = function (x, y, obj) { if (!gameStarted) { startGame(); } fish.jump(); isHolding = true; lastJumpTime = LK.ticks; }; // Handle input up game.up = function (x, y, obj) { isHolding = false; }; // Game update function game.update = function () { if (!gameStarted) { return; } // Handle continuous swimming when holding if (isHolding && !fish.isDead) { holdTimer++; // Make the fish swim every 15 ticks while holding if (holdTimer >= 15) { fish.jump(); holdTimer = 0; } } else { holdTimer = 0; } // Spawn obstacles if (LK.ticks - game.lastObstacleTime > 120) { game.lastObstacleTime = LK.ticks; spawnObstacle(); } // Spawn bubbles if (LK.ticks - game.lastBubbleTime > 60) { game.lastBubbleTime = LK.ticks; if (Math.random() < 0.3) { // 30% chance to spawn a bubble spawnBubble(); } } // Check collisions checkCollisions(); };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Bubble = Container.expand(function () {
var self = Container.call(this);
var bubbleGraphics = self.attachAsset('bubble', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -5;
self.collected = false;
// Add a small bobbing motion to the bubble
self.bobSpeed = 0.5 + Math.random() * 0.5;
self.bobAmount = 10;
self.bobOffset = Math.random() * Math.PI * 2;
self.startY = 0;
self.update = function () {
self.x += self.speed;
// Bob up and down
if (self.startY) {
self.y = self.startY + Math.sin(LK.ticks * 0.02 * self.bobSpeed + self.bobOffset) * self.bobAmount;
} else {
self.startY = self.y;
}
// Remove when off screen
if (self.x < -50) {
self.destroy();
var index = game.bubbles.indexOf(self);
if (index !== -1) {
game.bubbles.splice(index, 1);
}
}
};
self.collect = function () {
if (self.collected) {
return;
}
self.collected = true;
// Play collect sound
LK.getSound('collect').play();
// Add score
game.score += 5;
scoreTxt.setText(game.score.toString());
// Show visual effect
LK.effects.flashObject(self, 0xffffff, 200);
// Animate bubble scaling up and fading out
tween(self, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
self.destroy();
var index = game.bubbles.indexOf(self);
if (index !== -1) {
game.bubbles.splice(index, 1);
}
}
});
};
return self;
});
var Coral = Container.expand(function () {
var self = Container.call(this);
var coralGraphics = self.attachAsset('coral', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -5;
self.passed = false;
self.update = function () {
self.x += self.speed;
// Mark as passed for scoring
if (!self.passed && self.x < 600) {
self.passed = true;
if (!game.gameOver) {
game.score++;
scoreTxt.setText(game.score.toString());
}
}
// Remove when off screen
if (self.x < -100) {
self.destroy();
var index = game.obstacles.indexOf(self);
if (index !== -1) {
game.obstacles.splice(index, 1);
}
}
};
return self;
});
var Fish = Container.expand(function () {
var self = Container.call(this);
var fishGraphics = self.attachAsset('fish', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocity = 0;
self.gravity = 0.4; // Reduced gravity for smoother falling
self.jumpPower = -8; // Lower jump power for more controlled swimming
self.rotation = 0;
self.isDead = false;
self.lastY = 0; // Track last position for collision detection
self.jump = function () {
if (self.isDead) {
return;
}
// Apply jump/swim impulse
self.velocity = self.jumpPower;
// Play swimming sound
LK.getSound('swim').play();
// Add a slight upward animation using tween
tween(fishGraphics, {
scaleX: 1.2,
scaleY: 0.8
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(fishGraphics, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeOut
});
}
});
};
self.update = function () {
if (self.isDead) {
return;
}
// Track last position for collision detection
self.lastY = self.y;
// Apply physics
self.velocity += self.gravity;
self.y += self.velocity;
// Add water resistance (makes movement more fish-like)
self.velocity *= 0.95;
// Rotate fish based on velocity - smoother rotation for swimming feel
var targetRotation = Math.min(Math.max(self.velocity * 0.04, -0.5), 0.5);
self.rotation += (targetRotation - self.rotation) * 0.2;
fishGraphics.rotation = self.rotation;
// Keep fish within bounds
if (self.y < 50) {
self.y = 50;
self.velocity = 0;
}
if (self.y > 2732 - 50) {
self.y = 2732 - 50;
self.velocity = 0;
self.die();
}
};
self.die = function () {
if (self.isDead) {
return;
}
self.isDead = true;
LK.getSound('hit').play();
LK.effects.flashObject(self, 0xff0000, 500);
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x0066aa
});
/****
* Game Code
****/
// Create background
var background = game.addChild(LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
}));
// Game variables
game.speed = 5;
game.score = 0;
game.gameOver = false;
game.obstacles = [];
game.bubbles = [];
game.difficulty = 1;
game.lastObstacleTime = 0;
game.lastBubbleTime = 0;
// Create score text
var scoreTxt = new Text2('0', {
size: 100,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
scoreTxt.y = 50;
// Create instructions text (disappears after game starts)
var instructionsTxt = new Text2('Tap to make the fish swim!', {
size: 60,
fill: 0xFFFFFF
});
instructionsTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(instructionsTxt);
// Create fish
var fish = new Fish();
fish.x = 400;
fish.y = 2732 / 2;
game.addChild(fish);
// Game has started flag
var gameStarted = false;
// Function to start game
function startGame() {
if (gameStarted) {
return;
}
gameStarted = true;
instructionsTxt.visible = false;
// Start background music
LK.playMusic('underwater');
}
// Function to spawn a new coral obstacle
function spawnObstacle() {
// Calculate gap size based on difficulty
var gapSize = 500 - game.difficulty * 30;
gapSize = Math.max(gapSize, 200); // Minimum gap size
// Calculate random position for the gap
var gapPosition = 300 + Math.random() * (2732 - 600);
// Create top coral
var topCoral = new Coral();
topCoral.x = 2200;
topCoral.y = gapPosition - gapSize / 2;
topCoral.scaleY = (gapPosition - gapSize / 2) / 250; // Scale to reach from top to gap
game.addChild(topCoral);
game.obstacles.push(topCoral);
// Create bottom coral
var bottomCoral = new Coral();
bottomCoral.x = 2200;
bottomCoral.y = gapPosition + gapSize / 2 + 250;
bottomCoral.scaleY = (2732 - (gapPosition + gapSize / 2)) / 250; // Scale to reach from gap to bottom
game.addChild(bottomCoral);
game.obstacles.push(bottomCoral);
// Increase difficulty as game progresses
game.difficulty += 0.1;
// Increase speed slightly
game.speed = Math.min(game.speed + 0.1, 12);
// Update all obstacle speeds
for (var i = 0; i < game.obstacles.length; i++) {
game.obstacles[i].speed = -game.speed;
}
// Update all bubble speeds
for (var i = 0; i < game.bubbles.length; i++) {
game.bubbles[i].speed = -game.speed;
}
}
// Function to spawn a bubble
function spawnBubble() {
var bubble = new Bubble();
bubble.x = 2200;
bubble.y = 300 + Math.random() * (2732 - 600);
bubble.speed = -game.speed;
game.addChild(bubble);
game.bubbles.push(bubble);
}
// Check for collisions
function checkCollisions() {
if (fish.isDead) {
return;
}
// Check collision with obstacles
for (var i = 0; i < game.obstacles.length; i++) {
if (fish.intersects(game.obstacles[i])) {
fish.die();
gameOver();
return;
}
}
// Check collision with bubbles
for (var i = game.bubbles.length - 1; i >= 0; i--) {
if (!game.bubbles[i].collected && fish.intersects(game.bubbles[i])) {
game.bubbles[i].collect();
}
}
}
// Game over function
function gameOver() {
game.gameOver = true;
// Save high score
var highScore = storage.highScore || 0;
if (game.score > highScore) {
storage.highScore = game.score;
}
// Show game over screen
LK.setTimeout(function () {
LK.showGameOver();
}, 1000);
}
// Track if player is holding down for continuous swimming
var isHolding = false;
var holdTimer = 0;
var lastJumpTime = 0;
// Handle input down
game.down = function (x, y, obj) {
if (!gameStarted) {
startGame();
}
fish.jump();
isHolding = true;
lastJumpTime = LK.ticks;
};
// Handle input up
game.up = function (x, y, obj) {
isHolding = false;
};
// Game update function
game.update = function () {
if (!gameStarted) {
return;
}
// Handle continuous swimming when holding
if (isHolding && !fish.isDead) {
holdTimer++;
// Make the fish swim every 15 ticks while holding
if (holdTimer >= 15) {
fish.jump();
holdTimer = 0;
}
} else {
holdTimer = 0;
}
// Spawn obstacles
if (LK.ticks - game.lastObstacleTime > 120) {
game.lastObstacleTime = LK.ticks;
spawnObstacle();
}
// Spawn bubbles
if (LK.ticks - game.lastBubbleTime > 60) {
game.lastBubbleTime = LK.ticks;
if (Math.random() < 0.3) {
// 30% chance to spawn a bubble
spawnBubble();
}
}
// Check collisions
checkCollisions();
};
A fish. Single Game Texture. In-Game asset. Blank background. High contrast. No shadows
A bubble. Single Game Texture. In-Game asset. No shadows
Blue coral. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows. Awesome. Cool
Water. Single Game Texture. In-Game asset. 2d. High contrast. No shadows. Water