/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Animal class
var Animal = Container.expand(function () {
var self = Container.call(this);
self.type = null;
self.asset = null;
self.setType = function (type) {
self.type = type;
if (self.asset) {
self.removeChild(self.asset);
}
self.asset = self.attachAsset(type, {
anchorX: 0.5,
anchorY: 0.5
});
};
return self;
});
// Snake Segment (body or head)
var SnakeSegment = Container.expand(function () {
var self = Container.call(this);
self.asset = null;
self.setType = function (type) {
if (self.asset) {
self.removeChild(self.asset);
}
var assetId = type === 'head' ? 'snakeHead' : 'snakeBody';
self.asset = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
};
self.setType('body');
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// Animal assets: pig, cat, bee, frog (box/ellipse with different colors)
// Game constants
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var SNAKE_START_LENGTH = 3;
var SNAKE_START_SPEED = 8; // pixels per tick
var SNAKE_SPEED_INCREMENT = 1.5; // speed up per sequence
var SNAKE_MAX_SPEED = 32;
var SEGMENT_SPACING = 90; // px between segments
var ANIMAL_TYPES = ['pig', 'cat', 'bee', 'frog'];
var ANIMAL_NAMES = {
pig: "Pig",
cat: "Cat",
bee: "Bee",
frog: "Frog"
};
// Game state
var snake = [];
var snakeDir = {
x: 0,
y: -1
}; // start moving up
var snakeNextDir = {
x: 0,
y: -1
};
var snakeSpeed = SNAKE_START_SPEED;
var snakePositions = []; // for body following
var animals = [];
var currentTargetIndex = 0; // which animal in sequence to eat next
var score = 0;
var sequenceCount = 0;
var isGameOver = false;
var moveCooldown = 0; // for swipe input
var dragStart = null;
// GUI
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var orderTxt = new Text2('', {
size: 70,
fill: "#fff"
});
orderTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(orderTxt);
orderTxt.y = 130;
// Helper: get random position for animal, not too close to snake head
function getRandomAnimalPos() {
var margin = 180;
var tries = 0;
while (tries < 20) {
var x = margin + Math.random() * (GAME_WIDTH - 2 * margin);
var y = margin + Math.random() * (GAME_HEIGHT - 2 * margin);
// Avoid spawning too close to snake head
var dx = x - snake[0].x;
var dy = y - snake[0].y;
if (dx * dx + dy * dy > 400 * 400) {
return {
x: x,
y: y
};
}
tries++;
}
// fallback
return {
x: margin + Math.random() * (GAME_WIDTH - 2 * margin),
y: margin + Math.random() * (GAME_HEIGHT - 2 * margin)
};
}
// Helper: update order text
function updateOrderText() {
var s = '';
for (var i = 0; i < ANIMAL_TYPES.length; i++) {
if (i === currentTargetIndex) {
s += '[b]' + ANIMAL_NAMES[ANIMAL_TYPES[i]] + '[/b] ';
} else {
s += ANIMAL_NAMES[ANIMAL_TYPES[i]] + ' ';
}
}
orderTxt.setText(s.trim());
}
// Helper: spawn all animals
function spawnAnimals() {
// Remove old
for (var i = 0; i < animals.length; i++) {
animals[i].destroy();
}
animals = [];
for (var i = 0; i < ANIMAL_TYPES.length; i++) {
var animal = new Animal();
animal.setType(ANIMAL_TYPES[i]);
var pos = getRandomAnimalPos();
animal.x = pos.x;
animal.y = pos.y;
animals.push(animal);
game.addChild(animal);
}
}
// Helper: reset snake
function resetSnake() {
for (var i = 0; i < snake.length; i++) {
snake[i].destroy();
}
snake = [];
snakePositions = [];
var startX = GAME_WIDTH / 2;
var startY = GAME_HEIGHT / 2 + 300;
for (var i = 0; i < SNAKE_START_LENGTH; i++) {
var seg = new SnakeSegment();
seg.setType(i === 0 ? 'head' : 'body');
seg.x = startX;
seg.y = startY + i * SEGMENT_SPACING;
snake.push(seg);
game.addChild(seg);
snakePositions.push({
x: seg.x,
y: seg.y
});
}
snakeDir = {
x: 0,
y: -1
};
snakeNextDir = {
x: 0,
y: -1
};
}
// Helper: grow snake
function growSnake() {
var last = snake[snake.length - 1];
var seg = new SnakeSegment();
seg.setType('body');
seg.x = last.x;
seg.y = last.y;
snake.push(seg);
game.addChild(seg);
snakePositions.push({
x: seg.x,
y: seg.y
});
}
// Helper: check self collision
function checkSelfCollision() {
var head = snake[0];
for (var i = 2; i < snake.length; i++) {
var seg = snake[i];
var dx = head.x - seg.x;
var dy = head.y - seg.y;
if (dx * dx + dy * dy < 60 * 60) {
return true;
}
}
return false;
}
// Helper: check wall collision
function checkWallCollision() {
var head = snake[0];
var margin = 40;
if (head.x < margin || head.x > GAME_WIDTH - margin || head.y < margin || head.y > GAME_HEIGHT - margin) {
return true;
}
return false;
}
// Helper: check animal collision
function checkAnimalCollision() {
var head = snake[0];
for (var i = 0; i < animals.length; i++) {
var animal = animals[i];
var dx = head.x - animal.x;
var dy = head.y - animal.y;
if (dx * dx + dy * dy < 80 * 80) {
return i;
}
}
return -1;
}
// Helper: handle eating animal
function eatAnimal(idx) {
var animal = animals[idx];
if (animal.type === ANIMAL_TYPES[currentTargetIndex]) {
// Correct animal
growSnake();
score++;
scoreTxt.setText(score);
// Remove and respawn this animal
animal.destroy();
animals.splice(idx, 1);
// Next in sequence
currentTargetIndex++;
if (currentTargetIndex >= ANIMAL_TYPES.length) {
// Sequence complete
sequenceCount++;
currentTargetIndex = 0;
// Speed up
snakeSpeed = Math.min(snakeSpeed + SNAKE_SPEED_INCREMENT, SNAKE_MAX_SPEED);
// Respawn all animals
spawnAnimals();
// Flash green
LK.effects.flashScreen(0x00ff00, 400);
} else {
// Only respawn this animal
var newAnimal = new Animal();
newAnimal.setType(animal.type);
var pos = getRandomAnimalPos();
newAnimal.x = pos.x;
newAnimal.y = pos.y;
animals.push(newAnimal);
game.addChild(newAnimal);
}
updateOrderText();
} else {
// Wrong animal: game over
LK.effects.flashScreen(0xff0000, 800);
isGameOver = true;
LK.showGameOver();
}
}
// Helper: handle game over
function handleGameOver() {
isGameOver = true;
// LK.showGameOver() already called
}
// Input: swipe to change direction
function getSwipeDir(dx, dy) {
if (Math.abs(dx) > Math.abs(dy)) {
return {
x: dx > 0 ? 1 : -1,
y: 0
};
} else {
return {
x: 0,
y: dy > 0 ? 1 : -1
};
}
}
// Input: touch/drag
game.down = function (x, y, obj) {
dragStart = {
x: x,
y: y
};
};
game.up = function (x, y, obj) {
dragStart = null;
};
game.move = function (x, y, obj) {
if (dragStart && !isGameOver) {
var dx = x - dragStart.x;
var dy = y - dragStart.y;
if (dx * dx + dy * dy > 80 * 80) {
var dir = getSwipeDir(dx, dy);
// Prevent reversing
if (!(dir.x === -snakeDir.x && dir.y === -snakeDir.y)) {
snakeNextDir = dir;
}
dragStart = null;
}
}
};
// Main update loop
game.update = function () {
if (isGameOver) return;
// Move snake
// Update direction
if (snakeNextDir.x !== snakeDir.x || snakeNextDir.y !== snakeDir.y) {
snakeDir = {
x: snakeNextDir.x,
y: snakeNextDir.y
};
}
// Calculate new head position
var head = snake[0];
var newX = head.x + snakeDir.x * snakeSpeed;
var newY = head.y + snakeDir.y * snakeSpeed;
// Store previous positions
snakePositions.unshift({
x: newX,
y: newY
});
if (snakePositions.length > snake.length * SEGMENT_SPACING / snakeSpeed + 2) {
snakePositions.pop();
}
// Move segments
for (var i = 0; i < snake.length; i++) {
var posIdx = Math.min(i * Math.floor(SEGMENT_SPACING / snakeSpeed), snakePositions.length - 1);
var pos = snakePositions[posIdx];
snake[i].x = pos.x;
snake[i].y = pos.y;
}
// Check wall collision
if (checkWallCollision()) {
LK.effects.flashScreen(0xff0000, 800);
handleGameOver();
LK.showGameOver();
return;
}
// Check self collision
if (checkSelfCollision()) {
LK.effects.flashScreen(0xff0000, 800);
handleGameOver();
LK.showGameOver();
return;
}
// Check animal collision
var animalIdx = checkAnimalCollision();
if (animalIdx !== -1) {
eatAnimal(animalIdx);
}
};
// Game start/reset
function startGame() {
isGameOver = false;
score = 0;
sequenceCount = 0;
snakeSpeed = SNAKE_START_SPEED;
currentTargetIndex = 0;
scoreTxt.setText(score);
updateOrderText();
resetSnake();
spawnAnimals();
}
startGame(); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Animal class
var Animal = Container.expand(function () {
var self = Container.call(this);
self.type = null;
self.asset = null;
self.setType = function (type) {
self.type = type;
if (self.asset) {
self.removeChild(self.asset);
}
self.asset = self.attachAsset(type, {
anchorX: 0.5,
anchorY: 0.5
});
};
return self;
});
// Snake Segment (body or head)
var SnakeSegment = Container.expand(function () {
var self = Container.call(this);
self.asset = null;
self.setType = function (type) {
if (self.asset) {
self.removeChild(self.asset);
}
var assetId = type === 'head' ? 'snakeHead' : 'snakeBody';
self.asset = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
};
self.setType('body');
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// Animal assets: pig, cat, bee, frog (box/ellipse with different colors)
// Game constants
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var SNAKE_START_LENGTH = 3;
var SNAKE_START_SPEED = 8; // pixels per tick
var SNAKE_SPEED_INCREMENT = 1.5; // speed up per sequence
var SNAKE_MAX_SPEED = 32;
var SEGMENT_SPACING = 90; // px between segments
var ANIMAL_TYPES = ['pig', 'cat', 'bee', 'frog'];
var ANIMAL_NAMES = {
pig: "Pig",
cat: "Cat",
bee: "Bee",
frog: "Frog"
};
// Game state
var snake = [];
var snakeDir = {
x: 0,
y: -1
}; // start moving up
var snakeNextDir = {
x: 0,
y: -1
};
var snakeSpeed = SNAKE_START_SPEED;
var snakePositions = []; // for body following
var animals = [];
var currentTargetIndex = 0; // which animal in sequence to eat next
var score = 0;
var sequenceCount = 0;
var isGameOver = false;
var moveCooldown = 0; // for swipe input
var dragStart = null;
// GUI
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var orderTxt = new Text2('', {
size: 70,
fill: "#fff"
});
orderTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(orderTxt);
orderTxt.y = 130;
// Helper: get random position for animal, not too close to snake head
function getRandomAnimalPos() {
var margin = 180;
var tries = 0;
while (tries < 20) {
var x = margin + Math.random() * (GAME_WIDTH - 2 * margin);
var y = margin + Math.random() * (GAME_HEIGHT - 2 * margin);
// Avoid spawning too close to snake head
var dx = x - snake[0].x;
var dy = y - snake[0].y;
if (dx * dx + dy * dy > 400 * 400) {
return {
x: x,
y: y
};
}
tries++;
}
// fallback
return {
x: margin + Math.random() * (GAME_WIDTH - 2 * margin),
y: margin + Math.random() * (GAME_HEIGHT - 2 * margin)
};
}
// Helper: update order text
function updateOrderText() {
var s = '';
for (var i = 0; i < ANIMAL_TYPES.length; i++) {
if (i === currentTargetIndex) {
s += '[b]' + ANIMAL_NAMES[ANIMAL_TYPES[i]] + '[/b] ';
} else {
s += ANIMAL_NAMES[ANIMAL_TYPES[i]] + ' ';
}
}
orderTxt.setText(s.trim());
}
// Helper: spawn all animals
function spawnAnimals() {
// Remove old
for (var i = 0; i < animals.length; i++) {
animals[i].destroy();
}
animals = [];
for (var i = 0; i < ANIMAL_TYPES.length; i++) {
var animal = new Animal();
animal.setType(ANIMAL_TYPES[i]);
var pos = getRandomAnimalPos();
animal.x = pos.x;
animal.y = pos.y;
animals.push(animal);
game.addChild(animal);
}
}
// Helper: reset snake
function resetSnake() {
for (var i = 0; i < snake.length; i++) {
snake[i].destroy();
}
snake = [];
snakePositions = [];
var startX = GAME_WIDTH / 2;
var startY = GAME_HEIGHT / 2 + 300;
for (var i = 0; i < SNAKE_START_LENGTH; i++) {
var seg = new SnakeSegment();
seg.setType(i === 0 ? 'head' : 'body');
seg.x = startX;
seg.y = startY + i * SEGMENT_SPACING;
snake.push(seg);
game.addChild(seg);
snakePositions.push({
x: seg.x,
y: seg.y
});
}
snakeDir = {
x: 0,
y: -1
};
snakeNextDir = {
x: 0,
y: -1
};
}
// Helper: grow snake
function growSnake() {
var last = snake[snake.length - 1];
var seg = new SnakeSegment();
seg.setType('body');
seg.x = last.x;
seg.y = last.y;
snake.push(seg);
game.addChild(seg);
snakePositions.push({
x: seg.x,
y: seg.y
});
}
// Helper: check self collision
function checkSelfCollision() {
var head = snake[0];
for (var i = 2; i < snake.length; i++) {
var seg = snake[i];
var dx = head.x - seg.x;
var dy = head.y - seg.y;
if (dx * dx + dy * dy < 60 * 60) {
return true;
}
}
return false;
}
// Helper: check wall collision
function checkWallCollision() {
var head = snake[0];
var margin = 40;
if (head.x < margin || head.x > GAME_WIDTH - margin || head.y < margin || head.y > GAME_HEIGHT - margin) {
return true;
}
return false;
}
// Helper: check animal collision
function checkAnimalCollision() {
var head = snake[0];
for (var i = 0; i < animals.length; i++) {
var animal = animals[i];
var dx = head.x - animal.x;
var dy = head.y - animal.y;
if (dx * dx + dy * dy < 80 * 80) {
return i;
}
}
return -1;
}
// Helper: handle eating animal
function eatAnimal(idx) {
var animal = animals[idx];
if (animal.type === ANIMAL_TYPES[currentTargetIndex]) {
// Correct animal
growSnake();
score++;
scoreTxt.setText(score);
// Remove and respawn this animal
animal.destroy();
animals.splice(idx, 1);
// Next in sequence
currentTargetIndex++;
if (currentTargetIndex >= ANIMAL_TYPES.length) {
// Sequence complete
sequenceCount++;
currentTargetIndex = 0;
// Speed up
snakeSpeed = Math.min(snakeSpeed + SNAKE_SPEED_INCREMENT, SNAKE_MAX_SPEED);
// Respawn all animals
spawnAnimals();
// Flash green
LK.effects.flashScreen(0x00ff00, 400);
} else {
// Only respawn this animal
var newAnimal = new Animal();
newAnimal.setType(animal.type);
var pos = getRandomAnimalPos();
newAnimal.x = pos.x;
newAnimal.y = pos.y;
animals.push(newAnimal);
game.addChild(newAnimal);
}
updateOrderText();
} else {
// Wrong animal: game over
LK.effects.flashScreen(0xff0000, 800);
isGameOver = true;
LK.showGameOver();
}
}
// Helper: handle game over
function handleGameOver() {
isGameOver = true;
// LK.showGameOver() already called
}
// Input: swipe to change direction
function getSwipeDir(dx, dy) {
if (Math.abs(dx) > Math.abs(dy)) {
return {
x: dx > 0 ? 1 : -1,
y: 0
};
} else {
return {
x: 0,
y: dy > 0 ? 1 : -1
};
}
}
// Input: touch/drag
game.down = function (x, y, obj) {
dragStart = {
x: x,
y: y
};
};
game.up = function (x, y, obj) {
dragStart = null;
};
game.move = function (x, y, obj) {
if (dragStart && !isGameOver) {
var dx = x - dragStart.x;
var dy = y - dragStart.y;
if (dx * dx + dy * dy > 80 * 80) {
var dir = getSwipeDir(dx, dy);
// Prevent reversing
if (!(dir.x === -snakeDir.x && dir.y === -snakeDir.y)) {
snakeNextDir = dir;
}
dragStart = null;
}
}
};
// Main update loop
game.update = function () {
if (isGameOver) return;
// Move snake
// Update direction
if (snakeNextDir.x !== snakeDir.x || snakeNextDir.y !== snakeDir.y) {
snakeDir = {
x: snakeNextDir.x,
y: snakeNextDir.y
};
}
// Calculate new head position
var head = snake[0];
var newX = head.x + snakeDir.x * snakeSpeed;
var newY = head.y + snakeDir.y * snakeSpeed;
// Store previous positions
snakePositions.unshift({
x: newX,
y: newY
});
if (snakePositions.length > snake.length * SEGMENT_SPACING / snakeSpeed + 2) {
snakePositions.pop();
}
// Move segments
for (var i = 0; i < snake.length; i++) {
var posIdx = Math.min(i * Math.floor(SEGMENT_SPACING / snakeSpeed), snakePositions.length - 1);
var pos = snakePositions[posIdx];
snake[i].x = pos.x;
snake[i].y = pos.y;
}
// Check wall collision
if (checkWallCollision()) {
LK.effects.flashScreen(0xff0000, 800);
handleGameOver();
LK.showGameOver();
return;
}
// Check self collision
if (checkSelfCollision()) {
LK.effects.flashScreen(0xff0000, 800);
handleGameOver();
LK.showGameOver();
return;
}
// Check animal collision
var animalIdx = checkAnimalCollision();
if (animalIdx !== -1) {
eatAnimal(animalIdx);
}
};
// Game start/reset
function startGame() {
isGameOver = false;
score = 0;
sequenceCount = 0;
snakeSpeed = SNAKE_START_SPEED;
currentTargetIndex = 0;
scoreTxt.setText(score);
updateOrderText();
resetSnake();
spawnAnimals();
}
startGame();