/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Enemy Fish Class var EnemyFish = Container.expand(function () { var self = Container.call(this); // Will be set on creation self.fishType = 'smallFish'; // 'smallFish', 'mediumFish', 'bigFish' self.sizeValue = 1; // Used for collision and logic self.speed = 2; // px per frame self.direction = 1; // 1: right, -1: left // Asset var fishAsset = null; // Value label var valueLabel = null; // Collider visual var colliderCircle = null; self.init = function (type, sizeValue, speed, direction) { self.fishType = type; self.sizeValue = sizeValue; self.speed = speed; self.direction = direction; // Remove previous asset if any if (fishAsset) { self.removeChild(fishAsset); } if (valueLabel) { self.removeChild(valueLabel); } if (colliderCircle) { self.removeChild(colliderCircle); } fishAsset = self.attachAsset(type, { anchorX: 0.5, anchorY: 0.5 }); self.scale.x = direction; // Flip if moving left self.scale.y = 1; // Set scale based on sizeValue self.scale.x *= sizeValue; self.scale.y *= sizeValue; // Add value label (rounded to 2 decimals) valueLabel = new Text2(self.sizeValue.toFixed(2), { size: 60, fill: 0xffffff, align: "center" }); valueLabel.anchor.set(0.5, 0.5); // Place label at center of fish valueLabel.x = 0; valueLabel.y = 0; self.addChild(valueLabel); // Add collider visual (ellipse for more accurate fit) var colliderW = fishAsset.width * Math.abs(self.scale.x) * 0.45; var colliderH = fishAsset.height * Math.abs(self.scale.y) * 0.45; colliderCircle = LK.getAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, scaleX: colliderW / 50, scaleY: colliderH / 50, alpha: 0.25, tint: 0xff0000 }); self.addChild(colliderCircle); }; self.getRadius = function () { return fishAsset.width * Math.abs(self.scale.x) * 0.5; }; // Move fish self.update = function () { self.x += self.speed * self.direction; // Keep collider visual in sync with scale (ellipse) if (colliderCircle && fishAsset) { var colliderW = fishAsset.width * Math.abs(self.scale.x) * 0.45; var colliderH = fishAsset.height * Math.abs(self.scale.y) * 0.45; colliderCircle.scale.x = colliderW / 50; colliderCircle.scale.y = colliderH / 50; } }; return self; }); // Player Fish Class var PlayerFish = Container.expand(function () { var self = Container.call(this); var fish = self.attachAsset('playerFish', { anchorX: 0.5, anchorY: 0.5 }); var colliderCircle = LK.getAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, scaleX: fish.width * 0.45 / 50, scaleY: fish.height * 0.45 / 50, alpha: 0.25, tint: 0x00ff00 }); self.addChild(colliderCircle); self.size = 1; // Growth multiplier // Update size/scale self.setSize = function (scale) { self.size = scale; self.scale.x = scale; self.scale.y = scale; // Update collider visual (ellipse) if (colliderCircle && fish) { var colliderW = fish.width * self.size * 0.45; var colliderH = fish.height * self.size * 0.45; colliderCircle.scale.x = colliderW / 50; colliderCircle.scale.y = colliderH / 50; } }; // For collision, always use the container, not the asset self.getRadius = function () { return fish.width * self.size * 0.5; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x1a237e // Deep blue ocean }); /**** * Game Code ****/ // Game constants // Fish assets: player, small fish, medium fish, big fish var GAME_WIDTH = 2048; var GAME_HEIGHT = 2732; var PLAYER_START_SIZE = 1; var PLAYER_GROWTH = 0.12; // Each fish eaten increases size by this factor var MAX_PLAYER_SIZE = 2.5; var ENEMY_SPAWN_INTERVAL = 60; // frames var ENEMY_MIN_SPEED = 4; var ENEMY_MAX_SPEED = 10; var ENEMY_SPAWN_MARGIN = 120; // px offscreen // State var player = null; var enemyFishes = []; var score = 0; var scoreTxt = null; var dragNode = null; var lastGameOver = false; var spawnTick = 0; // Helper: Get random between min and max function randBetween(min, max) { return min + Math.random() * (max - min); } // Helper: Clamp value function clamp(val, min, max) { return Math.max(min, Math.min(max, val)); } // Initialize player player = new PlayerFish(); player.setSize(PLAYER_START_SIZE); player.x = GAME_WIDTH / 2; player.y = GAME_HEIGHT * 0.7; game.addChild(player); // Score text scoreTxt = new Text2('0', { size: 120, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Spawning enemy fish function spawnEnemyFish() { // Determine player size for dynamic difficulty var pSize = player.size; // Decide fish type and size var type, sizeValue, speed, direction; var r = Math.random(); // Probability: 50% small, 35% medium, 15% big if (r < 0.5) { type = 'smallFish'; // Always smaller than player sizeValue = clamp(randBetween(0.5, pSize * 0.85), 0.4, 1.2); } else if (r < 0.85) { type = 'mediumFish'; // Medium fish are now noticeably bigger sizeValue = clamp(randBetween(pSize * 1.1, pSize * 1.7), 1.2, 2.2); } else { type = 'bigFish'; // Big fish are now much bigger sizeValue = clamp(randBetween(pSize * 1.7, pSize * 2.7), 2.2, 3.2); } // Speed: smaller fish are faster speed = clamp(ENEMY_MAX_SPEED - sizeValue * 3, ENEMY_MIN_SPEED, ENEMY_MAX_SPEED); // Direction: left or right direction = Math.random() < 0.5 ? 1 : -1; var fish = new EnemyFish(); fish.init(type, sizeValue, speed, direction); // Y position: random, but not too close to top/bottom var minY = 200 + fish.getRadius(); var maxY = GAME_HEIGHT - 200 - fish.getRadius(); fish.y = randBetween(minY, maxY); // X position: offscreen left or right if (direction === 1) { fish.x = -ENEMY_SPAWN_MARGIN; } else { fish.x = GAME_WIDTH + ENEMY_SPAWN_MARGIN; } // Track enemyFishes.push(fish); game.addChild(fish); } // Move handler for dragging player function handleMove(x, y, obj) { if (dragNode) { // Clamp to game area var r = player.getRadius(); dragNode.x = clamp(x, r, GAME_WIDTH - r); dragNode.y = clamp(y, r, GAME_HEIGHT - r); } } // Touch/drag events game.down = function (x, y, obj) { // Only allow drag if touch is on player var dx = x - player.x; var dy = y - player.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < player.getRadius() * 1.1) { dragNode = player; handleMove(x, y, obj); } }; game.up = function (x, y, obj) { dragNode = null; }; game.move = handleMove; // Game update loop game.update = function () { // Spawn enemy fish spawnTick++; if (spawnTick >= ENEMY_SPAWN_INTERVAL) { spawnTick = 0; spawnEnemyFish(); } // Check if player collides with any edge var pr = player.getRadius(); if (player.x - pr <= 0 || player.x + pr >= GAME_WIDTH || player.y - pr <= 0 || player.y + pr >= GAME_HEIGHT) { if (!lastGameOver) { lastGameOver = true; LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); return; } } // Update enemy fish for (var i = enemyFishes.length - 1; i >= 0; i--) { var fish = enemyFishes[i]; fish.update(); // Remove if offscreen if (fish.direction === 1 && fish.x - fish.getRadius() > GAME_WIDTH + ENEMY_SPAWN_MARGIN || fish.direction === -1 && fish.x + fish.getRadius() < -ENEMY_SPAWN_MARGIN) { fish.destroy(); enemyFishes.splice(i, 1); continue; } // Collision with player var dx = fish.x - player.x; var dy = fish.y - player.y; var dist = Math.sqrt(dx * dx + dy * dy); var minDist = fish.getRadius() + player.getRadius() * 0.85; if (dist < minDist) { // Compare sizes if (player.size > fish.sizeValue * 1.05) { // Eat fish // Animate fish fade out tween(fish, { alpha: 0 }, { duration: 200, onFinish: function onFinish() { fish.destroy(); } }); enemyFishes.splice(i, 1); // Grow player var newSize = clamp(player.size + PLAYER_GROWTH * fish.sizeValue, PLAYER_START_SIZE, MAX_PLAYER_SIZE); player.setSize(newSize); // Update score score += Math.floor(10 * fish.sizeValue); scoreTxt.setText(score); // Win condition removed: game continues until player is eaten } else if (player.size < fish.sizeValue * 0.95) { // Player is eaten if (!lastGameOver) { lastGameOver = true; LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); return; } } // If similar size, nothing happens } } }; // Reset state on game restart LK.on('gameStart', function () { // Remove all enemy fish for (var i = 0; i < enemyFishes.length; i++) { enemyFishes[i].destroy(); } enemyFishes = []; // Reset player player.setSize(PLAYER_START_SIZE); player.x = GAME_WIDTH / 2; player.y = GAME_HEIGHT * 0.7; // Reset score score = 0; scoreTxt.setText(score); lastGameOver = false; spawnTick = 0; });
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Enemy Fish Class
var EnemyFish = Container.expand(function () {
var self = Container.call(this);
// Will be set on creation
self.fishType = 'smallFish'; // 'smallFish', 'mediumFish', 'bigFish'
self.sizeValue = 1; // Used for collision and logic
self.speed = 2; // px per frame
self.direction = 1; // 1: right, -1: left
// Asset
var fishAsset = null;
// Value label
var valueLabel = null;
// Collider visual
var colliderCircle = null;
self.init = function (type, sizeValue, speed, direction) {
self.fishType = type;
self.sizeValue = sizeValue;
self.speed = speed;
self.direction = direction;
// Remove previous asset if any
if (fishAsset) {
self.removeChild(fishAsset);
}
if (valueLabel) {
self.removeChild(valueLabel);
}
if (colliderCircle) {
self.removeChild(colliderCircle);
}
fishAsset = self.attachAsset(type, {
anchorX: 0.5,
anchorY: 0.5
});
self.scale.x = direction; // Flip if moving left
self.scale.y = 1;
// Set scale based on sizeValue
self.scale.x *= sizeValue;
self.scale.y *= sizeValue;
// Add value label (rounded to 2 decimals)
valueLabel = new Text2(self.sizeValue.toFixed(2), {
size: 60,
fill: 0xffffff,
align: "center"
});
valueLabel.anchor.set(0.5, 0.5);
// Place label at center of fish
valueLabel.x = 0;
valueLabel.y = 0;
self.addChild(valueLabel);
// Add collider visual (ellipse for more accurate fit)
var colliderW = fishAsset.width * Math.abs(self.scale.x) * 0.45;
var colliderH = fishAsset.height * Math.abs(self.scale.y) * 0.45;
colliderCircle = LK.getAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: colliderW / 50,
scaleY: colliderH / 50,
alpha: 0.25,
tint: 0xff0000
});
self.addChild(colliderCircle);
};
self.getRadius = function () {
return fishAsset.width * Math.abs(self.scale.x) * 0.5;
};
// Move fish
self.update = function () {
self.x += self.speed * self.direction;
// Keep collider visual in sync with scale (ellipse)
if (colliderCircle && fishAsset) {
var colliderW = fishAsset.width * Math.abs(self.scale.x) * 0.45;
var colliderH = fishAsset.height * Math.abs(self.scale.y) * 0.45;
colliderCircle.scale.x = colliderW / 50;
colliderCircle.scale.y = colliderH / 50;
}
};
return self;
});
// Player Fish Class
var PlayerFish = Container.expand(function () {
var self = Container.call(this);
var fish = self.attachAsset('playerFish', {
anchorX: 0.5,
anchorY: 0.5
});
var colliderCircle = LK.getAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: fish.width * 0.45 / 50,
scaleY: fish.height * 0.45 / 50,
alpha: 0.25,
tint: 0x00ff00
});
self.addChild(colliderCircle);
self.size = 1; // Growth multiplier
// Update size/scale
self.setSize = function (scale) {
self.size = scale;
self.scale.x = scale;
self.scale.y = scale;
// Update collider visual (ellipse)
if (colliderCircle && fish) {
var colliderW = fish.width * self.size * 0.45;
var colliderH = fish.height * self.size * 0.45;
colliderCircle.scale.x = colliderW / 50;
colliderCircle.scale.y = colliderH / 50;
}
};
// For collision, always use the container, not the asset
self.getRadius = function () {
return fish.width * self.size * 0.5;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a237e // Deep blue ocean
});
/****
* Game Code
****/
// Game constants
// Fish assets: player, small fish, medium fish, big fish
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var PLAYER_START_SIZE = 1;
var PLAYER_GROWTH = 0.12; // Each fish eaten increases size by this factor
var MAX_PLAYER_SIZE = 2.5;
var ENEMY_SPAWN_INTERVAL = 60; // frames
var ENEMY_MIN_SPEED = 4;
var ENEMY_MAX_SPEED = 10;
var ENEMY_SPAWN_MARGIN = 120; // px offscreen
// State
var player = null;
var enemyFishes = [];
var score = 0;
var scoreTxt = null;
var dragNode = null;
var lastGameOver = false;
var spawnTick = 0;
// Helper: Get random between min and max
function randBetween(min, max) {
return min + Math.random() * (max - min);
}
// Helper: Clamp value
function clamp(val, min, max) {
return Math.max(min, Math.min(max, val));
}
// Initialize player
player = new PlayerFish();
player.setSize(PLAYER_START_SIZE);
player.x = GAME_WIDTH / 2;
player.y = GAME_HEIGHT * 0.7;
game.addChild(player);
// Score text
scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Spawning enemy fish
function spawnEnemyFish() {
// Determine player size for dynamic difficulty
var pSize = player.size;
// Decide fish type and size
var type, sizeValue, speed, direction;
var r = Math.random();
// Probability: 50% small, 35% medium, 15% big
if (r < 0.5) {
type = 'smallFish';
// Always smaller than player
sizeValue = clamp(randBetween(0.5, pSize * 0.85), 0.4, 1.2);
} else if (r < 0.85) {
type = 'mediumFish';
// Medium fish are now noticeably bigger
sizeValue = clamp(randBetween(pSize * 1.1, pSize * 1.7), 1.2, 2.2);
} else {
type = 'bigFish';
// Big fish are now much bigger
sizeValue = clamp(randBetween(pSize * 1.7, pSize * 2.7), 2.2, 3.2);
}
// Speed: smaller fish are faster
speed = clamp(ENEMY_MAX_SPEED - sizeValue * 3, ENEMY_MIN_SPEED, ENEMY_MAX_SPEED);
// Direction: left or right
direction = Math.random() < 0.5 ? 1 : -1;
var fish = new EnemyFish();
fish.init(type, sizeValue, speed, direction);
// Y position: random, but not too close to top/bottom
var minY = 200 + fish.getRadius();
var maxY = GAME_HEIGHT - 200 - fish.getRadius();
fish.y = randBetween(minY, maxY);
// X position: offscreen left or right
if (direction === 1) {
fish.x = -ENEMY_SPAWN_MARGIN;
} else {
fish.x = GAME_WIDTH + ENEMY_SPAWN_MARGIN;
}
// Track
enemyFishes.push(fish);
game.addChild(fish);
}
// Move handler for dragging player
function handleMove(x, y, obj) {
if (dragNode) {
// Clamp to game area
var r = player.getRadius();
dragNode.x = clamp(x, r, GAME_WIDTH - r);
dragNode.y = clamp(y, r, GAME_HEIGHT - r);
}
}
// Touch/drag events
game.down = function (x, y, obj) {
// Only allow drag if touch is on player
var dx = x - player.x;
var dy = y - player.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < player.getRadius() * 1.1) {
dragNode = player;
handleMove(x, y, obj);
}
};
game.up = function (x, y, obj) {
dragNode = null;
};
game.move = handleMove;
// Game update loop
game.update = function () {
// Spawn enemy fish
spawnTick++;
if (spawnTick >= ENEMY_SPAWN_INTERVAL) {
spawnTick = 0;
spawnEnemyFish();
}
// Check if player collides with any edge
var pr = player.getRadius();
if (player.x - pr <= 0 || player.x + pr >= GAME_WIDTH || player.y - pr <= 0 || player.y + pr >= GAME_HEIGHT) {
if (!lastGameOver) {
lastGameOver = true;
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
}
// Update enemy fish
for (var i = enemyFishes.length - 1; i >= 0; i--) {
var fish = enemyFishes[i];
fish.update();
// Remove if offscreen
if (fish.direction === 1 && fish.x - fish.getRadius() > GAME_WIDTH + ENEMY_SPAWN_MARGIN || fish.direction === -1 && fish.x + fish.getRadius() < -ENEMY_SPAWN_MARGIN) {
fish.destroy();
enemyFishes.splice(i, 1);
continue;
}
// Collision with player
var dx = fish.x - player.x;
var dy = fish.y - player.y;
var dist = Math.sqrt(dx * dx + dy * dy);
var minDist = fish.getRadius() + player.getRadius() * 0.85;
if (dist < minDist) {
// Compare sizes
if (player.size > fish.sizeValue * 1.05) {
// Eat fish
// Animate fish fade out
tween(fish, {
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
fish.destroy();
}
});
enemyFishes.splice(i, 1);
// Grow player
var newSize = clamp(player.size + PLAYER_GROWTH * fish.sizeValue, PLAYER_START_SIZE, MAX_PLAYER_SIZE);
player.setSize(newSize);
// Update score
score += Math.floor(10 * fish.sizeValue);
scoreTxt.setText(score);
// Win condition removed: game continues until player is eaten
} else if (player.size < fish.sizeValue * 0.95) {
// Player is eaten
if (!lastGameOver) {
lastGameOver = true;
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
}
// If similar size, nothing happens
}
}
};
// Reset state on game restart
LK.on('gameStart', function () {
// Remove all enemy fish
for (var i = 0; i < enemyFishes.length; i++) {
enemyFishes[i].destroy();
}
enemyFishes = [];
// Reset player
player.setSize(PLAYER_START_SIZE);
player.x = GAME_WIDTH / 2;
player.y = GAME_HEIGHT * 0.7;
// Reset score
score = 0;
scoreTxt.setText(score);
lastGameOver = false;
spawnTick = 0;
});
Fish. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
A stingray fish . No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
A big Black and White Orca. In-Game asset. 2d. High contrast. No shadows
A blueish Anchovy. In-Game asset. 2d. High contrast. No shadows