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
});
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;
}
};
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
@@ -152,9 +152,9 @@
self.update = function () {
if (!self.active) {
return;
}
- self.y -= self.speed;
+ self.y += self.speed;
// Create fire trail particles
if (Math.random() < 0.3) {
createFireParticle(self.x + (Math.random() * 40 - 20), self.y + 20);
}
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