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