User prompt
The two fruits are spawning in the same place.
User prompt
If we click behind while the snake is looking forward, it will go back.
User prompt
Let it go where I stepped, but don't go diagonally.
User prompt
make the game's map grid-like
User prompt
Make a separate accessory for the snake's head and prepare 5 different foods. Each one should give a different score and display the score they gave on the screen.
Code edit (1 edits merged)
Please save this source code
User prompt
Snake Classic
Initial prompt
make a snake game
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Food Class var Food = Container.expand(function () { var self = Container.call(this); self.foodType = FOOD_TYPES[0]; var foodAsset = self.attachAsset('food1', { anchorX: 0.5, anchorY: 0.5 }); self.setType = function (typeObj) { self.foodType = typeObj; // Remove old asset if any if (self._foodAsset) self._foodAsset.destroy(); self._foodAsset = self.attachAsset(typeObj.id, { anchorX: 0.5, anchorY: 0.5 }); }; return self; }); // Food Score Popup Class var FoodScorePopup = Container.expand(function () { var self = Container.call(this); var txt = new Text2("+0", { size: 80, fill: "#fff", stroke: "#000", strokeThickness: 8 }); txt.anchor.set(0.5, 0.5); self.addChild(txt); self.setScore = function (score) { txt.setText("+" + score); }; self.show = function (x, y) { self.x = x; self.y = y; self.alpha = 1; game.addChild(self); tween(self, { y: y - 100, alpha: 0 }, { duration: 700, easing: tween.easeOutCubic, onComplete: function onComplete() { self.destroy(); } }); }; return self; }); // Snake Head Class (with accessory) var SnakeHead = Container.expand(function () { var self = Container.call(this); var segmentAsset = self.attachAsset('snakeSegment', { anchorX: 0.5, anchorY: 0.5 }); // Add accessory (e.g. white ellipse as "hat") var accessory = self.attachAsset('snakeHeadAccessory', { anchorX: 0.5, anchorY: 0.5, y: -20 }); return self; }); // Snake Segment Class var SnakeSegment = Container.expand(function () { var self = Container.call(this); // Each segment is a green box var segmentAsset = self.attachAsset('snakeSegment', { anchorX: 0.5, anchorY: 0.5 }); return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x222222 }); /**** * Game Code ****/ // Food type definitions // Orange // Blueberry // Yellow lemon // Green melon // Red apple // --- Game Constants --- // --- Asset Initialization --- var FOOD_TYPES = [{ id: 'food1', score: 1, color: 0xff3c28 }, { id: 'food2', score: 3, color: 0x3ecf4a }, { id: 'food3', score: 5, color: 0xffe156 }, { id: 'food4', score: 7, color: 0x5c6bc0 }, { id: 'food5', score: 10, color: 0xff8a65 }]; var GRID_SIZE = 80; // Each cell is 80x80 px var GRID_WIDTH = Math.floor(2048 / GRID_SIZE); var GRID_HEIGHT = Math.floor(2732 / GRID_SIZE); var MOVE_INTERVAL = 120; // ms between snake moves // --- Grid Background --- var gridBg = new Container(); for (var gx = 0; gx < GRID_WIDTH; gx++) { for (var gy = 0; gy < GRID_HEIGHT; gy++) { // Draw a faint box for each grid cell var cell = LK.getAsset('snakeSegment', { anchorX: 0.5, anchorY: 0.5, x: gridToPos(gx, gy).x, y: gridToPos(gx, gy).y, width: GRID_SIZE, height: GRID_SIZE, alpha: 0.08, // Very faint color: 0xcccccc // Subtle gray grid lines }); gridBg.addChild(cell); } } game.addChild(gridBg); // --- Game State --- var snake = []; // Array of SnakeSegment var snakeDir = { x: 1, y: 0 }; // Initial direction: right var nextDir = { x: 1, y: 0 }; // Next direction to turn to var food = null; var foodPos = { x: 0, y: 0 }; var moveTimer = null; var isAlive = true; var pendingGrowth = 0; // --- Score UI --- var score = 0; var scoreTxt = new Text2('0', { size: 120, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // --- Helper Functions --- function gridToPos(gx, gy) { // Center the grid in the game area var offsetX = Math.floor((2048 - GRID_WIDTH * GRID_SIZE) / 2); var offsetY = Math.floor((2732 - GRID_HEIGHT * GRID_SIZE) / 2); return { x: offsetX + gx * GRID_SIZE + GRID_SIZE / 2, y: offsetY + gy * GRID_SIZE + GRID_SIZE / 2 }; } function posToGrid(x, y) { var offsetX = Math.floor((2048 - GRID_WIDTH * GRID_SIZE) / 2); var offsetY = Math.floor((2732 - GRID_HEIGHT * GRID_SIZE) / 2); return { x: Math.floor((x - offsetX) / GRID_SIZE), y: Math.floor((y - offsetY) / GRID_SIZE) }; } function spawnFood() { // Remove old food if exists if (food) { food.destroy(); food = null; } // Find empty cell var emptyCells = []; for (var gx = 0; gx < GRID_WIDTH; gx++) { for (var gy = 0; gy < GRID_HEIGHT; gy++) { var occupied = false; for (var i = 0; i < snake.length; i++) { if (snake[i].gx === gx && snake[i].gy === gy) { occupied = true; break; } } // Prevent food from spawning on another food (if multiple foods are present) if (!occupied && food && food.gx === gx && food.gy === gy) { occupied = true; } if (!occupied) { emptyCells.push({ x: gx, y: gy }); } } } if (emptyCells.length === 0) { // No space left, player wins! LK.showYouWin(); return; } var idx = Math.floor(Math.random() * emptyCells.length); foodPos = emptyCells[idx]; food = new Food(); // Randomize food type var foodTypeIdx = Math.floor(Math.random() * FOOD_TYPES.length); var typeObj = FOOD_TYPES[foodTypeIdx]; food.setType(typeObj); var pos = gridToPos(foodPos.x, foodPos.y); food.x = pos.x; food.y = pos.y; food.gx = foodPos.x; food.gy = foodPos.y; food.foodType = typeObj; game.addChild(food); } // --- Snake Initialization --- function resetGame() { // Remove old snake for (var i = 0; i < snake.length; i++) { snake[i].destroy(); } snake = []; // Remove food if (food) { food.destroy(); food = null; } // Ensure grid background is at the back if (gridBg && gridBg.parent) { gridBg.parent.removeChild(gridBg); game.addChildAt(gridBg, 0); } // Reset state isAlive = true; score = 0; scoreTxt.setText(score); snakeDir = { x: 1, y: 0 }; nextDir = { x: 1, y: 0 }; pendingGrowth = 2; // Start with 3 segments // Place snake in center var startX = Math.floor(GRID_WIDTH / 2); var startY = Math.floor(GRID_HEIGHT / 2); for (var i = 0; i < 3; i++) { var seg; if (i === 0) { seg = new SnakeHead(); } else { seg = new SnakeSegment(); } seg.gx = startX - i; seg.gy = startY; var pos = gridToPos(seg.gx, seg.gy); seg.x = pos.x; seg.y = pos.y; game.addChild(seg); snake.push(seg); } spawnFood(); // Start move timer if (moveTimer) LK.clearInterval(moveTimer); moveTimer = LK.setInterval(moveSnake, MOVE_INTERVAL); } // --- Snake Movement --- function moveSnake() { if (!isAlive) return; // Update direction if ((nextDir.x !== -snakeDir.x || nextDir.y !== -snakeDir.y // Prevent 180 turns ) && (nextDir.x !== snakeDir.x || nextDir.y !== snakeDir.y)) { snakeDir.x = nextDir.x; snakeDir.y = nextDir.y; } // Calculate new head position var head = snake[0]; var newGX = head.gx + snakeDir.x; var newGY = head.gy + snakeDir.y; // Check wall collision if (newGX < 0 || newGX >= GRID_WIDTH || newGY < 0 || newGY >= GRID_HEIGHT) { gameOver(); return; } // Check self collision for (var i = 0; i < snake.length; i++) { if (snake[i].gx === newGX && snake[i].gy === newGY) { gameOver(); return; } } // Move segments var prevGX = head.gx; var prevGY = head.gy; var prevPos = gridToPos(prevGX, prevGY); // Move head head.gx = newGX; head.gy = newGY; var newPos = gridToPos(newGX, newGY); tween(head, { x: newPos.x, y: newPos.y }, { duration: MOVE_INTERVAL - 10, easing: tween.linear }); // Move body for (var i = 1; i < snake.length; i++) { var seg = snake[i]; var tmpGX = seg.gx; var tmpGY = seg.gy; var tmpPos = gridToPos(tmpGX, tmpGY); seg.gx = prevGX; seg.gy = prevGY; tween(seg, { x: prevPos.x, y: prevPos.y }, { duration: MOVE_INTERVAL - 10, easing: tween.linear }); prevGX = tmpGX; prevGY = tmpGY; prevPos = tmpPos; } // Growth if (pendingGrowth > 0) { var tail = snake[snake.length - 1]; var newSeg = new SnakeSegment(); newSeg.gx = prevGX; newSeg.gy = prevGY; var tailPos = gridToPos(prevGX, prevGY); newSeg.x = tailPos.x; newSeg.y = tailPos.y; game.addChild(newSeg); snake.push(newSeg); pendingGrowth--; } // Check food collision if (head.gx === food.gx && head.gy === food.gy) { var foodScore = food.foodType && food.foodType.score ? food.foodType.score : 1; score += foodScore; scoreTxt.setText(score); pendingGrowth++; // Show score popup var popup = new FoodScorePopup(); popup.setScore(foodScore); popup.show(food.x, food.y); spawnFood(); } } // --- Game Over --- function gameOver() { isAlive = false; LK.effects.flashScreen(0xff0000, 800); if (moveTimer) LK.clearInterval(moveTimer); LK.setTimeout(function () { LK.showGameOver(); }, 800); } // --- Input Handling --- // Touch/drag/swipe: change direction var dragStart = null; var dragLast = null; var dragThreshold = 40; // px function handleDirectionInput(x, y) { // Convert to grid var head = snake[0]; var pos = gridToPos(head.gx, head.gy); var dx = x - pos.x; var dy = y - pos.y; if (Math.abs(dx) > Math.abs(dy)) { // Horizontal if (dx > dragThreshold && snakeDir.x !== -1) { nextDir = { x: 1, y: 0 }; } else if (dx < -dragThreshold && snakeDir.x !== 1) { nextDir = { x: -1, y: 0 }; } } else { // Vertical if (dy > dragThreshold && snakeDir.y !== -1) { nextDir = { x: 0, y: 1 }; } else if (dy < -dragThreshold && snakeDir.y !== 1) { nextDir = { x: 0, y: -1 }; } } } // Touch down: start drag game.down = function (x, y, obj) { dragStart = { x: x, y: y }; dragLast = { x: x, y: y }; }; // Touch move: detect swipe game.move = function (x, y, obj) { if (!dragStart) return; var dx = x - dragStart.x; var dy = y - dragStart.y; if (Math.abs(dx) > dragThreshold || Math.abs(dy) > dragThreshold) { handleDirectionInput(x, y); dragStart = { x: x, y: y }; // Reset for next swipe } dragLast = { x: x, y: y }; }; // Touch up: tap to turn (if no swipe) game.up = function (x, y, obj) { if (!dragStart) return; var dx = x - dragStart.x; var dy = y - dragStart.y; if (Math.abs(dx) < dragThreshold && Math.abs(dy) < dragThreshold) { // Tap: move toward tapped grid cell, only in cardinal direction, not diagonally var head = snake[0]; var headGrid = { x: head.gx, y: head.gy }; var tapGrid = posToGrid(x, y); // If already at tap cell, do nothing if (headGrid.x !== tapGrid.x || headGrid.y !== tapGrid.y) { // Determine direction to move: only one axis at a time var diffX = tapGrid.x - headGrid.x; var diffY = tapGrid.y - headGrid.y; if (Math.abs(diffX) > Math.abs(diffY)) { // Move horizontally if (diffX > 0) { nextDir = { x: 1, y: 0 }; } else if (diffX < 0) { nextDir = { x: -1, y: 0 }; } } else if (Math.abs(diffY) > 0) { // Move vertically if (diffY > 0) { nextDir = { x: 0, y: 1 }; } else if (diffY < 0) { nextDir = { x: 0, y: -1 }; } } } } dragStart = null; dragLast = null; }; // --- Game Update (not used for movement, but could be used for future features) --- game.update = function () { // No per-frame logic needed for MVP }; // --- Start Game --- resetGame();
===================================================================
--- original.js
+++ change.js
@@ -206,8 +206,12 @@
occupied = true;
break;
}
}
+ // Prevent food from spawning on another food (if multiple foods are present)
+ if (!occupied && food && food.gx === gx && food.gy === gy) {
+ occupied = true;
+ }
if (!occupied) {
emptyCells.push({
x: gx,
y: gy
ultra realistic apple. In-Game asset. 2d. High contrast. No shadows
create ultra realistic pear. In-Game asset. 2d. High contrast. No shadows
ultra realistic strawberry. In-Game asset. 2d. High contrast. No shadows
ultra realistic orange fruit. In-Game asset. 2d. High contrast. No shadows
snake head 16x16. In-Game asset. 2d. High contrast. No shadows