User prompt
Please fix the bug: 'Cannot read properties of undefined (reading 'length')' in or related to this line: 'var animalSprite = self.attachAsset(self.assetId, {' Line Number: 73
Code edit (1 edits merged)
Please save this source code
User prompt
Animal Escape: Snake Tummy Struggle
Initial prompt
(Pig) (Cat) (Bee) (Frog) (Polar Bear) (Elephant) (Bird) (Snail) (Axolotl) (Pink Fox) (Boy Bat) (Wolf) (Chinchilla) (Moth) (Goat) (Bunny) (Bear) And (Deer) Is Struggle In The Snake Tummy With Outside Of Snake Tummy
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Acid Bubble (obstacle)
var AcidBubble = Container.expand(function () {
var self = Container.call(this);
var bubble = self.attachAsset('acidBubble', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = bubble.width / 2;
self.vx = (Math.random() - 0.5) * 6;
self.vy = (Math.random() - 0.5) * 6;
self.update = function () {
self.x += self.vx;
self.y += self.vy;
// Bounce off belly walls
if (self.x < snakeBelly.x - snakeBelly.width / 2 + self.radius) {
self.x = snakeBelly.x - snakeBelly.width / 2 + self.radius;
self.vx *= -1;
}
if (self.x > snakeBelly.x + snakeBelly.width / 2 - self.radius) {
self.x = snakeBelly.x + snakeBelly.width / 2 - self.radius;
self.vx *= -1;
}
if (self.y < snakeBelly.y - snakeBelly.height / 2 + self.radius) {
self.y = snakeBelly.y - snakeBelly.height / 2 + self.radius;
self.vy *= -1;
}
if (self.y > snakeBelly.y + snakeBelly.height / 2 - self.radius) {
self.y = snakeBelly.y + snakeBelly.height / 2 - self.radius;
self.vy *= -1;
}
};
return self;
});
// Animal class
var Animal = Container.expand(function () {
var self = Container.call(this);
// Asset id is passed in as self.assetId
var animalSprite = self.attachAsset(self.assetId, {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = animalSprite.width / 2;
// Used for movement
self.vx = 0;
self.vy = 0;
self.isSelected = false;
// Called every tick
self.update = function () {
// Gravity inside snake
self.vy += 0.5;
self.x += self.vx;
self.y += self.vy;
// Friction
self.vx *= 0.95;
self.vy *= 0.95;
// Keep inside belly bounds
if (self.x < snakeBelly.x - snakeBelly.width / 2 + self.radius) {
self.x = snakeBelly.x - snakeBelly.width / 2 + self.radius;
self.vx *= -0.5;
}
if (self.x > snakeBelly.x + snakeBelly.width / 2 - self.radius) {
self.x = snakeBelly.x + snakeBelly.width / 2 - self.radius;
self.vx *= -0.5;
}
if (self.y < snakeBelly.y - snakeBelly.height / 2 + self.radius) {
self.y = snakeBelly.y - snakeBelly.height / 2 + self.radius;
self.vy *= -0.5;
}
if (self.y > snakeBelly.y + snakeBelly.height / 2 - self.radius) {
self.y = snakeBelly.y + snakeBelly.height / 2 - self.radius;
self.vy *= -0.5;
}
};
// Touch down on animal
self.down = function (x, y, obj) {
self.isSelected = true;
selectedAnimal = self;
dragOffsetX = self.x - x;
dragOffsetY = self.y - y;
};
// Touch up
self.up = function (x, y, obj) {
self.isSelected = false;
selectedAnimal = null;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// Sound for animal jump
// Exit (esophagus opening)
// Obstacle (stomach acid bubble)
// Snake exterior (for outside interaction)
// Snake belly (background)
// Animal shapes (simple colored ellipses for each animal)
// --- Global variables ---
var animalIds = ['pig', 'cat', 'bee', 'frog', 'polarBear', 'elephant', 'bird', 'snail', 'axolotl', 'pinkFox', 'boyBat', 'wolf', 'chinchilla', 'moth', 'goat', 'bunny', 'bear', 'deer'];
var animals = [];
var acidBubbles = [];
var selectedAnimal = null;
var dragOffsetX = 0;
var dragOffsetY = 0;
var insideMode = true; // true: inside snake, false: outside
var snakeBelly, snakeSkin, exitNode;
var scoreTxt;
var rescuedCount = 0;
var totalToRescue = 5; // MVP: rescue 5 animals to win
// --- Setup snake belly (inside) ---
snakeBelly = LK.getAsset('snakeBelly', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 1200
});
game.addChild(snakeBelly);
// --- Setup exit (esophagus opening) ---
exitNode = LK.getAsset('exit', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 800
});
game.addChild(exitNode);
// --- Setup animals ---
function spawnAnimals() {
animals = [];
var startX = 2048 / 2 - 400;
var startY = 1200;
for (var i = 0; i < totalToRescue; i++) {
var id = animalIds[i];
var animal = new Animal();
animal.assetId = id;
animal.x = startX + i * 200;
animal.y = startY + Math.random() * 100 - 50;
animal.vx = (Math.random() - 0.5) * 6;
animal.vy = (Math.random() - 0.5) * 6;
animals.push(animal);
game.addChild(animal);
}
}
spawnAnimals();
// --- Setup acid bubbles (obstacles) ---
function spawnAcidBubbles() {
acidBubbles = [];
for (var i = 0; i < 3; i++) {
var bubble = new AcidBubble();
bubble.x = 2048 / 2 + (Math.random() - 0.5) * 600;
bubble.y = 1200 + (Math.random() - 0.5) * 300;
acidBubbles.push(bubble);
game.addChild(bubble);
}
}
spawnAcidBubbles();
// --- Setup score text ---
scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// --- Setup snake skin (outside) ---
snakeSkin = LK.getAsset('snakeSkin', {
anchorX: 0.5,
anchorY: 0,
x: 2048 / 2,
y: 0
});
// Not added yet; only shown in outside mode
// --- Helper: reset game state ---
function resetGame() {
// Remove all animals and bubbles
for (var i = 0; i < animals.length; i++) animals[i].destroy();
for (var i = 0; i < acidBubbles.length; i++) acidBubbles[i].destroy();
animals = [];
acidBubbles = [];
rescuedCount = 0;
scoreTxt.setText('0');
insideMode = true;
if (snakeSkin.parent) snakeSkin.parent.removeChild(snakeSkin);
if (!snakeBelly.parent) game.addChild(snakeBelly);
if (!exitNode.parent) game.addChild(exitNode);
spawnAnimals();
spawnAcidBubbles();
}
// --- Touch/drag logic ---
game.move = function (x, y, obj) {
if (!insideMode) return;
if (selectedAnimal) {
// Move animal with finger
selectedAnimal.x = x + dragOffsetX;
selectedAnimal.y = y + dragOffsetY;
}
};
game.down = function (x, y, obj) {
if (!insideMode) return;
// Check if any animal is touched
for (var i = 0; i < animals.length; i++) {
var a = animals[i];
var dx = x - a.x;
var dy = y - a.y;
if (dx * dx + dy * dy < a.radius * a.radius) {
a.down(x, y, obj);
return;
}
}
};
game.up = function (x, y, obj) {
if (!insideMode) return;
if (selectedAnimal) {
// Give a jump impulse upwards
selectedAnimal.vy = -18;
selectedAnimal.vx = (Math.random() - 0.5) * 8;
LK.getSound('jump').play();
selectedAnimal.up(x, y, obj);
}
};
// --- Outside mode: tap snake skin to help animals escape ---
snakeSkin.down = function (x, y, obj) {
if (!insideMode) {
// "Massage" the snake: all animals get a little upward boost
for (var i = 0; i < animals.length; i++) {
animals[i].vy -= 10 + Math.random() * 6;
}
// Animate snake skin
tween(snakeSkin, {
y: 20
}, {
duration: 120,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(snakeSkin, {
y: 0
}, {
duration: 180,
easing: tween.easeIn
});
}
});
}
};
// --- Game update loop ---
game.update = function () {
// Switch between inside/outside every 10 seconds
if (LK.ticks % 600 === 0) {
insideMode = !insideMode;
if (insideMode) {
// Show inside
if (!snakeBelly.parent) game.addChild(snakeBelly);
if (!exitNode.parent) game.addChild(exitNode);
if (snakeSkin.parent) snakeSkin.parent.removeChild(snakeSkin);
} else {
// Show outside
if (!snakeSkin.parent) game.addChild(snakeSkin);
if (snakeBelly.parent) snakeBelly.parent.removeChild(snakeBelly);
if (exitNode.parent) exitNode.parent.removeChild(exitNode);
}
}
// Update animals
for (var i = animals.length - 1; i >= 0; i--) {
var a = animals[i];
a.update();
// If animal touches exit, rescue it!
if (insideMode && a.intersects(exitNode)) {
// Animate out
tween(a, {
y: exitNode.y - 200
}, {
duration: 400,
easing: tween.easeIn,
onFinish: function onFinish() {
a.destroy();
}
});
animals.splice(i, 1);
rescuedCount++;
scoreTxt.setText(rescuedCount);
// Win condition
if (rescuedCount >= totalToRescue) {
LK.showYouWin();
return;
}
}
}
// Update acid bubbles
for (var i = acidBubbles.length - 1; i >= 0; i--) {
var b = acidBubbles[i];
b.update();
// If any animal hits a bubble, game over
for (var j = 0; j < animals.length; j++) {
var a = animals[j];
var dx = a.x - b.x;
var dy = a.y - b.y;
var dist = a.radius + b.radius - 10;
if (dx * dx + dy * dy < dist * dist) {
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
return;
}
}
}
};
// --- Reset game on game over or win ---
LK.on('gameover', function () {
resetGame();
});
LK.on('youwin', function () {
resetGame();
});
// --- Instructions text ---
var instructions = new Text2("Drag animals to help them jump to the exit!\nAvoid acid bubbles.\nSometimes tap the snake's back to help!", {
size: 60,
fill: "#fff"
});
instructions.anchor.set(0.5, 0);
LK.gui.top.addChild(instructions);
instructions.y = 180;
instructions.x = 2048 / 2; ===================================================================
--- original.js
+++ change.js
@@ -1,6 +1,334 @@
-/****
+/****
+* Plugins
+****/
+var tween = LK.import("@upit/tween.v1");
+
+/****
+* Classes
+****/
+// Acid Bubble (obstacle)
+var AcidBubble = Container.expand(function () {
+ var self = Container.call(this);
+ var bubble = self.attachAsset('acidBubble', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.radius = bubble.width / 2;
+ self.vx = (Math.random() - 0.5) * 6;
+ self.vy = (Math.random() - 0.5) * 6;
+ self.update = function () {
+ self.x += self.vx;
+ self.y += self.vy;
+ // Bounce off belly walls
+ if (self.x < snakeBelly.x - snakeBelly.width / 2 + self.radius) {
+ self.x = snakeBelly.x - snakeBelly.width / 2 + self.radius;
+ self.vx *= -1;
+ }
+ if (self.x > snakeBelly.x + snakeBelly.width / 2 - self.radius) {
+ self.x = snakeBelly.x + snakeBelly.width / 2 - self.radius;
+ self.vx *= -1;
+ }
+ if (self.y < snakeBelly.y - snakeBelly.height / 2 + self.radius) {
+ self.y = snakeBelly.y - snakeBelly.height / 2 + self.radius;
+ self.vy *= -1;
+ }
+ if (self.y > snakeBelly.y + snakeBelly.height / 2 - self.radius) {
+ self.y = snakeBelly.y + snakeBelly.height / 2 - self.radius;
+ self.vy *= -1;
+ }
+ };
+ return self;
+});
+// Animal class
+var Animal = Container.expand(function () {
+ var self = Container.call(this);
+ // Asset id is passed in as self.assetId
+ var animalSprite = self.attachAsset(self.assetId, {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.radius = animalSprite.width / 2;
+ // Used for movement
+ self.vx = 0;
+ self.vy = 0;
+ self.isSelected = false;
+ // Called every tick
+ self.update = function () {
+ // Gravity inside snake
+ self.vy += 0.5;
+ self.x += self.vx;
+ self.y += self.vy;
+ // Friction
+ self.vx *= 0.95;
+ self.vy *= 0.95;
+ // Keep inside belly bounds
+ if (self.x < snakeBelly.x - snakeBelly.width / 2 + self.radius) {
+ self.x = snakeBelly.x - snakeBelly.width / 2 + self.radius;
+ self.vx *= -0.5;
+ }
+ if (self.x > snakeBelly.x + snakeBelly.width / 2 - self.radius) {
+ self.x = snakeBelly.x + snakeBelly.width / 2 - self.radius;
+ self.vx *= -0.5;
+ }
+ if (self.y < snakeBelly.y - snakeBelly.height / 2 + self.radius) {
+ self.y = snakeBelly.y - snakeBelly.height / 2 + self.radius;
+ self.vy *= -0.5;
+ }
+ if (self.y > snakeBelly.y + snakeBelly.height / 2 - self.radius) {
+ self.y = snakeBelly.y + snakeBelly.height / 2 - self.radius;
+ self.vy *= -0.5;
+ }
+ };
+ // Touch down on animal
+ self.down = function (x, y, obj) {
+ self.isSelected = true;
+ selectedAnimal = self;
+ dragOffsetX = self.x - x;
+ dragOffsetY = self.y - y;
+ };
+ // Touch up
+ self.up = function (x, y, obj) {
+ self.isSelected = false;
+ selectedAnimal = null;
+ };
+ return self;
+});
+
+/****
* Initialize Game
-****/
+****/
var game = new LK.Game({
- backgroundColor: 0x000000
-});
\ No newline at end of file
+ backgroundColor: 0x222222
+});
+
+/****
+* Game Code
+****/
+// Sound for animal jump
+// Exit (esophagus opening)
+// Obstacle (stomach acid bubble)
+// Snake exterior (for outside interaction)
+// Snake belly (background)
+// Animal shapes (simple colored ellipses for each animal)
+// --- Global variables ---
+var animalIds = ['pig', 'cat', 'bee', 'frog', 'polarBear', 'elephant', 'bird', 'snail', 'axolotl', 'pinkFox', 'boyBat', 'wolf', 'chinchilla', 'moth', 'goat', 'bunny', 'bear', 'deer'];
+var animals = [];
+var acidBubbles = [];
+var selectedAnimal = null;
+var dragOffsetX = 0;
+var dragOffsetY = 0;
+var insideMode = true; // true: inside snake, false: outside
+var snakeBelly, snakeSkin, exitNode;
+var scoreTxt;
+var rescuedCount = 0;
+var totalToRescue = 5; // MVP: rescue 5 animals to win
+// --- Setup snake belly (inside) ---
+snakeBelly = LK.getAsset('snakeBelly', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: 2048 / 2,
+ y: 1200
+});
+game.addChild(snakeBelly);
+// --- Setup exit (esophagus opening) ---
+exitNode = LK.getAsset('exit', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: 2048 / 2,
+ y: 800
+});
+game.addChild(exitNode);
+// --- Setup animals ---
+function spawnAnimals() {
+ animals = [];
+ var startX = 2048 / 2 - 400;
+ var startY = 1200;
+ for (var i = 0; i < totalToRescue; i++) {
+ var id = animalIds[i];
+ var animal = new Animal();
+ animal.assetId = id;
+ animal.x = startX + i * 200;
+ animal.y = startY + Math.random() * 100 - 50;
+ animal.vx = (Math.random() - 0.5) * 6;
+ animal.vy = (Math.random() - 0.5) * 6;
+ animals.push(animal);
+ game.addChild(animal);
+ }
+}
+spawnAnimals();
+// --- Setup acid bubbles (obstacles) ---
+function spawnAcidBubbles() {
+ acidBubbles = [];
+ for (var i = 0; i < 3; i++) {
+ var bubble = new AcidBubble();
+ bubble.x = 2048 / 2 + (Math.random() - 0.5) * 600;
+ bubble.y = 1200 + (Math.random() - 0.5) * 300;
+ acidBubbles.push(bubble);
+ game.addChild(bubble);
+ }
+}
+spawnAcidBubbles();
+// --- Setup score text ---
+scoreTxt = new Text2('0', {
+ size: 120,
+ fill: "#fff"
+});
+scoreTxt.anchor.set(0.5, 0);
+LK.gui.top.addChild(scoreTxt);
+// --- Setup snake skin (outside) ---
+snakeSkin = LK.getAsset('snakeSkin', {
+ anchorX: 0.5,
+ anchorY: 0,
+ x: 2048 / 2,
+ y: 0
+});
+// Not added yet; only shown in outside mode
+// --- Helper: reset game state ---
+function resetGame() {
+ // Remove all animals and bubbles
+ for (var i = 0; i < animals.length; i++) animals[i].destroy();
+ for (var i = 0; i < acidBubbles.length; i++) acidBubbles[i].destroy();
+ animals = [];
+ acidBubbles = [];
+ rescuedCount = 0;
+ scoreTxt.setText('0');
+ insideMode = true;
+ if (snakeSkin.parent) snakeSkin.parent.removeChild(snakeSkin);
+ if (!snakeBelly.parent) game.addChild(snakeBelly);
+ if (!exitNode.parent) game.addChild(exitNode);
+ spawnAnimals();
+ spawnAcidBubbles();
+}
+// --- Touch/drag logic ---
+game.move = function (x, y, obj) {
+ if (!insideMode) return;
+ if (selectedAnimal) {
+ // Move animal with finger
+ selectedAnimal.x = x + dragOffsetX;
+ selectedAnimal.y = y + dragOffsetY;
+ }
+};
+game.down = function (x, y, obj) {
+ if (!insideMode) return;
+ // Check if any animal is touched
+ for (var i = 0; i < animals.length; i++) {
+ var a = animals[i];
+ var dx = x - a.x;
+ var dy = y - a.y;
+ if (dx * dx + dy * dy < a.radius * a.radius) {
+ a.down(x, y, obj);
+ return;
+ }
+ }
+};
+game.up = function (x, y, obj) {
+ if (!insideMode) return;
+ if (selectedAnimal) {
+ // Give a jump impulse upwards
+ selectedAnimal.vy = -18;
+ selectedAnimal.vx = (Math.random() - 0.5) * 8;
+ LK.getSound('jump').play();
+ selectedAnimal.up(x, y, obj);
+ }
+};
+// --- Outside mode: tap snake skin to help animals escape ---
+snakeSkin.down = function (x, y, obj) {
+ if (!insideMode) {
+ // "Massage" the snake: all animals get a little upward boost
+ for (var i = 0; i < animals.length; i++) {
+ animals[i].vy -= 10 + Math.random() * 6;
+ }
+ // Animate snake skin
+ tween(snakeSkin, {
+ y: 20
+ }, {
+ duration: 120,
+ easing: tween.easeOut,
+ onFinish: function onFinish() {
+ tween(snakeSkin, {
+ y: 0
+ }, {
+ duration: 180,
+ easing: tween.easeIn
+ });
+ }
+ });
+ }
+};
+// --- Game update loop ---
+game.update = function () {
+ // Switch between inside/outside every 10 seconds
+ if (LK.ticks % 600 === 0) {
+ insideMode = !insideMode;
+ if (insideMode) {
+ // Show inside
+ if (!snakeBelly.parent) game.addChild(snakeBelly);
+ if (!exitNode.parent) game.addChild(exitNode);
+ if (snakeSkin.parent) snakeSkin.parent.removeChild(snakeSkin);
+ } else {
+ // Show outside
+ if (!snakeSkin.parent) game.addChild(snakeSkin);
+ if (snakeBelly.parent) snakeBelly.parent.removeChild(snakeBelly);
+ if (exitNode.parent) exitNode.parent.removeChild(exitNode);
+ }
+ }
+ // Update animals
+ for (var i = animals.length - 1; i >= 0; i--) {
+ var a = animals[i];
+ a.update();
+ // If animal touches exit, rescue it!
+ if (insideMode && a.intersects(exitNode)) {
+ // Animate out
+ tween(a, {
+ y: exitNode.y - 200
+ }, {
+ duration: 400,
+ easing: tween.easeIn,
+ onFinish: function onFinish() {
+ a.destroy();
+ }
+ });
+ animals.splice(i, 1);
+ rescuedCount++;
+ scoreTxt.setText(rescuedCount);
+ // Win condition
+ if (rescuedCount >= totalToRescue) {
+ LK.showYouWin();
+ return;
+ }
+ }
+ }
+ // Update acid bubbles
+ for (var i = acidBubbles.length - 1; i >= 0; i--) {
+ var b = acidBubbles[i];
+ b.update();
+ // If any animal hits a bubble, game over
+ for (var j = 0; j < animals.length; j++) {
+ var a = animals[j];
+ var dx = a.x - b.x;
+ var dy = a.y - b.y;
+ var dist = a.radius + b.radius - 10;
+ if (dx * dx + dy * dy < dist * dist) {
+ LK.effects.flashScreen(0xff0000, 800);
+ LK.showGameOver();
+ return;
+ }
+ }
+ }
+};
+// --- Reset game on game over or win ---
+LK.on('gameover', function () {
+ resetGame();
+});
+LK.on('youwin', function () {
+ resetGame();
+});
+// --- Instructions text ---
+var instructions = new Text2("Drag animals to help them jump to the exit!\nAvoid acid bubbles.\nSometimes tap the snake's back to help!", {
+ size: 60,
+ fill: "#fff"
+});
+instructions.anchor.set(0.5, 0);
+LK.gui.top.addChild(instructions);
+instructions.y = 180;
+instructions.x = 2048 / 2;
\ No newline at end of file