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