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 () { var self = Container.call(this); // Attach animal asset var animalGraphics = self.attachAsset(self.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(); a.assetId = 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
@@ -1,6 +1,365 @@
-/****
+/****
+* Plugins
+****/
+var tween = LK.import("@upit/tween.v1");
+var facekit = LK.import("@upit/facekit.v1");
+
+/****
+* Classes
+****/
+// Animal class
+var Animal = Container.expand(function () {
+ var self = Container.call(this);
+ // Attach animal asset
+ var animalGraphics = self.attachAsset(self.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
-});
\ No newline at end of file
+});
+
+/****
+* 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();
+ a.assetId = 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)
\ No newline at end of file