User prompt
Please fix the bug: 'Cannot read properties of undefined (reading 'length')' in or related to this line: 'var animalGraphics = self.attachAsset(self.assetId, {' Line Number: 35
Code edit (1 edits merged)
Please save this source code
User prompt
Animal Rescue: Escape the Snake!
Initial prompt
(Pig) (Cat) (Bee) (Frog) (Polar Bear) (Elephant) (Bird) (Snail) (Axolotl) (Pink Fox) (Boy Bat) (Wolf) (Chinchilla) And (Moth) Is Struggle In The Snake Tummy Without Inside Of Snake Tummy With Video
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var facekit = LK.import("@upit/facekit.v1");
/****
* Classes
****/
// Animal class
var Animal = Container.expand(function (assetId) {
var self = Container.call(this);
// Attach animal asset
var animalGraphics = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
// Escape state
self.escaped = false;
// Movement velocity
self.vx = 0;
self.vy = 0;
// Used for mouth open "boost"
self.boostTimer = 0;
// Update method
self.update = function () {
if (self.escaped) return;
// Apply velocity
self.x += self.vx;
self.y += self.vy;
// Friction
self.vx *= 0.92;
self.vy *= 0.92;
// Clamp inside snake tummy
if (self.x < tummy.x - tummy.width / 2 + animalGraphics.width / 2) {
self.x = tummy.x - tummy.width / 2 + animalGraphics.width / 2;
self.vx *= -0.5;
}
if (self.x > tummy.x + tummy.width / 2 - animalGraphics.width / 2) {
self.x = tummy.x + tummy.width / 2 - animalGraphics.width / 2;
self.vx *= -0.5;
}
if (self.y < tummy.y - tummy.height / 2 + animalGraphics.height / 2) {
self.y = tummy.y - tummy.height / 2 + animalGraphics.height / 2;
self.vy *= -0.5;
}
if (self.y > tummy.y + tummy.height / 2 - animalGraphics.height / 2) {
self.y = tummy.y + tummy.height / 2 - animalGraphics.height / 2;
self.vy *= -0.5;
}
};
// Escape animation
self.escape = function () {
self.escaped = true;
tween(self, {
y: self.y - 600,
alpha: 0
}, {
duration: 900,
easing: tween.easeIn,
onFinish: function onFinish() {
self.visible = false;
}
});
};
return self;
});
// Obstacle class
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obsGraphics = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
// Movement
self.vx = 0;
self.vy = 0;
self.update = function () {
self.x += self.vx;
self.y += self.vy;
// Bounce off tummy walls
if (self.x < tummy.x - tummy.width / 2 + obsGraphics.width / 2) {
self.x = tummy.x - tummy.width / 2 + obsGraphics.width / 2;
self.vx *= -1;
}
if (self.x > tummy.x + tummy.width / 2 - obsGraphics.width / 2) {
self.x = tummy.x + tummy.width / 2 - obsGraphics.width / 2;
self.vx *= -1;
}
if (self.y < tummy.y - tummy.height / 2 + obsGraphics.height / 2) {
self.y = tummy.y - tummy.height / 2 + obsGraphics.height / 2;
self.vy *= -1;
}
if (self.y > tummy.y + tummy.height / 2 - obsGraphics.height / 2) {
self.y = tummy.y + tummy.height / 2 - obsGraphics.height / 2;
self.vy *= -1;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Success flash
// Obstacle
// Animal shapes (different colors)
// Snake tummy (background, semi-transparent)
/*
We use simple shapes for animals and obstacles for now.
Each animal will be a different colored ellipse.
The snake's tummy is a large, semi-transparent ellipse in the center.
Obstacles are red ellipses.
*/
// Use facekit: show camera feed as background
facekit.enabled = true;
// Center of the game
var centerX = 2048 / 2;
var centerY = 2732 / 2;
// Snake tummy (background, not interactive)
var tummy = LK.getAsset('snakeTummy', {
anchorX: 0.5,
anchorY: 0.5,
x: centerX,
y: centerY,
alpha: 0.7
});
game.addChild(tummy);
// List of animal types and their asset ids
var animalTypes = [{
id: 'animal_pig',
name: 'Pig'
}, {
id: 'animal_cat',
name: 'Cat'
}, {
id: 'animal_bee',
name: 'Bee'
}, {
id: 'animal_frog',
name: 'Frog'
}, {
id: 'animal_polarbear',
name: 'Polar Bear'
}, {
id: 'animal_elephant',
name: 'Elephant'
}, {
id: 'animal_bird',
name: 'Bird'
}, {
id: 'animal_snail',
name: 'Snail'
}, {
id: 'animal_axolotl',
name: 'Axolotl'
}, {
id: 'animal_fox',
name: 'Pink Fox'
}, {
id: 'animal_boybat',
name: 'Boy Bat'
}, {
id: 'animal_wolf',
name: 'Wolf'
}, {
id: 'animal_chinchilla',
name: 'Chinchilla'
}, {
id: 'animal_moth',
name: 'Moth'
}];
// Shuffle animals for random placement
function shuffle(arr) {
for (var i = arr.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
}
shuffle(animalTypes);
// Create animals
var animals = [];
for (var i = 0; i < animalTypes.length; i++) {
var a = new Animal(animalTypes[i].id);
// Place animals in a circle inside the tummy
var angle = i / animalTypes.length * Math.PI * 2;
var radius = 500;
a.x = centerX + Math.cos(angle) * radius;
a.y = centerY + Math.sin(angle) * radius;
a.name = animalTypes[i].name;
animals.push(a);
game.addChild(a);
}
// Obstacles
var obstacles = [];
for (var i = 0; i < 4; i++) {
var obs = new Obstacle();
// Place randomly inside tummy
obs.x = centerX + (Math.random() - 0.5) * 700;
obs.y = centerY + (Math.random() - 0.5) * 900;
// Random velocity
obs.vx = (Math.random() - 0.5) * 7;
obs.vy = (Math.random() - 0.5) * 7;
obstacles.push(obs);
game.addChild(obs);
}
// Score: animals escaped
var animalsEscaped = 0;
// Score text
var scoreTxt = new Text2('0 / ' + animals.length, {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Instructions text
var instructions = new Text2('Move your face to guide the animals!\nOpen your mouth to help them escape!', {
size: 70,
fill: "#fff"
});
instructions.anchor.set(0.5, 0);
LK.gui.bottom.addChild(instructions);
// Main animal to control (the first in the array)
var mainAnimal = animals[0];
// Helper: get facekit mouth center, fallback to center
function getFaceX() {
if (facekit.mouthCenter && typeof facekit.mouthCenter.x === 'number') {
return facekit.mouthCenter.x;
}
return centerX;
}
function getFaceY() {
if (facekit.mouthCenter && typeof facekit.mouthCenter.y === 'number') {
return facekit.mouthCenter.y;
}
return centerY;
}
// Helper: check if animal is at escape zone (top of tummy)
function isAtEscapeZone(animal) {
// Escape zone: top 120px of tummy
return animal.y < tummy.y - tummy.height / 2 + 120;
}
// Helper: check collision between two containers (circle approx)
function isColliding(a, b) {
var dx = a.x - b.x;
var dy = a.y - b.y;
var dist = Math.sqrt(dx * dx + dy * dy);
var r1 = a.width || 90;
var r2 = b.width || 60;
return dist < r1 / 2 + r2 / 2 - 10;
}
// Flash effect for escape
function flashEscape() {
var flash = LK.getAsset('escapeFlash', {
anchorX: 0.5,
anchorY: 0.5,
x: centerX,
y: centerY,
alpha: 0.7
});
game.addChild(flash);
tween(flash, {
alpha: 0
}, {
duration: 600,
onFinish: function onFinish() {
flash.destroy();
}
});
}
// Game update
game.update = function () {
// Update all animals
for (var i = 0; i < animals.length; i++) {
animals[i].update();
}
// Update obstacles
for (var i = 0; i < obstacles.length; i++) {
obstacles[i].update();
}
// Control main animal with face
if (!mainAnimal.escaped) {
// Move towards face position
var fx = getFaceX();
var fy = getFaceY();
// Clamp to tummy area
var tx = Math.max(tummy.x - tummy.width / 2 + 90, Math.min(tummy.x + tummy.width / 2 - 90, fx));
var ty = Math.max(tummy.y - tummy.height / 2 + 90, Math.min(tummy.y + tummy.height / 2 - 90, fy));
// Smooth follow
mainAnimal.vx += (tx - mainAnimal.x) * 0.08;
mainAnimal.vy += (ty - mainAnimal.y) * 0.08;
// If mouth open, give a boost upwards
if (facekit.mouthOpen) {
mainAnimal.vy -= 2.5;
mainAnimal.boostTimer = 8;
} else if (mainAnimal.boostTimer > 0) {
mainAnimal.vy -= 1.2;
mainAnimal.boostTimer--;
}
// If at escape zone, escape!
if (isAtEscapeZone(mainAnimal)) {
mainAnimal.escape();
animalsEscaped++;
scoreTxt.setText(animalsEscaped + ' / ' + animals.length);
flashEscape();
// Next animal becomes main
var found = false;
for (var i = 0; i < animals.length; i++) {
if (!animals[i].escaped) {
mainAnimal = animals[i];
found = true;
break;
}
}
if (!found) {
// All escaped!
LK.showYouWin();
}
}
}
// Collisions: main animal with obstacles
if (!mainAnimal.escaped) {
for (var i = 0; i < obstacles.length; i++) {
if (isColliding(mainAnimal, obstacles[i])) {
// Flash red, reset animal to center
LK.effects.flashScreen(0xff0000, 600);
mainAnimal.x = centerX;
mainAnimal.y = centerY + 400;
mainAnimal.vx = 0;
mainAnimal.vy = 0;
}
}
}
// Collisions: animals with each other (push apart)
for (var i = 0; i < animals.length; i++) {
for (var j = i + 1; j < animals.length; j++) {
if (animals[i].escaped || animals[j].escaped) continue;
var dx = animals[i].x - animals[j].x;
var dy = animals[i].y - animals[j].y;
var dist = Math.sqrt(dx * dx + dy * dy);
var minDist = 90;
if (dist < minDist && dist > 0) {
var overlap = (minDist - dist) / 2;
var ox = dx / dist * overlap;
var oy = dy / dist * overlap;
animals[i].x += ox;
animals[i].y += oy;
animals[j].x -= ox;
animals[j].y -= oy;
}
}
}
};
// No touch controls: all control is via facekit
// No music, no sound, no pause, no game over handling (handled by LK) ===================================================================
--- original.js
+++ change.js
@@ -7,12 +7,12 @@
/****
* Classes
****/
// Animal class
-var Animal = Container.expand(function () {
+var Animal = Container.expand(function (assetId) {
var self = Container.call(this);
// Attach animal asset
- var animalGraphics = self.attachAsset(self.assetId, {
+ var animalGraphics = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
// Escape state
@@ -189,10 +189,9 @@
shuffle(animalTypes);
// Create animals
var animals = [];
for (var i = 0; i < animalTypes.length; i++) {
- var a = new Animal();
- a.assetId = animalTypes[i].id;
+ var a = new Animal(animalTypes[i].id);
// Place animals in a circle inside the tummy
var angle = i / animalTypes.length * Math.PI * 2;
var radius = 500;
a.x = centerX + Math.cos(angle) * radius;