/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
currentLevel: 0,
highScore: 0
});
/****
* Classes
****/
var Food = Container.expand(function () {
var self = Container.call(this);
self.foodGraphic = self.attachAsset('food', {
anchorX: 0.5,
anchorY: 0.5
});
self.value = 1; // Default food value
// Pulsing animation
self.pulseFood = function () {
tween(self.foodGraphic.scale, {
x: 1.2,
y: 1.2
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self.foodGraphic.scale, {
x: 1.0,
y: 1.0
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: self.pulseFood
});
}
});
};
self.pulseFood();
return self;
});
var GameLevel = Container.expand(function (levelNum) {
var self = Container.call(this);
self.levelNumber = levelNum || 0;
self.walls = [];
self.obstacles = [];
self.foods = [];
self.backgroundColor = 0x88cc66; // Default jungle background
self.setupLevel = function () {
// Clear any existing level elements
self.walls.forEach(function (wall) {
wall.destroy();
});
self.obstacles.forEach(function (obstacle) {
obstacle.destroy();
});
self.foods.forEach(function (food) {
food.destroy();
});
self.walls = [];
self.obstacles = [];
self.foods = [];
// Set up specific level based on level number
switch (self.levelNumber) {
case 0:
// Jungle - Easy
self.backgroundColor = 0x88cc66;
self.createBorderWalls();
LK.playMusic('jungleMusic');
break;
case 1:
// Arctic - Medium
self.backgroundColor = 0xaaddff;
self.createBorderWalls();
self.createCrossObstacles();
LK.playMusic('arcticMusic');
break;
case 2:
// Desert - Hard
self.backgroundColor = 0xddbb77;
self.createBorderWalls();
self.createRandomObstacles(10);
LK.playMusic('desertMusic');
break;
case 3:
// Cyber - Expert
self.backgroundColor = 0x223355;
self.createBorderWalls();
self.createMazeObstacles();
LK.playMusic('cyberMusic');
break;
}
game.setBackgroundColor(self.backgroundColor);
self.spawnFood();
};
self.createBorderWalls = function () {
// Create border walls
var wallThickness = 30;
var wallSpacing = 60; // Space between wall segments
// Top and bottom walls
for (var x = wallThickness; x < 2048; x += wallSpacing) {
// Top wall
var topWall = new Wall();
topWall.x = x;
topWall.y = wallThickness;
self.walls.push(topWall);
self.addChild(topWall);
// Bottom wall
var bottomWall = new Wall();
bottomWall.x = x;
bottomWall.y = 2732 - wallThickness;
self.walls.push(bottomWall);
self.addChild(bottomWall);
}
// Left and right walls
for (var y = wallThickness; y < 2732; y += wallSpacing) {
// Left wall
var leftWall = new Wall();
leftWall.x = wallThickness;
leftWall.y = y;
self.walls.push(leftWall);
self.addChild(leftWall);
// Right wall
var rightWall = new Wall();
rightWall.x = 2048 - wallThickness;
rightWall.y = y;
self.walls.push(rightWall);
self.addChild(rightWall);
}
};
self.createCrossObstacles = function () {
// Create cross-shaped obstacles for Arctic level
var centerX = 2048 / 2;
var centerY = 2732 / 2;
var spacing = 120;
// Horizontal line
for (var x = centerX - 400; x <= centerX + 400; x += spacing) {
var hObstacle = new Obstacle();
hObstacle.x = x;
hObstacle.y = centerY;
self.obstacles.push(hObstacle);
self.addChild(hObstacle);
}
// Vertical line
for (var y = centerY - 600; y <= centerY + 600; y += spacing) {
if (Math.abs(y - centerY) > spacing / 2) {
// Skip center to avoid overlap
var vObstacle = new Obstacle();
vObstacle.x = centerX;
vObstacle.y = y;
self.obstacles.push(vObstacle);
self.addChild(vObstacle);
}
}
};
self.createRandomObstacles = function (count) {
// Create random obstacles for Desert level
var margin = 150; // Keep obstacles away from edges
for (var i = 0; i < count; i++) {
var obstacle = new Obstacle();
// Keep trying positions until we find one that doesn't overlap
var validPosition = false;
var maxTries = 20;
var tries = 0;
while (!validPosition && tries < maxTries) {
obstacle.x = margin + Math.random() * (2048 - 2 * margin);
obstacle.y = margin + Math.random() * (2732 - 2 * margin);
// Check distance from other obstacles
validPosition = true;
for (var j = 0; j < self.obstacles.length; j++) {
var existingObstacle = self.obstacles[j];
var dx = obstacle.x - existingObstacle.x;
var dy = obstacle.y - existingObstacle.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 200) {
validPosition = false;
break;
}
}
tries++;
}
if (validPosition) {
self.obstacles.push(obstacle);
self.addChild(obstacle);
}
}
};
self.createMazeObstacles = function () {
// Create maze-like obstacles for Cyber level
var gridSize = 150;
var obstacleChance = 0.35; // Probability of placing an obstacle
for (var x = gridSize * 2; x < 2048 - gridSize * 2; x += gridSize) {
for (var y = gridSize * 2; y < 2732 - gridSize * 2; y += gridSize) {
// Skip center area to ensure there's space to move
var distFromCenter = Math.sqrt(Math.pow(x - 2048 / 2, 2) + Math.pow(y - 2732 / 2, 2));
if (distFromCenter < 400) {
continue;
}
if (Math.random() < obstacleChance) {
var obstacle = new Obstacle();
obstacle.x = x;
obstacle.y = y;
self.obstacles.push(obstacle);
self.addChild(obstacle);
}
}
}
};
self.spawnFood = function () {
var food = new Food();
var validPosition = false;
var gridSize = 60;
var margin = 120; // Keep food away from edges
// Keep trying until we find a valid position
while (!validPosition) {
// Generate random position, aligned to grid
var gridX = Math.floor((margin + Math.random() * (2048 - 2 * margin)) / gridSize);
var gridY = Math.floor((margin + Math.random() * (2732 - 2 * margin)) / gridSize);
food.x = gridX * gridSize;
food.y = gridY * gridSize;
// Check for collisions with walls and obstacles
validPosition = true;
// Check walls
for (var i = 0; i < self.walls.length; i++) {
if (self.isCollision(food, self.walls[i], 80)) {
validPosition = false;
break;
}
}
// Check obstacles
if (validPosition) {
for (var j = 0; j < self.obstacles.length; j++) {
if (self.isCollision(food, self.obstacles[j], 80)) {
validPosition = false;
break;
}
}
}
}
// Set food value based on level
if (self.levelNumber > 0) {
food.value = self.levelNumber + 1;
// Change food color based on value
switch (food.value) {
case 2:
food.foodGraphic.tint = 0x33ccff; // Blue for level 1
break;
case 3:
food.foodGraphic.tint = 0xffcc33; // Orange for level 2
break;
case 4:
food.foodGraphic.tint = 0xff33cc; // Pink for level 3
break;
}
}
self.foods.push(food);
self.addChild(food);
};
self.isCollision = function (obj1, obj2, minDistance) {
var dx = obj1.x - obj2.x;
var dy = obj1.y - obj2.y;
var distance = Math.sqrt(dx * dx + dy * dy);
return distance < minDistance;
};
return self;
});
var LevelSelectionScreen = Container.expand(function () {
var self = Container.call(this);
// Background image for level selection
var background = self.attachAsset('background', {
anchorX: 0.5,
anchorY: 0.5
});
background.x = 2048 / 2;
background.y = 2732 / 2;
// Level buttons
var levels = [{
name: "Jungle",
color: 0x88cc66
}, {
name: "Arctic",
color: 0xaaddff
}, {
name: "Desert",
color: 0xddbb77
}, {
name: "Cyber",
color: 0x223355
}];
levels.forEach(function (level, index) {
var levelBtn = new Text2('Level ' + (index + 1) + ': ' + level.name, {
size: 100,
fill: index === 1 ? 0xff0000 : level.color,
// Bright red for Arctic
align: 'center',
fontWeight: 'bold'
});
levelBtn.anchor.set(0.5, 0.5);
levelBtn.x = 2048 / 2;
levelBtn.y = 800 + index * 200;
self.addChild(levelBtn);
levelBtn.down = function () {
currentLevel = index;
storage.currentLevel = currentLevel;
self.visible = false;
initGame();
};
});
return self;
});
var Obstacle = Container.expand(function () {
var self = Container.call(this);
self.obstacleGraphic = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var SnakeSegment = Container.expand(function (isHead) {
var self = Container.call(this);
self.assetId = isHead ? 'snakeHead' : 'snakeBody';
self.bodyGraphic = self.attachAsset(self.assetId, {
anchorX: 0.5,
anchorY: 0.5
});
self.nextPosition = {
x: 0,
y: 0
};
// Make head slightly larger
if (isHead) {
self.bodyGraphic.scale.set(1.2, 1.2);
}
return self;
});
var Wall = Container.expand(function () {
var self = Container.call(this);
self.wallGraphic = self.attachAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000 // Always start with black background
});
/****
* Game Code
****/
var levelSelectionScreen = new LevelSelectionScreen();
game.addChild(levelSelectionScreen);
// Full screen mode is not supported by LK engine, removing the call.
var STATE_TITLE = 0;
var STATE_PLAYING = 1;
var STATE_LEVEL_TRANSITION = 2;
var gameState = STATE_TITLE;
// Game variables
var score = 0;
var currentLevel = storage.currentLevel || 0;
var highScore = storage.highScore || 0;
var gridSize = 60;
var movementSpeed = 5; // Frames per movement
var speedCounter = 0;
var gameLevel;
// Snake variables
var snake = [];
var snakeDirection = 'right';
var pendingDirection = 'right';
var snakeSize = 5; // Starting snake size
var snakeGrowing = 0;
// Touch control variables
var touchStartX = 0;
var touchStartY = 0;
// Create UI elements
var scoreTxt = new Text2('Score: 0', {
size: 70,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(scoreTxt);
scoreTxt.x = -scoreTxt.width - 20;
scoreTxt.y = 20;
// Create home button
var homeBtn = new Text2('Home', {
size: 70,
fill: 0xFF4500 // Bright red orange color
});
homeBtn.anchor.set(0.5, 0.5);
LK.gui.topRight.addChild(homeBtn);
homeBtn.x = -homeBtn.width - 20; // Align with score text
homeBtn.y = scoreTxt.y + scoreTxt.height + 20; // Position under the score text
// Add event listener for home button
homeBtn.down = function () {
levelSelectionScreen.visible = true; // Show level selection screen
gameState = STATE_TITLE; // Set game state to title
};
var levelTxt = new Text2('Level: Jungle', {
size: 70,
fill: 0xFFFFFF
});
levelTxt.anchor.set(0, 0);
LK.gui.top.addChild(levelTxt);
levelTxt.y = 20;
var titleTxt = new Text2('SNAKE EVOLUTION\nFOUR REALMS', {
size: 120,
fill: 0xFFFFFF,
align: 'center'
});
titleTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(titleTxt);
var instructionTxt = new Text2('Swipe to control the snake\nEat food to grow longer\nAvoid obstacles and yourself\n\nTap to Start', {
size: 80,
fill: 0xFFFFFF,
align: 'center'
});
instructionTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(instructionTxt);
instructionTxt.y = 300;
// Function to get level name from level number
function getLevelName(levelNum) {
switch (levelNum) {
case 0:
return "Jungle";
case 1:
return "Arctic";
case 2:
return "Desert";
case 3:
return "Cyber";
default:
return "Unknown";
}
}
// Initialize the game
function initGame() {
// Reset game variables
score = 0;
snakeSize = 5;
snakeGrowing = 0;
speedCounter = 0;
// Update UI
updateScore();
updateLevelText();
// Hide title screen elements
titleTxt.visible = false;
instructionTxt.visible = false;
scoreTxt.visible = true;
levelTxt.visible = true;
// Create game level
if (gameLevel) {
gameLevel.destroy();
}
gameLevel = new GameLevel(currentLevel);
game.addChild(gameLevel);
gameLevel.setupLevel();
// Create initial snake
createSnake();
// Set game state to playing
gameState = STATE_PLAYING;
}
// Create the snake with initial segments
function createSnake() {
// Destroy existing snake segments if any
snake.forEach(function (segment) {
segment.destroy();
});
snake = [];
// Create new snake
var centerX, centerY;
if (currentLevel === 1) {
// Arctic level: Start snake away from cross obstacles
centerX = Math.floor(2048 / 4 / gridSize) * gridSize; // Start on the left side
centerY = Math.floor(2732 / 4 / gridSize) * gridSize; // Start on the top side
} else {
centerX = Math.floor(2048 / 2 / gridSize) * gridSize;
centerY = Math.floor(2732 / 2 / gridSize) * gridSize;
}
// Create head
var head = new SnakeSegment(true);
head.x = centerX;
head.y = centerY;
game.addChild(head);
snake.push(head);
// Create body segments
for (var i = 1; i < snakeSize; i++) {
var segment = new SnakeSegment(false);
segment.x = centerX - i * gridSize;
segment.y = centerY;
game.addChild(segment);
snake.push(segment);
}
// Set initial direction
snakeDirection = 'right';
pendingDirection = 'right';
}
// Move the snake
function moveSnake() {
// Calculate new head position
var head = snake[0];
var newX = head.x;
var newY = head.y;
// Update direction based on pending direction
snakeDirection = pendingDirection;
// Calculate new position based on direction
switch (snakeDirection) {
case 'up':
newY -= gridSize;
break;
case 'down':
newY += gridSize;
break;
case 'left':
newX -= gridSize;
break;
case 'right':
newX += gridSize;
break;
}
// Move the snake by updating the target position for each segment
for (var i = snake.length - 1; i >= 0; i--) {
if (i === 0) {
// Head moves to new position
snake[i].nextPosition = {
x: newX,
y: newY
};
} else {
// Body follows the segment in front
snake[i].nextPosition = {
x: snake[i - 1].x,
y: snake[i - 1].y
};
}
}
// Apply movement
snake.forEach(function (segment) {
segment.x = segment.nextPosition.x;
segment.y = segment.nextPosition.y;
});
// Add new segment if snake is growing
if (snakeGrowing > 0) {
var lastSegment = snake[snake.length - 1];
var newSegment = new SnakeSegment(false);
newSegment.x = lastSegment.x;
newSegment.y = lastSegment.y;
game.addChild(newSegment);
snake.push(newSegment);
snakeGrowing--;
}
// Check for collisions
checkCollisions();
}
// Check for collisions with food, walls, obstacles, and self
function checkCollisions() {
var head = snake[0];
// Check collision with food
for (var i = 0; i < gameLevel.foods.length; i++) {
var food = gameLevel.foods[i];
if (distance(head, food) < gridSize * 0.8) {
// Eat food
LK.getSound('eat').play();
LK.getSound('powerUp').play();
// Increase score
score += food.value; // Increment score by the food's value when eaten
updateScore(); // Update the score display
scoreTxt.setText('Score: ' + score); // Ensure score text is updated
// Grow snake
snakeGrowing += food.value;
// Remove food
food.destroy();
gameLevel.foods.splice(i, 1);
// Spawn new food
gameLevel.spawnFood();
// Check if we should level up
if (score >= (currentLevel + 1) * 100 && currentLevel < 3) {
levelUp();
return;
}
break;
}
}
// Check collision with walls
for (var j = 0; j < gameLevel.walls.length; j++) {
if (distance(head, gameLevel.walls[j]) < gridSize * 0.8) {
gameOver();
return;
}
}
// Check collision with obstacles
for (var k = 0; k < gameLevel.obstacles.length; k++) {
if (distance(head, gameLevel.obstacles[k]) < gridSize * 0.8) {
gameOver();
return;
}
}
// Check collision with self (skip the first few segments)
for (var l = 3; l < snake.length; l++) {
if (distance(head, snake[l]) < gridSize * 0.5) {
gameOver();
return;
}
}
// Check if snake is out of bounds
if (head.x < 0 || head.x > 2048 || head.y < 0 || head.y > 2732) {
gameOver();
return;
}
}
// Calculate distance between two objects
function distance(obj1, obj2) {
var dx = obj1.x - obj2.x;
var dy = obj1.y - obj2.y;
return Math.sqrt(dx * dx + dy * dy);
}
// Handle level up
function levelUp() {
// Play level up sound
LK.getSound('levelUp').play();
// Save current level
currentLevel++;
storage.currentLevel = currentLevel;
// Show transition
gameState = STATE_LEVEL_TRANSITION;
// Create level transition text
var levelUpTxt = new Text2('LEVEL UP!\n' + getLevelName(currentLevel), {
size: 120,
fill: 0xFFFFFF,
align: 'center'
});
levelUpTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(levelUpTxt);
// Fade out after 2 seconds
LK.setTimeout(function () {
tween(levelUpTxt, {
alpha: 0
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
levelUpTxt.destroy();
initGame();
}
});
}, 2000);
}
// Handle game over
function gameOver() {
// Play collision sound
LK.getSound('collision').play();
LK.getSound('hitWall').play();
// Flash screen red
LK.effects.flashScreen(0xff0000, 500);
// Update high score
if (score > highScore) {
highScore = score;
storage.highScore = highScore;
}
// Show game over
LK.showGameOver();
}
// Update the score display
function updateScore() {
scoreTxt.setText('Score: ' + score);
}
// Update the level text
function updateLevelText() {
levelTxt.setText('Level: ' + getLevelName(currentLevel));
}
// Handle touch events
game.down = function (x, y, obj) {
if (gameState === STATE_PLAYING) {
// Record start of swipe
touchStartX = x;
touchStartY = y;
}
};
game.up = function (x, y, obj) {
if (gameState === STATE_PLAYING) {
// Calculate swipe direction
var dx = x - touchStartX;
var dy = y - touchStartY;
// Only change direction if the swipe is long enough
if (Math.abs(dx) > 30 || Math.abs(dy) > 30) {
// Determine primary direction of swipe
if (Math.abs(dx) > Math.abs(dy)) {
// Horizontal swipe
pendingDirection = dx > 0 ? 'right' : 'left';
} else {
// Vertical swipe
pendingDirection = dy > 0 ? 'down' : 'up';
}
// Don't allow 180-degree turns
if (snakeDirection === 'up' && pendingDirection === 'down' || snakeDirection === 'down' && pendingDirection === 'up' || snakeDirection === 'left' && pendingDirection === 'right' || snakeDirection === 'right' && pendingDirection === 'left') {
pendingDirection = snakeDirection;
}
}
}
};
// Game update function
game.update = function () {
if (gameState === STATE_PLAYING) {
// Move snake at appropriate speed
speedCounter++;
var moveInterval = movementSpeed - Math.min(3, Math.floor(currentLevel / 2));
if (speedCounter >= moveInterval) {
moveSnake();
speedCounter = 0;
}
}
};
// Start with level selection screen
titleTxt.visible = false;
instructionTxt.visible = false;
scoreTxt.visible = false;
levelTxt.visible = false;
levelSelectionScreen.visible = true;
// Initialize background
gameLevel = new GameLevel(currentLevel);
game.addChild(gameLevel);
gameLevel.setupLevel(); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
currentLevel: 0,
highScore: 0
});
/****
* Classes
****/
var Food = Container.expand(function () {
var self = Container.call(this);
self.foodGraphic = self.attachAsset('food', {
anchorX: 0.5,
anchorY: 0.5
});
self.value = 1; // Default food value
// Pulsing animation
self.pulseFood = function () {
tween(self.foodGraphic.scale, {
x: 1.2,
y: 1.2
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self.foodGraphic.scale, {
x: 1.0,
y: 1.0
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: self.pulseFood
});
}
});
};
self.pulseFood();
return self;
});
var GameLevel = Container.expand(function (levelNum) {
var self = Container.call(this);
self.levelNumber = levelNum || 0;
self.walls = [];
self.obstacles = [];
self.foods = [];
self.backgroundColor = 0x88cc66; // Default jungle background
self.setupLevel = function () {
// Clear any existing level elements
self.walls.forEach(function (wall) {
wall.destroy();
});
self.obstacles.forEach(function (obstacle) {
obstacle.destroy();
});
self.foods.forEach(function (food) {
food.destroy();
});
self.walls = [];
self.obstacles = [];
self.foods = [];
// Set up specific level based on level number
switch (self.levelNumber) {
case 0:
// Jungle - Easy
self.backgroundColor = 0x88cc66;
self.createBorderWalls();
LK.playMusic('jungleMusic');
break;
case 1:
// Arctic - Medium
self.backgroundColor = 0xaaddff;
self.createBorderWalls();
self.createCrossObstacles();
LK.playMusic('arcticMusic');
break;
case 2:
// Desert - Hard
self.backgroundColor = 0xddbb77;
self.createBorderWalls();
self.createRandomObstacles(10);
LK.playMusic('desertMusic');
break;
case 3:
// Cyber - Expert
self.backgroundColor = 0x223355;
self.createBorderWalls();
self.createMazeObstacles();
LK.playMusic('cyberMusic');
break;
}
game.setBackgroundColor(self.backgroundColor);
self.spawnFood();
};
self.createBorderWalls = function () {
// Create border walls
var wallThickness = 30;
var wallSpacing = 60; // Space between wall segments
// Top and bottom walls
for (var x = wallThickness; x < 2048; x += wallSpacing) {
// Top wall
var topWall = new Wall();
topWall.x = x;
topWall.y = wallThickness;
self.walls.push(topWall);
self.addChild(topWall);
// Bottom wall
var bottomWall = new Wall();
bottomWall.x = x;
bottomWall.y = 2732 - wallThickness;
self.walls.push(bottomWall);
self.addChild(bottomWall);
}
// Left and right walls
for (var y = wallThickness; y < 2732; y += wallSpacing) {
// Left wall
var leftWall = new Wall();
leftWall.x = wallThickness;
leftWall.y = y;
self.walls.push(leftWall);
self.addChild(leftWall);
// Right wall
var rightWall = new Wall();
rightWall.x = 2048 - wallThickness;
rightWall.y = y;
self.walls.push(rightWall);
self.addChild(rightWall);
}
};
self.createCrossObstacles = function () {
// Create cross-shaped obstacles for Arctic level
var centerX = 2048 / 2;
var centerY = 2732 / 2;
var spacing = 120;
// Horizontal line
for (var x = centerX - 400; x <= centerX + 400; x += spacing) {
var hObstacle = new Obstacle();
hObstacle.x = x;
hObstacle.y = centerY;
self.obstacles.push(hObstacle);
self.addChild(hObstacle);
}
// Vertical line
for (var y = centerY - 600; y <= centerY + 600; y += spacing) {
if (Math.abs(y - centerY) > spacing / 2) {
// Skip center to avoid overlap
var vObstacle = new Obstacle();
vObstacle.x = centerX;
vObstacle.y = y;
self.obstacles.push(vObstacle);
self.addChild(vObstacle);
}
}
};
self.createRandomObstacles = function (count) {
// Create random obstacles for Desert level
var margin = 150; // Keep obstacles away from edges
for (var i = 0; i < count; i++) {
var obstacle = new Obstacle();
// Keep trying positions until we find one that doesn't overlap
var validPosition = false;
var maxTries = 20;
var tries = 0;
while (!validPosition && tries < maxTries) {
obstacle.x = margin + Math.random() * (2048 - 2 * margin);
obstacle.y = margin + Math.random() * (2732 - 2 * margin);
// Check distance from other obstacles
validPosition = true;
for (var j = 0; j < self.obstacles.length; j++) {
var existingObstacle = self.obstacles[j];
var dx = obstacle.x - existingObstacle.x;
var dy = obstacle.y - existingObstacle.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 200) {
validPosition = false;
break;
}
}
tries++;
}
if (validPosition) {
self.obstacles.push(obstacle);
self.addChild(obstacle);
}
}
};
self.createMazeObstacles = function () {
// Create maze-like obstacles for Cyber level
var gridSize = 150;
var obstacleChance = 0.35; // Probability of placing an obstacle
for (var x = gridSize * 2; x < 2048 - gridSize * 2; x += gridSize) {
for (var y = gridSize * 2; y < 2732 - gridSize * 2; y += gridSize) {
// Skip center area to ensure there's space to move
var distFromCenter = Math.sqrt(Math.pow(x - 2048 / 2, 2) + Math.pow(y - 2732 / 2, 2));
if (distFromCenter < 400) {
continue;
}
if (Math.random() < obstacleChance) {
var obstacle = new Obstacle();
obstacle.x = x;
obstacle.y = y;
self.obstacles.push(obstacle);
self.addChild(obstacle);
}
}
}
};
self.spawnFood = function () {
var food = new Food();
var validPosition = false;
var gridSize = 60;
var margin = 120; // Keep food away from edges
// Keep trying until we find a valid position
while (!validPosition) {
// Generate random position, aligned to grid
var gridX = Math.floor((margin + Math.random() * (2048 - 2 * margin)) / gridSize);
var gridY = Math.floor((margin + Math.random() * (2732 - 2 * margin)) / gridSize);
food.x = gridX * gridSize;
food.y = gridY * gridSize;
// Check for collisions with walls and obstacles
validPosition = true;
// Check walls
for (var i = 0; i < self.walls.length; i++) {
if (self.isCollision(food, self.walls[i], 80)) {
validPosition = false;
break;
}
}
// Check obstacles
if (validPosition) {
for (var j = 0; j < self.obstacles.length; j++) {
if (self.isCollision(food, self.obstacles[j], 80)) {
validPosition = false;
break;
}
}
}
}
// Set food value based on level
if (self.levelNumber > 0) {
food.value = self.levelNumber + 1;
// Change food color based on value
switch (food.value) {
case 2:
food.foodGraphic.tint = 0x33ccff; // Blue for level 1
break;
case 3:
food.foodGraphic.tint = 0xffcc33; // Orange for level 2
break;
case 4:
food.foodGraphic.tint = 0xff33cc; // Pink for level 3
break;
}
}
self.foods.push(food);
self.addChild(food);
};
self.isCollision = function (obj1, obj2, minDistance) {
var dx = obj1.x - obj2.x;
var dy = obj1.y - obj2.y;
var distance = Math.sqrt(dx * dx + dy * dy);
return distance < minDistance;
};
return self;
});
var LevelSelectionScreen = Container.expand(function () {
var self = Container.call(this);
// Background image for level selection
var background = self.attachAsset('background', {
anchorX: 0.5,
anchorY: 0.5
});
background.x = 2048 / 2;
background.y = 2732 / 2;
// Level buttons
var levels = [{
name: "Jungle",
color: 0x88cc66
}, {
name: "Arctic",
color: 0xaaddff
}, {
name: "Desert",
color: 0xddbb77
}, {
name: "Cyber",
color: 0x223355
}];
levels.forEach(function (level, index) {
var levelBtn = new Text2('Level ' + (index + 1) + ': ' + level.name, {
size: 100,
fill: index === 1 ? 0xff0000 : level.color,
// Bright red for Arctic
align: 'center',
fontWeight: 'bold'
});
levelBtn.anchor.set(0.5, 0.5);
levelBtn.x = 2048 / 2;
levelBtn.y = 800 + index * 200;
self.addChild(levelBtn);
levelBtn.down = function () {
currentLevel = index;
storage.currentLevel = currentLevel;
self.visible = false;
initGame();
};
});
return self;
});
var Obstacle = Container.expand(function () {
var self = Container.call(this);
self.obstacleGraphic = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var SnakeSegment = Container.expand(function (isHead) {
var self = Container.call(this);
self.assetId = isHead ? 'snakeHead' : 'snakeBody';
self.bodyGraphic = self.attachAsset(self.assetId, {
anchorX: 0.5,
anchorY: 0.5
});
self.nextPosition = {
x: 0,
y: 0
};
// Make head slightly larger
if (isHead) {
self.bodyGraphic.scale.set(1.2, 1.2);
}
return self;
});
var Wall = Container.expand(function () {
var self = Container.call(this);
self.wallGraphic = self.attachAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000 // Always start with black background
});
/****
* Game Code
****/
var levelSelectionScreen = new LevelSelectionScreen();
game.addChild(levelSelectionScreen);
// Full screen mode is not supported by LK engine, removing the call.
var STATE_TITLE = 0;
var STATE_PLAYING = 1;
var STATE_LEVEL_TRANSITION = 2;
var gameState = STATE_TITLE;
// Game variables
var score = 0;
var currentLevel = storage.currentLevel || 0;
var highScore = storage.highScore || 0;
var gridSize = 60;
var movementSpeed = 5; // Frames per movement
var speedCounter = 0;
var gameLevel;
// Snake variables
var snake = [];
var snakeDirection = 'right';
var pendingDirection = 'right';
var snakeSize = 5; // Starting snake size
var snakeGrowing = 0;
// Touch control variables
var touchStartX = 0;
var touchStartY = 0;
// Create UI elements
var scoreTxt = new Text2('Score: 0', {
size: 70,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(scoreTxt);
scoreTxt.x = -scoreTxt.width - 20;
scoreTxt.y = 20;
// Create home button
var homeBtn = new Text2('Home', {
size: 70,
fill: 0xFF4500 // Bright red orange color
});
homeBtn.anchor.set(0.5, 0.5);
LK.gui.topRight.addChild(homeBtn);
homeBtn.x = -homeBtn.width - 20; // Align with score text
homeBtn.y = scoreTxt.y + scoreTxt.height + 20; // Position under the score text
// Add event listener for home button
homeBtn.down = function () {
levelSelectionScreen.visible = true; // Show level selection screen
gameState = STATE_TITLE; // Set game state to title
};
var levelTxt = new Text2('Level: Jungle', {
size: 70,
fill: 0xFFFFFF
});
levelTxt.anchor.set(0, 0);
LK.gui.top.addChild(levelTxt);
levelTxt.y = 20;
var titleTxt = new Text2('SNAKE EVOLUTION\nFOUR REALMS', {
size: 120,
fill: 0xFFFFFF,
align: 'center'
});
titleTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(titleTxt);
var instructionTxt = new Text2('Swipe to control the snake\nEat food to grow longer\nAvoid obstacles and yourself\n\nTap to Start', {
size: 80,
fill: 0xFFFFFF,
align: 'center'
});
instructionTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(instructionTxt);
instructionTxt.y = 300;
// Function to get level name from level number
function getLevelName(levelNum) {
switch (levelNum) {
case 0:
return "Jungle";
case 1:
return "Arctic";
case 2:
return "Desert";
case 3:
return "Cyber";
default:
return "Unknown";
}
}
// Initialize the game
function initGame() {
// Reset game variables
score = 0;
snakeSize = 5;
snakeGrowing = 0;
speedCounter = 0;
// Update UI
updateScore();
updateLevelText();
// Hide title screen elements
titleTxt.visible = false;
instructionTxt.visible = false;
scoreTxt.visible = true;
levelTxt.visible = true;
// Create game level
if (gameLevel) {
gameLevel.destroy();
}
gameLevel = new GameLevel(currentLevel);
game.addChild(gameLevel);
gameLevel.setupLevel();
// Create initial snake
createSnake();
// Set game state to playing
gameState = STATE_PLAYING;
}
// Create the snake with initial segments
function createSnake() {
// Destroy existing snake segments if any
snake.forEach(function (segment) {
segment.destroy();
});
snake = [];
// Create new snake
var centerX, centerY;
if (currentLevel === 1) {
// Arctic level: Start snake away from cross obstacles
centerX = Math.floor(2048 / 4 / gridSize) * gridSize; // Start on the left side
centerY = Math.floor(2732 / 4 / gridSize) * gridSize; // Start on the top side
} else {
centerX = Math.floor(2048 / 2 / gridSize) * gridSize;
centerY = Math.floor(2732 / 2 / gridSize) * gridSize;
}
// Create head
var head = new SnakeSegment(true);
head.x = centerX;
head.y = centerY;
game.addChild(head);
snake.push(head);
// Create body segments
for (var i = 1; i < snakeSize; i++) {
var segment = new SnakeSegment(false);
segment.x = centerX - i * gridSize;
segment.y = centerY;
game.addChild(segment);
snake.push(segment);
}
// Set initial direction
snakeDirection = 'right';
pendingDirection = 'right';
}
// Move the snake
function moveSnake() {
// Calculate new head position
var head = snake[0];
var newX = head.x;
var newY = head.y;
// Update direction based on pending direction
snakeDirection = pendingDirection;
// Calculate new position based on direction
switch (snakeDirection) {
case 'up':
newY -= gridSize;
break;
case 'down':
newY += gridSize;
break;
case 'left':
newX -= gridSize;
break;
case 'right':
newX += gridSize;
break;
}
// Move the snake by updating the target position for each segment
for (var i = snake.length - 1; i >= 0; i--) {
if (i === 0) {
// Head moves to new position
snake[i].nextPosition = {
x: newX,
y: newY
};
} else {
// Body follows the segment in front
snake[i].nextPosition = {
x: snake[i - 1].x,
y: snake[i - 1].y
};
}
}
// Apply movement
snake.forEach(function (segment) {
segment.x = segment.nextPosition.x;
segment.y = segment.nextPosition.y;
});
// Add new segment if snake is growing
if (snakeGrowing > 0) {
var lastSegment = snake[snake.length - 1];
var newSegment = new SnakeSegment(false);
newSegment.x = lastSegment.x;
newSegment.y = lastSegment.y;
game.addChild(newSegment);
snake.push(newSegment);
snakeGrowing--;
}
// Check for collisions
checkCollisions();
}
// Check for collisions with food, walls, obstacles, and self
function checkCollisions() {
var head = snake[0];
// Check collision with food
for (var i = 0; i < gameLevel.foods.length; i++) {
var food = gameLevel.foods[i];
if (distance(head, food) < gridSize * 0.8) {
// Eat food
LK.getSound('eat').play();
LK.getSound('powerUp').play();
// Increase score
score += food.value; // Increment score by the food's value when eaten
updateScore(); // Update the score display
scoreTxt.setText('Score: ' + score); // Ensure score text is updated
// Grow snake
snakeGrowing += food.value;
// Remove food
food.destroy();
gameLevel.foods.splice(i, 1);
// Spawn new food
gameLevel.spawnFood();
// Check if we should level up
if (score >= (currentLevel + 1) * 100 && currentLevel < 3) {
levelUp();
return;
}
break;
}
}
// Check collision with walls
for (var j = 0; j < gameLevel.walls.length; j++) {
if (distance(head, gameLevel.walls[j]) < gridSize * 0.8) {
gameOver();
return;
}
}
// Check collision with obstacles
for (var k = 0; k < gameLevel.obstacles.length; k++) {
if (distance(head, gameLevel.obstacles[k]) < gridSize * 0.8) {
gameOver();
return;
}
}
// Check collision with self (skip the first few segments)
for (var l = 3; l < snake.length; l++) {
if (distance(head, snake[l]) < gridSize * 0.5) {
gameOver();
return;
}
}
// Check if snake is out of bounds
if (head.x < 0 || head.x > 2048 || head.y < 0 || head.y > 2732) {
gameOver();
return;
}
}
// Calculate distance between two objects
function distance(obj1, obj2) {
var dx = obj1.x - obj2.x;
var dy = obj1.y - obj2.y;
return Math.sqrt(dx * dx + dy * dy);
}
// Handle level up
function levelUp() {
// Play level up sound
LK.getSound('levelUp').play();
// Save current level
currentLevel++;
storage.currentLevel = currentLevel;
// Show transition
gameState = STATE_LEVEL_TRANSITION;
// Create level transition text
var levelUpTxt = new Text2('LEVEL UP!\n' + getLevelName(currentLevel), {
size: 120,
fill: 0xFFFFFF,
align: 'center'
});
levelUpTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(levelUpTxt);
// Fade out after 2 seconds
LK.setTimeout(function () {
tween(levelUpTxt, {
alpha: 0
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
levelUpTxt.destroy();
initGame();
}
});
}, 2000);
}
// Handle game over
function gameOver() {
// Play collision sound
LK.getSound('collision').play();
LK.getSound('hitWall').play();
// Flash screen red
LK.effects.flashScreen(0xff0000, 500);
// Update high score
if (score > highScore) {
highScore = score;
storage.highScore = highScore;
}
// Show game over
LK.showGameOver();
}
// Update the score display
function updateScore() {
scoreTxt.setText('Score: ' + score);
}
// Update the level text
function updateLevelText() {
levelTxt.setText('Level: ' + getLevelName(currentLevel));
}
// Handle touch events
game.down = function (x, y, obj) {
if (gameState === STATE_PLAYING) {
// Record start of swipe
touchStartX = x;
touchStartY = y;
}
};
game.up = function (x, y, obj) {
if (gameState === STATE_PLAYING) {
// Calculate swipe direction
var dx = x - touchStartX;
var dy = y - touchStartY;
// Only change direction if the swipe is long enough
if (Math.abs(dx) > 30 || Math.abs(dy) > 30) {
// Determine primary direction of swipe
if (Math.abs(dx) > Math.abs(dy)) {
// Horizontal swipe
pendingDirection = dx > 0 ? 'right' : 'left';
} else {
// Vertical swipe
pendingDirection = dy > 0 ? 'down' : 'up';
}
// Don't allow 180-degree turns
if (snakeDirection === 'up' && pendingDirection === 'down' || snakeDirection === 'down' && pendingDirection === 'up' || snakeDirection === 'left' && pendingDirection === 'right' || snakeDirection === 'right' && pendingDirection === 'left') {
pendingDirection = snakeDirection;
}
}
}
};
// Game update function
game.update = function () {
if (gameState === STATE_PLAYING) {
// Move snake at appropriate speed
speedCounter++;
var moveInterval = movementSpeed - Math.min(3, Math.floor(currentLevel / 2));
if (speedCounter >= moveInterval) {
moveSnake();
speedCounter = 0;
}
}
};
// Start with level selection screen
titleTxt.visible = false;
instructionTxt.visible = false;
scoreTxt.visible = false;
levelTxt.visible = false;
levelSelectionScreen.visible = true;
// Initialize background
gameLevel = new GameLevel(currentLevel);
game.addChild(gameLevel);
gameLevel.setupLevel();
Juicy or glossy ball, like a glowing apple or bouncing berry.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Two big eyes with tounge outside that is snake head with yellow and redcolors . Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows