User prompt
Update with: var DragonHead = Container.expand(function () { var self = Container.call(this); var head = self.attachAsset('dragonHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 2 }); var leftEye = self.attachAsset('dragonEyes', { anchorX: 0.5, anchorY: 0.5, x: -50, y: -30 }); var rightEye = self.attachAsset('dragonEyes', { anchorX: 0.5, anchorY: 0.5, x: 50, y: -30 }); // Position smoothing variables self.targetX = 2048 / 2; self.targetY = 2732 * 0.2; self.smoothingFactor = 0.15; // Simplified rotation variables self.currentRotation = 0; self.targetRotation = 0; self.rotationSmoothing = 0.1; self.update = function () { // Position tracking if (facekit.noseTip) { self.targetX = facekit.noseTip.x; self.targetY = facekit.noseTip.y; } // Apply position smoothing self.x += (self.targetX - self.x) * self.smoothingFactor; self.y += (self.targetY - self.y) * self.smoothingFactor; // Fixed rotation calculation with reversed direction if (facekit.leftEye && facekit.rightEye) { // Calculate angle between eyes var dx = facekit.rightEye.x - facekit.leftEye.x; var dy = facekit.rightEye.y - facekit.leftEye.y; // Get the raw angle in radians and negate it to reverse the direction var rawAngle = -Math.atan2(dy, dx); // Limit the rotation to prevent extreme angles (±0.3 radians ≈ ±17 degrees) self.targetRotation = Math.max(-0.3, Math.min(0.3, rawAngle)); // Apply rotation smoothing self.rotation += (self.targetRotation - self.rotation) * self.rotationSmoothing; } }; return self; }); ↪💡 Consider importing and using the following plugins: @upit/facekit.v1
User prompt
Update with: var DragonHead = Container.expand(function () { var self = Container.call(this); var head = self.attachAsset('dragonHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 2 }); var leftEye = self.attachAsset('dragonEyes', { anchorX: 0.5, anchorY: 0.5, x: -50, y: -30 }); var rightEye = self.attachAsset('dragonEyes', { anchorX: 0.5, anchorY: 0.5, x: 50, y: -30 }); // Position smoothing variables self.targetX = 2048 / 2; self.targetY = 2732 * 0.2; self.smoothingFactor = 0.15; // Simplified rotation variables self.currentRotation = 0; self.targetRotation = 0; self.rotationSmoothing = 0.1; self.update = function () { // Position tracking if (facekit.noseTip) { self.targetX = facekit.noseTip.x; self.targetY = facekit.noseTip.y; } // Apply position smoothing self.x += (self.targetX - self.x) * self.smoothingFactor; self.y += (self.targetY - self.y) * self.smoothingFactor; // Simplified rotation calculation directly from eye positions if (facekit.leftEye && facekit.rightEye) { // Calculate angle between eyes (simpler approach) var dx = facekit.rightEye.x - facekit.leftEye.x; var dy = facekit.rightEye.y - facekit.leftEye.y; // Get the raw angle in radians var rawAngle = Math.atan2(dy, dx); // Limit the rotation to prevent extreme angles (±0.3 radians ≈ ±17 degrees) self.targetRotation = Math.max(-0.3, Math.min(0.3, rawAngle)); // Apply rotation smoothing self.rotation += (self.targetRotation - self.rotation) * self.rotationSmoothing; // Debug output - uncomment if you have console logging // console.log("Eyes dx/dy:", dx, dy, "Target rotation:", self.targetRotation, "Current rotation:", self.rotation); } }; return self; }); ↪💡 Consider importing and using the following plugins: @upit/facekit.v1
User prompt
Update with: var DragonHead = Container.expand(function () { var self = Container.call(this); var head = self.attachAsset('dragonHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 2 }); var leftEye = self.attachAsset('dragonEyes', { anchorX: 0.5, anchorY: 0.5, x: -50, y: -30 }); var rightEye = self.attachAsset('dragonEyes', { anchorX: 0.5, anchorY: 0.5, x: 50, y: -30 }); // Position smoothing variables self.targetX = 2048 / 2; self.targetY = 2732 * 0.2; self.smoothingFactor = 0.15; // Rotation/tilt variables using the provided code approach self.targetTilt = 0; self.tiltSmoothingFactor = 0.11; self.tiltScaleFactor = 0.09; // Calculate face tilt based on eye and mouth positions self.calculateFaceTilt = function() { if (facekit.leftEye && facekit.rightEye && facekit.mouthCenter) { // Calculate midpoint between eyes var eyeMidX = (facekit.leftEye.x + facekit.rightEye.x) / 2; var eyeMidY = (facekit.leftEye.y + facekit.rightEye.y) / 2; // Calculate angle between eye midpoint and mouth, negated to fix direction var dx = facekit.mouthCenter.x - eyeMidX; var dy = facekit.mouthCenter.y - eyeMidY; var angle = -(Math.atan2(dx, dy) * (180 / Math.PI)); // Reduced max angle to ±15 degrees and lowered multiplier return Math.max(-15, Math.min(15, angle * 0.15)); } return 0; // Default to straight when face points aren't available }; self.update = function () { // Track the nose position instead of mouth if (facekit.noseTip) { // Set target positions based on nose tip self.targetX = facekit.noseTip.x; self.targetY = facekit.noseTip.y; } // Apply smoothing to position self.x += (self.targetX - self.x) * self.smoothingFactor; self.y += (self.targetY - self.y) * self.smoothingFactor; // Apply face tilt calculation for rotation if (facekit.leftEye && facekit.rightEye) { self.targetTilt = self.calculateFaceTilt() * self.tiltScaleFactor; // Reduce max rotation to ±15 degrees self.targetTilt = Math.max(-15, Math.min(15, self.targetTilt)); // Convert degrees to radians for rotation var targetTiltRadians = self.targetTilt * (Math.PI / 180); self.rotation += (targetTiltRadians - self.rotation) * self.tiltSmoothingFactor; } }; return self; }); ↪💡 Consider importing and using the following plugins: @upit/facekit.v1
User prompt
Update with: var DragonHead = Container.expand(function () { var self = Container.call(this); var head = self.attachAsset('dragonHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 2 }); var leftEye = self.attachAsset('dragonEyes', { anchorX: 0.5, anchorY: 0.5, x: -50, y: -30 }); var rightEye = self.attachAsset('dragonEyes', { anchorX: 0.5, anchorY: 0.5, x: 50, y: -30 }); // Position smoothing variables self.targetX = 2048 / 2; // Default center position self.targetY = 2732 * 0.2; self.smoothingFactor = 0.15; // Previous positions for stable rotation calculation self.prevX = self.targetX; self.prevY = self.targetY; // Rotation variables self.targetRotation = 0; self.rotationSmoothingFactor = 0.08; self.maxRotation = Math.PI / 8; // Limit rotation to avoid flipping self.update = function () { // Track the nose position if (facekit.noseTip) { // Store previous position before updating self.prevX = self.x; self.prevY = self.y; // Set target positions based on nose tip self.targetX = facekit.noseTip.x; self.targetY = facekit.noseTip.y; // Limit the rotation calculation to small head movements // Only calculate rotation if we've moved enough to avoid jitter var dx = self.targetX - self.prevX; var dy = self.targetY - self.prevY; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 3) { // Minimum threshold for rotation calculation // Calculate rotation based on movement direction var angle = Math.atan2(dy, dx); // Limit rotation to prevent upside-down dragon var limitedAngle = Math.max(-self.maxRotation, Math.min(self.maxRotation, angle)); self.targetRotation = limitedAngle; } } // Apply smoothing to position self.x += (self.targetX - self.x) * self.smoothingFactor; self.y += (self.targetY - self.y) * self.smoothingFactor; // Apply smoothing to rotation with limits var rotationDiff = self.targetRotation - self.rotation; // Normalize the rotation difference while (rotationDiff > Math.PI) rotationDiff -= Math.PI * 2; while (rotationDiff < -Math.PI) rotationDiff += Math.PI * 2; // Apply smoothed rotation with limiting self.rotation += rotationDiff * self.rotationSmoothingFactor; }; return self; }); ↪💡 Consider importing and using the following plugins: @upit/facekit.v1
User prompt
Update with: var DragonHead = Container.expand(function () { var self = Container.call(this); var head = self.attachAsset('dragonHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 2 }); var leftEye = self.attachAsset('dragonEyes', { anchorX: 0.5, anchorY: 0.5, x: -50, y: -30 }); var rightEye = self.attachAsset('dragonEyes', { anchorX: 0.5, anchorY: 0.5, x: 50, y: -30 }); // Position smoothing variables self.targetX = 0; self.targetY = 0; self.smoothingFactor = 0.15; // Adjust for more/less smoothing // Rotation smoothing variables self.targetRotation = 0; self.rotationSmoothingFactor = 0.1; // Adjust for more/less smoothing self.update = function () { // Track the nose position instead of mouth if (facekit.noseTip) { // Set target positions based on nose tip self.targetX = facekit.noseTip.x; self.targetY = facekit.noseTip.y; // Calculate rotation if we have both eyes if (facekit.leftEye && facekit.rightEye) { var dx = facekit.rightEye.x - facekit.leftEye.x; var dy = facekit.rightEye.y - facekit.leftEye.y; self.targetRotation = Math.atan2(dy, dx); } } // Apply smoothing to position self.x += (self.targetX - self.x) * self.smoothingFactor; self.y += (self.targetY - self.y) * self.smoothingFactor; // Apply smoothing to rotation self.rotation += (self.targetRotation - self.rotation) * self.rotationSmoothingFactor; }; return self; }); ↪💡 Consider importing and using the following plugins: @upit/facekit.v1
User prompt
Change the direction of the fireballs, they need to be going down.
Code edit (1 edits merged)
Please save this source code
User prompt
Dragon's Breath
Initial prompt
Hi Ava, we are going to make a FaceKit game that allows players to play as a dragon who opens their mouth to blow fire to defeat enemies that are attacking them. The game will feature a mask of a dragon over the players face, with a body and wings layers behind the face to be animated separately, but they will be pinned to the mask to give the illusion of the full body of a dragon. The dragon will be facing down on the screen and the enemies will have the perspective of running up and into the screen while the dragon blows fire down and out towards the bottom of the screen. When hit with the dragon fire, the enemies will have different animations, like being turned to ash or being sent flying. The game is over when the dragon loses all its life. Score is recorded and high scores are saved.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); var facekit = LK.import("@upit/facekit.v1"); /**** * Classes ****/ var DragonHead = Container.expand(function () { var self = Container.call(this); var head = self.attachAsset('dragonHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 2 }); var leftEye = self.attachAsset('dragonEyes', { anchorX: 0.5, anchorY: 0.5, x: -50, y: -30 }); var rightEye = self.attachAsset('dragonEyes', { anchorX: 0.5, anchorY: 0.5, x: 50, y: -30 }); // Position smoothing variables self.targetX = 0; self.targetY = 0; self.smoothingFactor = 0.15; // Adjust for more/less smoothing // Rotation smoothing variables self.targetRotation = 0; self.rotationSmoothingFactor = 0.1; // Adjust for more/less smoothing self.update = function () { // Track the nose position instead of mouth if (facekit.noseTip) { // Set target positions based on nose tip self.targetX = facekit.noseTip.x; self.targetY = facekit.noseTip.y; // Calculate rotation if we have both eyes if (facekit.leftEye && facekit.rightEye) { var dx = facekit.rightEye.x - facekit.leftEye.x; var dy = facekit.rightEye.y - facekit.leftEye.y; self.targetRotation = Math.atan2(dy, dx); } } // Apply smoothing to position self.x += (self.targetX - self.x) * self.smoothingFactor; self.y += (self.targetY - self.y) * self.smoothingFactor; // Apply smoothing to rotation self.rotation += (self.targetRotation - self.rotation) * self.rotationSmoothingFactor; }; return self; }); var Enemy = Container.expand(function (type) { var self = Container.call(this); var assetId = 'enemy1'; if (type === 2) { assetId = 'enemy2'; } if (type === 3) { assetId = 'enemy3'; } self.type = type || 1; var enemyGraphic = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); // Different enemy types have different speeds and health switch (self.type) { case 1: self.speed = 2; self.health = 1; break; case 2: self.speed = 1.5; self.health = 2; break; case 3: self.speed = 3; self.health = 1; break; } self.update = function () { self.y -= self.speed; // Enemies move up the screen toward the player }; self.hit = function () { self.health--; // Visual feedback when hit LK.effects.flashObject(enemyGraphic, 0xff0000, 200); // Different death animations based on enemy type if (self.health <= 0) { if (self.type === 1) { // Type 1 enemy burns to ash tween(enemyGraphic, { alpha: 0, scaleX: 0.5, scaleY: 0.5 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { self.toDestroy = true; } }); } else if (self.type === 2) { // Type 2 enemy spins away tween(enemyGraphic, { rotation: Math.PI * 4, y: self.y + 300 }, { duration: 700, easing: tween.easeIn, onFinish: function onFinish() { self.toDestroy = true; } }); } else if (self.type === 3) { // Type 3 enemy explodes outward tween(enemyGraphic, { alpha: 0, scaleX: 2, scaleY: 2 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { self.toDestroy = true; } }); } return true; } return false; }; return self; }); var FireParticle = Container.expand(function () { var self = Container.call(this); var particle = self.attachAsset('fireParticle', { anchorX: 0.5, anchorY: 0.5 }); self.vx = Math.random() * 4 - 2; self.vy = Math.random() * 2 + 1; self.lifespan = 20 + Math.random() * 20; self.age = 0; self.update = function () { self.x += self.vx; self.y += self.vy; self.age++; particle.alpha = 1 - self.age / self.lifespan; if (self.age >= self.lifespan) { self.destroy(); } }; return self; }); var Fireball = Container.expand(function () { var self = Container.call(this); var fireballGraphic = self.attachAsset('fireball', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 15; self.active = true; self.update = function () { if (!self.active) { return; } self.y += self.speed; // Create fire trail particles if (Math.random() < 0.3) { createFireParticle(self.x + (Math.random() * 40 - 20), self.y + 20); } }; return self; }); var PowerBar = Container.expand(function () { var self = Container.call(this); // Background bar var background = self.attachAsset('powerBarBg', { anchorX: 0.5, anchorY: 0.5 }); // Foreground bar (showing power level) var bar = self.attachAsset('powerBar', { anchorX: 0, anchorY: 0.5, x: -250 }); self.maxWidth = 500; self.setPower = function (percent) { var newWidth = self.maxWidth * percent; bar.width = Math.max(0, Math.min(newWidth, self.maxWidth)); // Change color based on power level if (percent < 0.3) { bar.tint = 0xe74c3c; // Red when low } else if (percent < 0.6) { bar.tint = 0xf39c12; // Orange when medium } else { bar.tint = 0x2ecc71; // Green when high } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Set dark blue background for sky effect game.setBackgroundColor(0x2c3e50); // Game state variables var score = 0; var firepower = 100; var maxFirepower = 100; var firepowerRechargeRate = 0.5; var firepowerUseRate = 2; var gameActive = true; var fireballs = []; var enemies = []; var fireParticles = []; var isFiring = false; var lastFireTime = 0; var fireRate = 150; // ms between fireballs var enemySpawnRate = 60; // Frames between enemy spawns var difficultyScaling = 0; // Create dragon head (player character) var dragon = game.addChild(new DragonHead()); dragon.x = 2048 / 2; dragon.y = 2732 * 0.2; // Place dragon at top fifth of screen // Create power bar var powerBar = new PowerBar(); powerBar.x = 2048 / 2; powerBar.y = 150; LK.gui.top.addChild(powerBar); // Score display var scoreTxt = new Text2('0', { size: 80, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); scoreTxt.y = 50; LK.gui.top.addChild(scoreTxt); // Create a function to add fire particles function createFireParticle(x, y) { var particle = new FireParticle(); particle.x = x; particle.y = y; game.addChild(particle); fireParticles.push(particle); } // Function to spawn a fireball function spawnFireball() { if (!gameActive || firepower < 10) { return; } var now = Date.now(); if (now - lastFireTime < fireRate) { return; } lastFireTime = now; // Create new fireball var fireball = new Fireball(); fireball.x = dragon.x; fireball.y = dragon.y + 50; // Spawn below dragon's mouth game.addChild(fireball); fireballs.push(fireball); // Use firepower firepower -= firepowerUseRate; powerBar.setPower(firepower / maxFirepower); // Play sound LK.getSound('firebreathSound').play(); } // Function to spawn enemies function spawnEnemy() { if (!gameActive) { return; } // Create enemy of random type var type = Math.floor(Math.random() * 3) + 1; var enemy = new Enemy(type); // Position randomly along bottom of screen enemy.x = Math.random() * (2048 - 200) + 100; enemy.y = 2732 + 100; // Start below screen game.addChild(enemy); enemies.push(enemy); } // Game update logic game.update = function () { if (!gameActive) { return; } // Check if mouth is open for fire breathing if (facekit && facekit.mouthOpen) { isFiring = true; spawnFireball(); } else { isFiring = false; } // Recharge firepower when not firing if (!isFiring && firepower < maxFirepower) { firepower += firepowerRechargeRate; powerBar.setPower(firepower / maxFirepower); } // Warn player when power is low if (firepower < 20 && Math.floor(LK.ticks) % 60 === 0) { LK.getSound('powerLow').play(); } // Spawn enemies if (LK.ticks % Math.max(10, enemySpawnRate - difficultyScaling) === 0) { spawnEnemy(); } // Increase difficulty over time if (LK.ticks % 300 === 0 && difficultyScaling < 40) { difficultyScaling += 1; } // Update fireballs and check for collisions for (var i = fireballs.length - 1; i >= 0; i--) { var fireball = fireballs[i]; // Remove fireballs that go off screen if (fireball.y < -100) { fireball.destroy(); fireballs.splice(i, 1); continue; } // Check for collisions with enemies for (var j = enemies.length - 1; j >= 0; j--) { var enemy = enemies[j]; if (fireball.active && fireball.intersects(enemy)) { // Hit the enemy var killed = enemy.hit(); if (killed) { score += enemy.type * 10; scoreTxt.setText(score); LK.setScore(score); LK.getSound('enemyDefeat').play(); } // Deactivate the fireball fireball.active = false; // Destroy it after a slight delay (for visual effect) LK.setTimeout(function () { if (fireballs.indexOf(fireball) !== -1) { fireball.destroy(); fireballs.splice(fireballs.indexOf(fireball), 1); } }, 100); break; } } } // Update enemies for (var k = enemies.length - 1; k >= 0; k--) { var currentEnemy = enemies[k]; // Remove enemies marked for destruction if (currentEnemy.toDestroy) { currentEnemy.destroy(); enemies.splice(k, 1); continue; } // Check if enemy reaches player if (currentEnemy.y < 300) { // Game over gameActive = false; LK.getSound('gameOverSound').play(); LK.showGameOver(); } } // Update fire particles for (var m = fireParticles.length - 1; m >= 0; m--) { if (!fireParticles[m].parent) { fireParticles.splice(m, 1); } } }; // Start background music LK.playMusic('gameMusic', { fade: { start: 0, end: 0.3, duration: 1000 } });
===================================================================
--- original.js
+++ change.js
@@ -27,14 +27,33 @@
anchorY: 0.5,
x: 50,
y: -30
});
+ // Position smoothing variables
+ self.targetX = 0;
+ self.targetY = 0;
+ self.smoothingFactor = 0.15; // Adjust for more/less smoothing
+ // Rotation smoothing variables
+ self.targetRotation = 0;
+ self.rotationSmoothingFactor = 0.1; // Adjust for more/less smoothing
self.update = function () {
- // The dragon head follows the player's face position
- if (facekit.mouthCenter) {
- self.x = facekit.mouthCenter.x;
- self.y = facekit.mouthCenter.y;
+ // Track the nose position instead of mouth
+ if (facekit.noseTip) {
+ // Set target positions based on nose tip
+ self.targetX = facekit.noseTip.x;
+ self.targetY = facekit.noseTip.y;
+ // Calculate rotation if we have both eyes
+ if (facekit.leftEye && facekit.rightEye) {
+ var dx = facekit.rightEye.x - facekit.leftEye.x;
+ var dy = facekit.rightEye.y - facekit.leftEye.y;
+ self.targetRotation = Math.atan2(dy, dx);
+ }
}
+ // Apply smoothing to position
+ self.x += (self.targetX - self.x) * self.smoothingFactor;
+ self.y += (self.targetY - self.y) * self.smoothingFactor;
+ // Apply smoothing to rotation
+ self.rotation += (self.targetRotation - self.rotation) * self.rotationSmoothingFactor;
};
return self;
});
var Enemy = Container.expand(function (type) {
A clear blue sky background with no clouds.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
a small bush. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
a wooden arrow with red feathers and a metal arrow head. Completely vertical orientation. Cartoon. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A small vertical flame. Cartoon. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
a black scorch mark on the ground left by a meteor impact. cartoon. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A bright spark. Cartoon. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
a red heart. cartoon. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
An SVG of the word **BOSS** in sharp red font.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
An SVG of the word “Start” written in fire. Cartoon.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows