User prompt
Please fix the bug: 'TypeError: undefined is not an object (evaluating 'head.lastDir')' in or related to this line: 'if (head.lastDir === undefined) {' Line Number: 449
User prompt
If you make a sharp turn, it kills you
User prompt
Make the head spawn normally from the body
User prompt
Now it want move and it keeps killing me because the obstacles pawn on the snake
User prompt
Make the head spawn on the body
User prompt
Remove speed poweup
User prompt
Make the head spawn away from the body
User prompt
More content
User prompt
Move the head to be away from the body (not close)
User prompt
Mive the head foward
User prompt
Remove the blue food
User prompt
Make the head further away from the body
User prompt
Make the head further away from the body
User prompt
Don’t make the obstacles move
Code edit (1 edits merged)
Please save this source code
User prompt
Snake Evolution
Initial prompt
Snake game (with new content)
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Food var Food = Container.expand(function () { var self = Container.call(this); self.asset = self.attachAsset('food', { anchorX: 0.5, anchorY: 0.5 }); return self; }); // FoodPurple var FoodPurple = Container.expand(function () { var self = Container.call(this); self.asset = self.attachAsset('foodPurple', { anchorX: 0.5, anchorY: 0.5 }); return self; }); // MovingObstacle var MovingObstacle = Container.expand(function () { var self = Container.call(this); self.asset = self.attachAsset('obstacleMoving', { anchorX: 0.5, anchorY: 0.5 }); // Pick a random direction and speed var angle = Math.random() * Math.PI * 2; var speed = 4 + Math.random() * 4; self.vx = Math.cos(angle) * speed; self.vy = Math.sin(angle) * speed; self.update = function () { // Save last position for event triggers if (self.lastX === undefined) self.lastX = self.x; if (self.lastY === undefined) self.lastY = self.y; self.x += self.vx; self.y += self.vy; // Bounce off walls (100px margin) if (self.x < 100 + self.asset.width / 2 && self.vx < 0) self.vx *= -1; if (self.x > 2048 - 100 - self.asset.width / 2 && self.vx > 0) self.vx *= -1; if (self.y < 100 + self.asset.height / 2 && self.vy < 0) self.vy *= -1; if (self.y > 2732 - 100 - self.asset.height / 2 && self.vy > 0) self.vy *= -1; self.lastX = self.x; self.lastY = self.y; }; return self; }); // Obstacle (static) var Obstacle = Container.expand(function () { var self = Container.call(this); self.asset = self.attachAsset('obstacle', { anchorX: 0.5, anchorY: 0.5 }); // Obstacles are static: no velocity, no update needed return self; }); // PowerUp var PowerUp = Container.expand(function () { var self = Container.call(this); self.type = 'speed'; // or 'invincible' self.asset = null; self.setType = function (type) { if (self.asset) self.removeChild(self.asset); self.type = type; if (type === 'speed') { self.asset = self.attachAsset('powerupSpeed', { anchorX: 0.5, anchorY: 0.5 }); } else if (type === 'invincible') { self.asset = self.attachAsset('powerupInvincible', { anchorX: 0.5, anchorY: 0.5 }); } else if (type === 'shrink') { self.asset = self.attachAsset('powerupShrink', { anchorX: 0.5, anchorY: 0.5 }); } }; self.setType('speed'); return self; }); // Snake Segment (body or head) var SnakeSegment = Container.expand(function () { var self = Container.call(this); self.isHead = false; self.asset = null; self.setType = function (type) { if (self.asset) self.removeChild(self.asset); if (type === 'head') { self.asset = self.attachAsset('snakeHead', { anchorX: 0.5, anchorY: 0.5 }); self.isHead = true; } else { self.asset = self.attachAsset('snakeBody', { anchorX: 0.5, anchorY: 0.5 }); self.isHead = false; } }; self.setType('body'); return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x181818 }); /**** * Game Code ****/ // Music // Sounds // Obstacles // Power-ups // Food // Snake head and body // --- Game constants --- var SNAKE_INIT_LENGTH = 5; var SNAKE_INIT_SPEED = 16; // pixels per tick var SNAKE_MIN_SPEED = 8; var SNAKE_MAX_SPEED = 40; var SNAKE_TURN_ANGLE = Math.PI / 18; // 10 degrees per input var FOOD_SCORE = 10; var POWERUP_SCORE = 25; var POWERUP_DURATION = 300; // ticks (5 seconds) var OBSTACLE_COUNT = 3; var OBSTACLE_SPEED = 6; // --- Game state --- var snake = []; var snakeDir = 0; // in radians, 0 = right var snakeNextDir = 0; var snakeSpeed = SNAKE_INIT_SPEED; var snakeGrow = 0; var food = null; var powerup = null; var powerupActive = false; var powerupType = null; var powerupTimer = 0; var obstacles = []; var score = 0; var isInvincible = false; var lastTouch = null; var dragStart = null; var dragAngle = null; var dragActive = false; var gameOver = false; // --- GUI --- var scoreTxt = new Text2('0', { size: 120, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // --- Helper functions --- function randomPos(margin) { margin = margin || 150; var x = margin + Math.random() * (2048 - 2 * margin); var y = margin + Math.random() * (2732 - 2 * margin); return { x: x, y: y }; } function dist2(a, b) { var dx = a.x - b.x, dy = a.y - b.y; return dx * dx + dy * dy; } function angleBetween(a, b) { return Math.atan2(b.y - a.y, b.x - a.x); } function wrapPos(pos) { // Not used, but could be for wrap-around if (pos.x < 0) pos.x += 2048; if (pos.x > 2048) pos.x -= 2048; if (pos.y < 0) pos.y += 2732; if (pos.y > 2732) pos.y -= 2732; } function spawnFood() { if (food) food.destroy(); // 20% chance for purple food if (Math.random() < 0.2) { food = new FoodPurple(); food.isPurple = true; } else { food = new Food(); food.isPurple = false; } var pos = randomPos(200); food.x = pos.x; food.y = pos.y; game.addChild(food); } function spawnPowerUp() { if (powerup) powerup.destroy(); powerup = new PowerUp(); var pos = randomPos(200); // 40% speed, 40% invincible, 20% shrink var r = Math.random(); var t; if (r < 0.4) t = 'speed';else if (r < 0.8) t = 'invincible';else t = 'shrink'; powerup.setType(t); powerup.x = pos.x; powerup.y = pos.y; game.addChild(powerup); } function spawnObstacles() { for (var i = 0; i < obstacles.length; ++i) { obstacles[i].destroy(); } obstacles = []; for (var i = 0; i < OBSTACLE_COUNT; ++i) { var obs = new Obstacle(); var pos = randomPos(300); obs.x = pos.x; obs.y = pos.y; // Obstacles are static, no velocity needed obstacles.push(obs); game.addChild(obs); } } function resetSnake() { for (var i = 0; i < snake.length; ++i) { snake[i].destroy(); } snake = []; var startX = 2048 / 2, startY = 2732 / 2; for (var i = 0; i < SNAKE_INIT_LENGTH; ++i) { var seg = new SnakeSegment(); if (i === 0) seg.setType('head');else seg.setType('body'); // Make the head much further away from the first body segment if (i === 0) { seg.x = startX + snakeSpeed * 6; // Move head much further forward seg.y = startY; } else if (i === 1) { seg.x = startX; // Place first body segment at center seg.y = startY; } else { seg.x = startX - (i - 1) * snakeSpeed * 1.2; // Maintain spacing for the rest seg.y = startY; } game.addChild(seg); snake.push(seg); } snakeDir = 0; snakeNextDir = 0; snakeSpeed = SNAKE_INIT_SPEED; snakeGrow = 0; isInvincible = false; powerupActive = false; powerupType = null; powerupTimer = 0; } function updateScore(val) { score = val; scoreTxt.setText(score); LK.setScore(score); } function activatePowerUp(type) { powerupActive = true; powerupType = type; powerupTimer = POWERUP_DURATION; if (type === 'speed') { snakeSpeed = Math.min(SNAKE_MAX_SPEED, SNAKE_INIT_SPEED * 2); } else if (type === 'invincible') { isInvincible = true; // Flash snake for (var i = 0; i < snake.length; ++i) { LK.effects.flashObject(snake[i], 0xf1c40f, 500); } } else if (type === 'shrink') { // Remove 3 segments from the tail, but always keep at least 5 var minLen = 5; var removeCount = Math.min(3, snake.length - minLen); for (var i = 0; i < removeCount; ++i) { var seg = snake.pop(); seg.destroy(); } } } function deactivatePowerUp() { powerupActive = false; powerupType = null; powerupTimer = 0; snakeSpeed = SNAKE_INIT_SPEED; isInvincible = false; } function endGame() { if (gameOver) return; gameOver = true; LK.effects.flashScreen(0xff0000, 1000); LK.getSound('hit').play(); LK.showGameOver(); } // --- Input handling (touch/drag to steer) --- game.down = function (x, y, obj) { // Only allow drag from outside top left 100x100 if (x < 100 && y < 100) return; dragStart = { x: x, y: y }; dragAngle = null; dragActive = true; }; game.move = function (x, y, obj) { if (!dragActive) return; if (!dragStart) return; // Calculate angle from snake head to drag point var head = snake[0]; var dx = x - head.x; var dy = y - head.y; if (dx * dx + dy * dy < 100) return; // Ignore tiny drags var angle = Math.atan2(dy, dx); snakeNextDir = angle; dragAngle = angle; }; game.up = function (x, y, obj) { dragActive = false; dragStart = null; dragAngle = null; }; // --- Main game update loop --- game.update = function () { if (gameOver) return; // Power-up timer if (powerupActive) { powerupTimer--; if (powerupTimer <= 0) { deactivatePowerUp(); } } // Update moving obstacles for (var i = 0; i < obstacles.length; ++i) { if (obstacles[i].update) obstacles[i].update(); } // Obstacles are static, no update needed // Snake direction: smooth turn toward nextDir var d = snakeNextDir - snakeDir; while (d > Math.PI) d -= 2 * Math.PI; while (d < -Math.PI) d += 2 * Math.PI; if (Math.abs(d) > 0.01) { var turn = Math.sign(d) * Math.min(Math.abs(d), SNAKE_TURN_ANGLE); snakeDir += turn; } // Move snake head var head = snake[0]; var prevPos = { x: head.x, y: head.y }; head.x += Math.cos(snakeDir) * snakeSpeed; head.y += Math.sin(snakeDir) * snakeSpeed; // Clamp to bounds (leave 100px margin for UI) head.x = Math.max(100 + head.asset.width / 2, Math.min(2048 - 100 - head.asset.width / 2, head.x)); head.y = Math.max(100 + head.asset.height / 2, Math.min(2732 - 100 - head.asset.height / 2, head.y)); // Move body segments to follow for (var i = 1; i < snake.length; ++i) { var seg = snake[i]; var target = snake[i - 1]; var dx = target.x - seg.x; var dy = target.y - seg.y; var dist = Math.sqrt(dx * dx + dy * dy); var desired = snakeSpeed * 0.9; if (dist > desired) { var move = (dist - desired) * 0.5; seg.x += dx / dist * move; seg.y += dy / dist * move; } } // Grow snake if needed if (snakeGrow > 0) { var tail = snake[snake.length - 1]; var newSeg = new SnakeSegment(); newSeg.setType('body'); newSeg.x = tail.x; newSeg.y = tail.y; game.addChild(newSeg); snake.push(newSeg); snakeGrow--; } // Check collision with food if (food && head.intersects(food)) { LK.getSound('eat').play(); if (food.isPurple) { updateScore(score + FOOD_SCORE * 3); snakeGrow += 6; } else { updateScore(score + FOOD_SCORE); snakeGrow += 2; } food.destroy(); food = null; spawnFood(); // Chance to spawn powerup if (!powerup && Math.random() < 0.2) { spawnPowerUp(); } } // Check collision with powerup if (powerup && head.intersects(powerup)) { LK.getSound('powerup').play(); updateScore(score + POWERUP_SCORE); activatePowerUp(powerup.type); powerup.destroy(); powerup = null; } // Check collision with obstacles for (var i = 0; i < obstacles.length; ++i) { if (head.intersects(obstacles[i])) { if (!isInvincible) { endGame(); return; } else { // Flash obstacle and destroy it LK.effects.flashObject(obstacles[i], 0xffffff, 300); obstacles[i].destroy(); obstacles.splice(i, 1); i--; } } } // Check collision with self (skip first 4 segments for leniency) if (!isInvincible) { for (var i = 4; i < snake.length; ++i) { if (head.intersects(snake[i])) { endGame(); return; } } } // Evolving obstacles: every 30 seconds, add a new one (max 8) if (LK.ticks % (60 * 30) === 0 && obstacles.length < 8) { var obs; // 50% chance for moving obstacle if (Math.random() < 0.5) { obs = new MovingObstacle(); } else { obs = new Obstacle(); } var pos = randomPos(300); obs.x = pos.x; obs.y = pos.y; obstacles.push(obs); game.addChild(obs); } }; // --- Game start/reset --- function startGame() { gameOver = false; updateScore(0); resetSnake(); spawnFood(); if (powerup) { powerup.destroy(); powerup = null; } spawnObstacles(); deactivatePowerUp(); LK.playMusic('snakebg', { fade: { start: 0, end: 1, duration: 1000 } }); } startGame(); // --- Game over/win handling is automatic by LK --- // --- Music is started on game start ---
===================================================================
--- original.js
+++ change.js
@@ -14,8 +14,45 @@
anchorY: 0.5
});
return self;
});
+// FoodPurple
+var FoodPurple = Container.expand(function () {
+ var self = Container.call(this);
+ self.asset = self.attachAsset('foodPurple', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ return self;
+});
+// MovingObstacle
+var MovingObstacle = Container.expand(function () {
+ var self = Container.call(this);
+ self.asset = self.attachAsset('obstacleMoving', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ // Pick a random direction and speed
+ var angle = Math.random() * Math.PI * 2;
+ var speed = 4 + Math.random() * 4;
+ self.vx = Math.cos(angle) * speed;
+ self.vy = Math.sin(angle) * speed;
+ self.update = function () {
+ // Save last position for event triggers
+ if (self.lastX === undefined) self.lastX = self.x;
+ if (self.lastY === undefined) self.lastY = self.y;
+ self.x += self.vx;
+ self.y += self.vy;
+ // Bounce off walls (100px margin)
+ if (self.x < 100 + self.asset.width / 2 && self.vx < 0) self.vx *= -1;
+ if (self.x > 2048 - 100 - self.asset.width / 2 && self.vx > 0) self.vx *= -1;
+ if (self.y < 100 + self.asset.height / 2 && self.vy < 0) self.vy *= -1;
+ if (self.y > 2732 - 100 - self.asset.height / 2 && self.vy > 0) self.vy *= -1;
+ self.lastX = self.x;
+ self.lastY = self.y;
+ };
+ return self;
+});
// Obstacle (static)
var Obstacle = Container.expand(function () {
var self = Container.call(this);
self.asset = self.attachAsset('obstacle', {
@@ -42,8 +79,13 @@
self.asset = self.attachAsset('powerupInvincible', {
anchorX: 0.5,
anchorY: 0.5
});
+ } else if (type === 'shrink') {
+ self.asset = self.attachAsset('powerupShrink', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
}
};
self.setType('speed');
return self;
@@ -152,9 +194,16 @@
if (pos.y > 2732) pos.y -= 2732;
}
function spawnFood() {
if (food) food.destroy();
- food = new Food();
+ // 20% chance for purple food
+ if (Math.random() < 0.2) {
+ food = new FoodPurple();
+ food.isPurple = true;
+ } else {
+ food = new Food();
+ food.isPurple = false;
+ }
var pos = randomPos(200);
food.x = pos.x;
food.y = pos.y;
game.addChild(food);
@@ -162,9 +211,12 @@
function spawnPowerUp() {
if (powerup) powerup.destroy();
powerup = new PowerUp();
var pos = randomPos(200);
- var t = Math.random() < 0.5 ? 'speed' : 'invincible';
+ // 40% speed, 40% invincible, 20% shrink
+ var r = Math.random();
+ var t;
+ if (r < 0.4) t = 'speed';else if (r < 0.8) t = 'invincible';else t = 'shrink';
powerup.setType(t);
powerup.x = pos.x;
powerup.y = pos.y;
game.addChild(powerup);
@@ -233,8 +285,16 @@
// Flash snake
for (var i = 0; i < snake.length; ++i) {
LK.effects.flashObject(snake[i], 0xf1c40f, 500);
}
+ } else if (type === 'shrink') {
+ // Remove 3 segments from the tail, but always keep at least 5
+ var minLen = 5;
+ var removeCount = Math.min(3, snake.length - minLen);
+ for (var i = 0; i < removeCount; ++i) {
+ var seg = snake.pop();
+ seg.destroy();
+ }
}
}
function deactivatePowerUp() {
powerupActive = false;
@@ -287,8 +347,12 @@
if (powerupTimer <= 0) {
deactivatePowerUp();
}
}
+ // Update moving obstacles
+ for (var i = 0; i < obstacles.length; ++i) {
+ if (obstacles[i].update) obstacles[i].update();
+ }
// Obstacles are static, no update needed
// Snake direction: smooth turn toward nextDir
var d = snakeNextDir - snakeDir;
while (d > Math.PI) d -= 2 * Math.PI;
@@ -335,10 +399,15 @@
}
// Check collision with food
if (food && head.intersects(food)) {
LK.getSound('eat').play();
- updateScore(score + FOOD_SCORE);
- snakeGrow += 2;
+ if (food.isPurple) {
+ updateScore(score + FOOD_SCORE * 3);
+ snakeGrow += 6;
+ } else {
+ updateScore(score + FOOD_SCORE);
+ snakeGrow += 2;
+ }
food.destroy();
food = null;
spawnFood();
// Chance to spawn powerup
@@ -379,13 +448,18 @@
}
}
// Evolving obstacles: every 30 seconds, add a new one (max 8)
if (LK.ticks % (60 * 30) === 0 && obstacles.length < 8) {
- var obs = new Obstacle();
+ var obs;
+ // 50% chance for moving obstacle
+ if (Math.random() < 0.5) {
+ obs = new MovingObstacle();
+ } else {
+ obs = new Obstacle();
+ }
var pos = randomPos(300);
obs.x = pos.x;
obs.y = pos.y;
- // Obstacles are static, no velocity needed
obstacles.push(obs);
game.addChild(obs);
}
};