/****
* 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