/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Orb class
var Orb = Container.expand(function () {
var self = Container.call(this);
var orb = self.attachAsset('orb', {
anchorX: 0.5,
anchorY: 0.5
});
// Animate orb with a pulsing effect
tween(orb, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(orb, {
scaleX: 1,
scaleY: 1
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Loop
if (self.parent) {
orb.scaleX = 1;
orb.scaleY = 1;
tween(orb, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: arguments.callee
});
}
}
});
}
});
return self;
});
// Snake Segment (body or head)
var SnakeSegment = Container.expand(function () {
var self = Container.call(this);
// By default, body segment
var assetId = self.isHead ? 'snakeHead' : 'snakeBody';
var seg = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181c2c
});
/****
* Game Code
****/
// Orb: glowing yellow
// Snake body: ellipse, lighter green
// Snake head: ellipse, bright green
// Game constants
var GAME_W = 2048;
var GAME_H = 2732;
var ARENA_MARGIN = 60; // px, keep snake/orbs away from edge
var SNAKE_INIT_LEN = 6;
var SNAKE_SPEED = 13; // px per tick
var SNAKE_TURN_SPEED = 0.16; // radians per tick
var ORB_COUNT = 3;
// State
var snake = [];
var snakeDirs = []; // Array of directions (radians) for each segment
var snakeTargetDir = 0; // radians, where the head is steering
var snakeCurDir = 0; // radians, current head direction
var snakePendingGrowth = 0;
var orbs = [];
var dragging = false;
var dragTarget = {
x: 0,
y: 0
};
var score = 0;
var gameOver = false;
// GUI
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Helper: clamp
function clamp(val, min, max) {
return Math.max(min, Math.min(max, val));
}
// Helper: distance
function dist(ax, ay, bx, by) {
var dx = ax - bx;
var dy = ay - by;
return Math.sqrt(dx * dx + dy * dy);
}
// Helper: angle between two points
function angleTo(ax, ay, bx, by) {
return Math.atan2(by - ay, bx - ax);
}
// Helper: wrap angle to [-PI, PI]
function wrapAngle(a) {
while (a > Math.PI) a -= Math.PI * 2;
while (a < -Math.PI) a += Math.PI * 2;
return a;
}
// Helper: random position inside arena
function randomArenaPos() {
var x = ARENA_MARGIN + Math.random() * (GAME_W - 2 * ARENA_MARGIN);
var y = ARENA_MARGIN + Math.random() * (GAME_H - 2 * ARENA_MARGIN);
return {
x: x,
y: y
};
}
// Place orbs, avoiding snake
function placeOrb(orb) {
var tries = 0;
while (tries < 20) {
var pos = randomArenaPos();
var ok = true;
for (var i = 0; i < snake.length; ++i) {
if (dist(pos.x, pos.y, snake[i].x, snake[i].y) < 180) {
ok = false;
break;
}
}
if (ok) {
orb.x = pos.x;
orb.y = pos.y;
return;
}
tries++;
}
// fallback
orb.x = ARENA_MARGIN + Math.random() * (GAME_W - 2 * ARENA_MARGIN);
orb.y = ARENA_MARGIN + Math.random() * (GAME_H - 2 * ARENA_MARGIN);
}
// Initialize snake
function initSnake() {
// Remove old
for (var i = 0; i < snake.length; ++i) {
snake[i].destroy();
}
snake = [];
snakeDirs = [];
// Center start
var startX = GAME_W / 2;
var startY = GAME_H / 2;
var dir = -Math.PI / 2; // Up
for (var i = 0; i < SNAKE_INIT_LEN; ++i) {
var seg = new SnakeSegment();
seg.isHead = i === 0;
// Re-attach correct asset for head
if (seg.isHead) {
seg.removeChildren();
seg.attachAsset('snakeHead', {
anchorX: 0.5,
anchorY: 0.5
});
}
seg.x = startX - Math.cos(dir) * i * 85;
seg.y = startY - Math.sin(dir) * i * 85;
game.addChild(seg);
snake.push(seg);
snakeDirs.push(dir);
}
snakeCurDir = dir;
snakeTargetDir = dir;
snakePendingGrowth = 0;
}
// Initialize orbs
function initOrbs() {
for (var i = 0; i < orbs.length; ++i) {
orbs[i].destroy();
}
orbs = [];
for (var i = 0; i < ORB_COUNT; ++i) {
var orb = new Orb();
placeOrb(orb);
game.addChild(orb);
orbs.push(orb);
}
}
// Reset game state
function resetGame() {
score = 0;
scoreTxt.setText(score);
gameOver = false;
initSnake();
initOrbs();
}
// Start game
resetGame();
// Touch/drag controls
game.down = function (x, y, obj) {
// Don't allow drag in top left 100x100
if (x < 100 && y < 100) return;
dragging = true;
dragTarget.x = x;
dragTarget.y = y;
// Set target direction immediately
var head = snake[0];
snakeTargetDir = angleTo(head.x, head.y, x, y);
};
game.move = function (x, y, obj) {
if (!dragging) return;
dragTarget.x = x;
dragTarget.y = y;
var head = snake[0];
snakeTargetDir = angleTo(head.x, head.y, x, y);
};
game.up = function (x, y, obj) {
dragging = false;
};
// Main update loop
game.update = function () {
if (gameOver) return;
// Move snake head
var head = snake[0];
var angleDiff = wrapAngle(snakeTargetDir - snakeCurDir);
// Limit turn speed
if (Math.abs(angleDiff) > SNAKE_TURN_SPEED) {
snakeCurDir += SNAKE_TURN_SPEED * (angleDiff > 0 ? 1 : -1);
} else {
snakeCurDir = snakeTargetDir;
}
// Move head forward
var newHeadX = head.x + Math.cos(snakeCurDir) * SNAKE_SPEED;
var newHeadY = head.y + Math.sin(snakeCurDir) * SNAKE_SPEED;
// Check wall collision
if (newHeadX < ARENA_MARGIN || newHeadX > GAME_W - ARENA_MARGIN || newHeadY < ARENA_MARGIN || newHeadY > GAME_H - ARENA_MARGIN) {
// Game over
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
gameOver = true;
return;
}
// Move body: each segment follows the previous
var prevX = head.x,
prevY = head.y;
var prevDir = snakeCurDir;
// Move head
head.x = newHeadX;
head.y = newHeadY;
snakeDirs[0] = snakeCurDir;
for (var i = 1; i < snake.length; ++i) {
var seg = snake[i];
var dx = prevX - seg.x;
var dy = prevY - seg.y;
var d = Math.sqrt(dx * dx + dy * dy);
var desiredDist = 85;
if (d > 1) {
var moveDist = clamp(d - desiredDist, 0, SNAKE_SPEED * 1.2);
if (moveDist > 0) {
var moveX = seg.x + dx / d * moveDist;
var moveY = seg.y + dy / d * moveDist;
seg.x = moveX;
seg.y = moveY;
}
}
// Face the direction of movement
snakeDirs[i] = Math.atan2(seg.y - prevY, seg.x - prevX);
prevX = seg.x;
prevY = seg.y;
prevDir = snakeDirs[i];
}
// Check self-collision (head with body, skip first 3 segments for leniency)
for (var i = 3; i < snake.length; ++i) {
var seg = snake[i];
if (dist(head.x, head.y, seg.x, seg.y) < 60) {
// Game over
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
gameOver = true;
return;
}
}
// Check orb collision
for (var i = 0; i < orbs.length; ++i) {
var orb = orbs[i];
if (dist(head.x, head.y, orb.x, orb.y) < 80) {
// Eat orb
score += 1;
scoreTxt.setText(score);
snakePendingGrowth += 2;
// Animate orb
tween(orb, {
scaleX: 1.7,
scaleY: 1.7,
alpha: 0
}, {
duration: 180,
easing: tween.easeOut,
onFinish: function (orb, i) {
return function () {
orb.scaleX = 1;
orb.scaleY = 1;
orb.alpha = 1;
placeOrb(orb);
};
}(orb, i)
});
}
}
// Grow snake if needed
while (snakePendingGrowth > 0) {
var last = snake[snake.length - 1];
var dir = snakeDirs[snake.length - 1];
var seg = new SnakeSegment();
seg.isHead = false;
seg.removeChildren();
seg.attachAsset('snakeBody', {
anchorX: 0.5,
anchorY: 0.5
});
// Place at last segment's position, trailing behind
seg.x = last.x - Math.cos(dir) * 85;
seg.y = last.y - Math.sin(dir) * 85;
game.addChild(seg);
snake.push(seg);
snakeDirs.push(dir);
snakePendingGrowth--;
}
};
// On game over, reset state for replay
LK.on('gameover', function () {
resetGame();
});
LK.on('youWin', function () {
resetGame();
}); ===================================================================
--- original.js
+++ change.js
@@ -1,6 +1,349 @@
-/****
+/****
+* Plugins
+****/
+var tween = LK.import("@upit/tween.v1");
+
+/****
+* Classes
+****/
+// Orb class
+var Orb = Container.expand(function () {
+ var self = Container.call(this);
+ var orb = self.attachAsset('orb', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ // Animate orb with a pulsing effect
+ tween(orb, {
+ scaleX: 1.2,
+ scaleY: 1.2
+ }, {
+ duration: 600,
+ easing: tween.easeInOut,
+ onFinish: function onFinish() {
+ tween(orb, {
+ scaleX: 1,
+ scaleY: 1
+ }, {
+ duration: 600,
+ easing: tween.easeInOut,
+ onFinish: function onFinish() {
+ // Loop
+ if (self.parent) {
+ orb.scaleX = 1;
+ orb.scaleY = 1;
+ tween(orb, {
+ scaleX: 1.2,
+ scaleY: 1.2
+ }, {
+ duration: 600,
+ easing: tween.easeInOut,
+ onFinish: arguments.callee
+ });
+ }
+ }
+ });
+ }
+ });
+ return self;
+});
+// Snake Segment (body or head)
+var SnakeSegment = Container.expand(function () {
+ var self = Container.call(this);
+ // By default, body segment
+ var assetId = self.isHead ? 'snakeHead' : 'snakeBody';
+ var seg = self.attachAsset(assetId, {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ return self;
+});
+
+/****
* Initialize Game
-****/
+****/
var game = new LK.Game({
- backgroundColor: 0x000000
+ backgroundColor: 0x181c2c
+});
+
+/****
+* Game Code
+****/
+// Orb: glowing yellow
+// Snake body: ellipse, lighter green
+// Snake head: ellipse, bright green
+// Game constants
+var GAME_W = 2048;
+var GAME_H = 2732;
+var ARENA_MARGIN = 60; // px, keep snake/orbs away from edge
+var SNAKE_INIT_LEN = 6;
+var SNAKE_SPEED = 13; // px per tick
+var SNAKE_TURN_SPEED = 0.16; // radians per tick
+var ORB_COUNT = 3;
+// State
+var snake = [];
+var snakeDirs = []; // Array of directions (radians) for each segment
+var snakeTargetDir = 0; // radians, where the head is steering
+var snakeCurDir = 0; // radians, current head direction
+var snakePendingGrowth = 0;
+var orbs = [];
+var dragging = false;
+var dragTarget = {
+ x: 0,
+ y: 0
+};
+var score = 0;
+var gameOver = false;
+// GUI
+var scoreTxt = new Text2('0', {
+ size: 120,
+ fill: "#fff"
+});
+scoreTxt.anchor.set(0.5, 0);
+LK.gui.top.addChild(scoreTxt);
+// Helper: clamp
+function clamp(val, min, max) {
+ return Math.max(min, Math.min(max, val));
+}
+// Helper: distance
+function dist(ax, ay, bx, by) {
+ var dx = ax - bx;
+ var dy = ay - by;
+ return Math.sqrt(dx * dx + dy * dy);
+}
+// Helper: angle between two points
+function angleTo(ax, ay, bx, by) {
+ return Math.atan2(by - ay, bx - ax);
+}
+// Helper: wrap angle to [-PI, PI]
+function wrapAngle(a) {
+ while (a > Math.PI) a -= Math.PI * 2;
+ while (a < -Math.PI) a += Math.PI * 2;
+ return a;
+}
+// Helper: random position inside arena
+function randomArenaPos() {
+ var x = ARENA_MARGIN + Math.random() * (GAME_W - 2 * ARENA_MARGIN);
+ var y = ARENA_MARGIN + Math.random() * (GAME_H - 2 * ARENA_MARGIN);
+ return {
+ x: x,
+ y: y
+ };
+}
+// Place orbs, avoiding snake
+function placeOrb(orb) {
+ var tries = 0;
+ while (tries < 20) {
+ var pos = randomArenaPos();
+ var ok = true;
+ for (var i = 0; i < snake.length; ++i) {
+ if (dist(pos.x, pos.y, snake[i].x, snake[i].y) < 180) {
+ ok = false;
+ break;
+ }
+ }
+ if (ok) {
+ orb.x = pos.x;
+ orb.y = pos.y;
+ return;
+ }
+ tries++;
+ }
+ // fallback
+ orb.x = ARENA_MARGIN + Math.random() * (GAME_W - 2 * ARENA_MARGIN);
+ orb.y = ARENA_MARGIN + Math.random() * (GAME_H - 2 * ARENA_MARGIN);
+}
+// Initialize snake
+function initSnake() {
+ // Remove old
+ for (var i = 0; i < snake.length; ++i) {
+ snake[i].destroy();
+ }
+ snake = [];
+ snakeDirs = [];
+ // Center start
+ var startX = GAME_W / 2;
+ var startY = GAME_H / 2;
+ var dir = -Math.PI / 2; // Up
+ for (var i = 0; i < SNAKE_INIT_LEN; ++i) {
+ var seg = new SnakeSegment();
+ seg.isHead = i === 0;
+ // Re-attach correct asset for head
+ if (seg.isHead) {
+ seg.removeChildren();
+ seg.attachAsset('snakeHead', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ }
+ seg.x = startX - Math.cos(dir) * i * 85;
+ seg.y = startY - Math.sin(dir) * i * 85;
+ game.addChild(seg);
+ snake.push(seg);
+ snakeDirs.push(dir);
+ }
+ snakeCurDir = dir;
+ snakeTargetDir = dir;
+ snakePendingGrowth = 0;
+}
+// Initialize orbs
+function initOrbs() {
+ for (var i = 0; i < orbs.length; ++i) {
+ orbs[i].destroy();
+ }
+ orbs = [];
+ for (var i = 0; i < ORB_COUNT; ++i) {
+ var orb = new Orb();
+ placeOrb(orb);
+ game.addChild(orb);
+ orbs.push(orb);
+ }
+}
+// Reset game state
+function resetGame() {
+ score = 0;
+ scoreTxt.setText(score);
+ gameOver = false;
+ initSnake();
+ initOrbs();
+}
+// Start game
+resetGame();
+// Touch/drag controls
+game.down = function (x, y, obj) {
+ // Don't allow drag in top left 100x100
+ if (x < 100 && y < 100) return;
+ dragging = true;
+ dragTarget.x = x;
+ dragTarget.y = y;
+ // Set target direction immediately
+ var head = snake[0];
+ snakeTargetDir = angleTo(head.x, head.y, x, y);
+};
+game.move = function (x, y, obj) {
+ if (!dragging) return;
+ dragTarget.x = x;
+ dragTarget.y = y;
+ var head = snake[0];
+ snakeTargetDir = angleTo(head.x, head.y, x, y);
+};
+game.up = function (x, y, obj) {
+ dragging = false;
+};
+// Main update loop
+game.update = function () {
+ if (gameOver) return;
+ // Move snake head
+ var head = snake[0];
+ var angleDiff = wrapAngle(snakeTargetDir - snakeCurDir);
+ // Limit turn speed
+ if (Math.abs(angleDiff) > SNAKE_TURN_SPEED) {
+ snakeCurDir += SNAKE_TURN_SPEED * (angleDiff > 0 ? 1 : -1);
+ } else {
+ snakeCurDir = snakeTargetDir;
+ }
+ // Move head forward
+ var newHeadX = head.x + Math.cos(snakeCurDir) * SNAKE_SPEED;
+ var newHeadY = head.y + Math.sin(snakeCurDir) * SNAKE_SPEED;
+ // Check wall collision
+ if (newHeadX < ARENA_MARGIN || newHeadX > GAME_W - ARENA_MARGIN || newHeadY < ARENA_MARGIN || newHeadY > GAME_H - ARENA_MARGIN) {
+ // Game over
+ LK.effects.flashScreen(0xff0000, 800);
+ LK.showGameOver();
+ gameOver = true;
+ return;
+ }
+ // Move body: each segment follows the previous
+ var prevX = head.x,
+ prevY = head.y;
+ var prevDir = snakeCurDir;
+ // Move head
+ head.x = newHeadX;
+ head.y = newHeadY;
+ snakeDirs[0] = snakeCurDir;
+ for (var i = 1; i < snake.length; ++i) {
+ var seg = snake[i];
+ var dx = prevX - seg.x;
+ var dy = prevY - seg.y;
+ var d = Math.sqrt(dx * dx + dy * dy);
+ var desiredDist = 85;
+ if (d > 1) {
+ var moveDist = clamp(d - desiredDist, 0, SNAKE_SPEED * 1.2);
+ if (moveDist > 0) {
+ var moveX = seg.x + dx / d * moveDist;
+ var moveY = seg.y + dy / d * moveDist;
+ seg.x = moveX;
+ seg.y = moveY;
+ }
+ }
+ // Face the direction of movement
+ snakeDirs[i] = Math.atan2(seg.y - prevY, seg.x - prevX);
+ prevX = seg.x;
+ prevY = seg.y;
+ prevDir = snakeDirs[i];
+ }
+ // Check self-collision (head with body, skip first 3 segments for leniency)
+ for (var i = 3; i < snake.length; ++i) {
+ var seg = snake[i];
+ if (dist(head.x, head.y, seg.x, seg.y) < 60) {
+ // Game over
+ LK.effects.flashScreen(0xff0000, 800);
+ LK.showGameOver();
+ gameOver = true;
+ return;
+ }
+ }
+ // Check orb collision
+ for (var i = 0; i < orbs.length; ++i) {
+ var orb = orbs[i];
+ if (dist(head.x, head.y, orb.x, orb.y) < 80) {
+ // Eat orb
+ score += 1;
+ scoreTxt.setText(score);
+ snakePendingGrowth += 2;
+ // Animate orb
+ tween(orb, {
+ scaleX: 1.7,
+ scaleY: 1.7,
+ alpha: 0
+ }, {
+ duration: 180,
+ easing: tween.easeOut,
+ onFinish: function (orb, i) {
+ return function () {
+ orb.scaleX = 1;
+ orb.scaleY = 1;
+ orb.alpha = 1;
+ placeOrb(orb);
+ };
+ }(orb, i)
+ });
+ }
+ }
+ // Grow snake if needed
+ while (snakePendingGrowth > 0) {
+ var last = snake[snake.length - 1];
+ var dir = snakeDirs[snake.length - 1];
+ var seg = new SnakeSegment();
+ seg.isHead = false;
+ seg.removeChildren();
+ seg.attachAsset('snakeBody', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ // Place at last segment's position, trailing behind
+ seg.x = last.x - Math.cos(dir) * 85;
+ seg.y = last.y - Math.sin(dir) * 85;
+ game.addChild(seg);
+ snake.push(seg);
+ snakeDirs.push(dir);
+ snakePendingGrowth--;
+ }
+};
+// On game over, reset state for replay
+LK.on('gameover', function () {
+ resetGame();
+});
+LK.on('youWin', function () {
+ resetGame();
});
\ No newline at end of file