/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highscore: 0,
level: 1
});
var facekit = LK.import("@upit/facekit.v1");
/****
* Classes
****/
var Monster = Container.expand(function () {
var self = Container.call(this);
// Create monster visuals
var monsterGraphics = self.attachAsset('monster', {
anchorX: 0.5,
anchorY: 0.5
});
// Monster properties
self.speed = 2 + Math.random() * 3;
self.targetX = 0;
self.targetY = 0;
self.isEaten = false;
self.directionChangeTimer = 0;
self.directionChangeFrequency = 30; // frames
self.value = 10; // score value
// Set random color tint for variety
var colors = [0xffc0cb, 0xffb6c1, 0xffd700, 0xadd8e6, 0x98fb98];
monsterGraphics.tint = colors[Math.floor(Math.random() * colors.length)];
self.update = function () {
if (self.isEaten) {
return;
}
self.directionChangeTimer++;
// Update targets periodically for more natural movement
if (self.directionChangeTimer >= self.directionChangeFrequency) {
self.directionChangeTimer = 0;
// When mouth is closed, monsters flee from player
if (!facekit.mouthOpen) {
// Run away from player
var playerPos = {
x: facekit.mouthCenter.x,
y: facekit.mouthCenter.y
};
var angle = Math.atan2(self.y - playerPos.y, self.x - playerPos.x);
self.targetX = self.x + Math.cos(angle) * 300;
self.targetY = self.y + Math.sin(angle) * 300;
// Keep within bounds
if (self.targetX < 100) {
self.targetX = 100;
}
if (self.targetX > 2048 - 100) {
self.targetX = 2048 - 100;
}
if (self.targetY < 100) {
self.targetY = 100;
}
if (self.targetY > 2732 - 100) {
self.targetY = 2732 - 100;
}
} else {
// Random movement when mouth is open
self.targetX = 100 + Math.random() * (2048 - 200);
self.targetY = 100 + Math.random() * (2732 - 200);
}
}
// Move toward target position
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
};
self.eat = function () {
if (self.isEaten) {
return;
}
self.isEaten = true;
// Tween to player's mouth with cute animation
tween(self, {
x: facekit.mouthCenter.x,
y: facekit.mouthCenter.y,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0,
rotation: Math.PI * 2 // Add a spin for cuteness
}, {
duration: 300,
easing: tween.easeIn,
onFinish: function onFinish() {
self.destroy();
}
});
return self.value;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// Game variables
var monsters = [];
var score = 0;
var highscore = storage.highscore || 0;
var level = storage.level || 1;
var monstersToSpawn = 5;
var monstersPerLevel = 5;
var monsterSpawnTimer = 0;
var monsterSpawnDelay = 60; // frames
var mouthPosition = {
x: 0,
y: 0
};
var isEatingMode = false;
var lastMouthState = false;
var gameActive = true;
// Play cute background music
LK.playMusic('cuteGameMusic', {
fade: {
start: 0,
end: 0.5,
duration: 1000
}
});
// Create player visualization (follows mouth)
var player = game.addChild(LK.getAsset('player', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7
}));
// UI elements
var scoreText = new Text2('Score: 0', {
size: 50,
fill: 0xFFFFFF
});
var scoreBackground = LK.getAsset('scoreBackground', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.5
});
scoreText.anchor.set(0.5, 0.5);
LK.gui.topRight.addChild(scoreBackground);
LK.gui.topRight.addChild(scoreText);
var levelText = new Text2('Level: 1', {
size: 50,
fill: 0xFFFFFF
});
var levelBackground = LK.getAsset('levelBackground', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.5
});
levelText.anchor.set(0.5, 0.5);
LK.gui.topLeft.addChild(levelBackground);
LK.gui.topLeft.addChild(levelText);
// Position UI elements
scoreBackground.x = -250;
scoreBackground.y = 50;
scoreText.x = -250;
scoreText.y = 50;
levelBackground.x = 250;
levelBackground.y = 50;
levelText.x = 250;
levelText.y = 50;
// Disclaimer text
var disclaimerText = new Text2("Make sure you are in an open and well-lighted environment", {
size: 60,
fill: 0xFF0000
});
disclaimerText.anchor.set(0.5, 0.5);
disclaimerText.y = 150; // Lower the disclaimer text position further
LK.gui.top.addChild(disclaimerText);
// Instructions text
var instructionsText = new Text2("Open your mouth to eat monsters!\nClose it to make them run away!", {
size: 60,
fill: 0xFFFFFF
});
instructionsText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(instructionsText);
// Hide instructions after 5 seconds
LK.setTimeout(function () {
tween(instructionsText, {
alpha: 0
}, {
duration: 1000,
onFinish: function onFinish() {
instructionsText.destroy();
}
});
}, 5000);
// Helper function to spawn a monster
function spawnMonster() {
var monster = new Monster();
// Random position along the edge of the screen
var side = Math.floor(Math.random() * 4);
switch (side) {
case 0:
// top
monster.x = Math.random() * 2048;
monster.y = 0;
break;
case 1:
// right
monster.x = 2048;
monster.y = Math.random() * 2732;
break;
case 2:
// bottom
monster.x = Math.random() * 2048;
monster.y = 2732;
break;
case 3:
// left
monster.x = 0;
monster.y = Math.random() * 2732;
break;
}
// Speed up monsters based on level
monster.speed += (level - 1) * 0.5;
// Increase score value based on level
monster.value = 10 * level;
monsters.push(monster);
game.addChild(monster);
return monster;
}
// Helper function to start the next level
function startNextLevel() {
level++;
levelText.setText("Level: " + level);
monstersToSpawn = level * monstersPerLevel;
// Store level progress
storage.level = level;
// Play level up sound
LK.getSound('levelUp').play();
// Show level up message
var levelUpText = new Text2("Level " + level + "!", {
size: 120,
fill: 0xFFFF00
});
levelUpText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(levelUpText);
// Animate level up text
tween(levelUpText, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(levelUpText, {
scaleX: 1,
scaleY: 1,
alpha: 0
}, {
duration: 500,
easing: tween.easeIn,
onFinish: function onFinish() {
levelUpText.destroy();
}
});
}
});
}
// Game update function
game.update = function () {
// Update mouth position from facekit
mouthPosition.x = facekit.mouthCenter.x;
mouthPosition.y = facekit.mouthCenter.y;
isEatingMode = facekit.mouthOpen;
// Update player position to follow mouth
player.x = mouthPosition.x;
player.y = mouthPosition.y;
// Visual feedback for mouth state
if (isEatingMode) {
// Mouth open - eating mode
tween.stop(player, {
scaleX: true,
scaleY: true
});
player.scaleX = 1.5;
player.scaleY = 1.5;
player.tint = 0x00ff00;
} else {
// Mouth closed - neutral mode
tween.stop(player, {
scaleX: true,
scaleY: true
});
player.scaleX = 1;
player.scaleY = 1;
player.tint = 0x4287f5;
}
// Track mouth state changes
if (isEatingMode !== lastMouthState) {
lastMouthState = isEatingMode;
}
// Spawn monsters
if (monstersToSpawn > 0 && monsters.length < 10) {
monsterSpawnTimer++;
if (monsterSpawnTimer >= monsterSpawnDelay) {
monsterSpawnTimer = 0;
spawnMonster();
monstersToSpawn--;
}
}
// Check if level is complete
if (monstersToSpawn === 0 && monsters.length === 0) {
startNextLevel();
}
// Update all monsters
for (var i = monsters.length - 1; i >= 0; i--) {
var monster = monsters[i];
// Detect if monster is eaten
if (isEatingMode && !monster.isEaten) {
var dx = monster.x - mouthPosition.x;
var dy = monster.y - mouthPosition.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < player.width / 2) {
// Monster is eaten
score += monster.eat();
scoreText.setText("Score: " + score);
// Update highscore if needed
if (score > highscore) {
highscore = score;
storage.highscore = highscore;
}
// Play cute munch sound
LK.getSound('cuteMunch').play();
// Remove from array
monsters.splice(i, 1);
}
}
}
};
// Game click/touch handlers (for testing on desktop)
game.down = function (x, y, obj) {
// Not needed for face tracking game, but could be used for additional controls
};
game.up = function (x, y, obj) {
// Not needed for face tracking game
};
game.move = function (x, y, obj) {
// Not needed for face tracking game
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highscore: 0,
level: 1
});
var facekit = LK.import("@upit/facekit.v1");
/****
* Classes
****/
var Monster = Container.expand(function () {
var self = Container.call(this);
// Create monster visuals
var monsterGraphics = self.attachAsset('monster', {
anchorX: 0.5,
anchorY: 0.5
});
// Monster properties
self.speed = 2 + Math.random() * 3;
self.targetX = 0;
self.targetY = 0;
self.isEaten = false;
self.directionChangeTimer = 0;
self.directionChangeFrequency = 30; // frames
self.value = 10; // score value
// Set random color tint for variety
var colors = [0xffc0cb, 0xffb6c1, 0xffd700, 0xadd8e6, 0x98fb98];
monsterGraphics.tint = colors[Math.floor(Math.random() * colors.length)];
self.update = function () {
if (self.isEaten) {
return;
}
self.directionChangeTimer++;
// Update targets periodically for more natural movement
if (self.directionChangeTimer >= self.directionChangeFrequency) {
self.directionChangeTimer = 0;
// When mouth is closed, monsters flee from player
if (!facekit.mouthOpen) {
// Run away from player
var playerPos = {
x: facekit.mouthCenter.x,
y: facekit.mouthCenter.y
};
var angle = Math.atan2(self.y - playerPos.y, self.x - playerPos.x);
self.targetX = self.x + Math.cos(angle) * 300;
self.targetY = self.y + Math.sin(angle) * 300;
// Keep within bounds
if (self.targetX < 100) {
self.targetX = 100;
}
if (self.targetX > 2048 - 100) {
self.targetX = 2048 - 100;
}
if (self.targetY < 100) {
self.targetY = 100;
}
if (self.targetY > 2732 - 100) {
self.targetY = 2732 - 100;
}
} else {
// Random movement when mouth is open
self.targetX = 100 + Math.random() * (2048 - 200);
self.targetY = 100 + Math.random() * (2732 - 200);
}
}
// Move toward target position
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
};
self.eat = function () {
if (self.isEaten) {
return;
}
self.isEaten = true;
// Tween to player's mouth with cute animation
tween(self, {
x: facekit.mouthCenter.x,
y: facekit.mouthCenter.y,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0,
rotation: Math.PI * 2 // Add a spin for cuteness
}, {
duration: 300,
easing: tween.easeIn,
onFinish: function onFinish() {
self.destroy();
}
});
return self.value;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// Game variables
var monsters = [];
var score = 0;
var highscore = storage.highscore || 0;
var level = storage.level || 1;
var monstersToSpawn = 5;
var monstersPerLevel = 5;
var monsterSpawnTimer = 0;
var monsterSpawnDelay = 60; // frames
var mouthPosition = {
x: 0,
y: 0
};
var isEatingMode = false;
var lastMouthState = false;
var gameActive = true;
// Play cute background music
LK.playMusic('cuteGameMusic', {
fade: {
start: 0,
end: 0.5,
duration: 1000
}
});
// Create player visualization (follows mouth)
var player = game.addChild(LK.getAsset('player', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7
}));
// UI elements
var scoreText = new Text2('Score: 0', {
size: 50,
fill: 0xFFFFFF
});
var scoreBackground = LK.getAsset('scoreBackground', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.5
});
scoreText.anchor.set(0.5, 0.5);
LK.gui.topRight.addChild(scoreBackground);
LK.gui.topRight.addChild(scoreText);
var levelText = new Text2('Level: 1', {
size: 50,
fill: 0xFFFFFF
});
var levelBackground = LK.getAsset('levelBackground', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.5
});
levelText.anchor.set(0.5, 0.5);
LK.gui.topLeft.addChild(levelBackground);
LK.gui.topLeft.addChild(levelText);
// Position UI elements
scoreBackground.x = -250;
scoreBackground.y = 50;
scoreText.x = -250;
scoreText.y = 50;
levelBackground.x = 250;
levelBackground.y = 50;
levelText.x = 250;
levelText.y = 50;
// Disclaimer text
var disclaimerText = new Text2("Make sure you are in an open and well-lighted environment", {
size: 60,
fill: 0xFF0000
});
disclaimerText.anchor.set(0.5, 0.5);
disclaimerText.y = 150; // Lower the disclaimer text position further
LK.gui.top.addChild(disclaimerText);
// Instructions text
var instructionsText = new Text2("Open your mouth to eat monsters!\nClose it to make them run away!", {
size: 60,
fill: 0xFFFFFF
});
instructionsText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(instructionsText);
// Hide instructions after 5 seconds
LK.setTimeout(function () {
tween(instructionsText, {
alpha: 0
}, {
duration: 1000,
onFinish: function onFinish() {
instructionsText.destroy();
}
});
}, 5000);
// Helper function to spawn a monster
function spawnMonster() {
var monster = new Monster();
// Random position along the edge of the screen
var side = Math.floor(Math.random() * 4);
switch (side) {
case 0:
// top
monster.x = Math.random() * 2048;
monster.y = 0;
break;
case 1:
// right
monster.x = 2048;
monster.y = Math.random() * 2732;
break;
case 2:
// bottom
monster.x = Math.random() * 2048;
monster.y = 2732;
break;
case 3:
// left
monster.x = 0;
monster.y = Math.random() * 2732;
break;
}
// Speed up monsters based on level
monster.speed += (level - 1) * 0.5;
// Increase score value based on level
monster.value = 10 * level;
monsters.push(monster);
game.addChild(monster);
return monster;
}
// Helper function to start the next level
function startNextLevel() {
level++;
levelText.setText("Level: " + level);
monstersToSpawn = level * monstersPerLevel;
// Store level progress
storage.level = level;
// Play level up sound
LK.getSound('levelUp').play();
// Show level up message
var levelUpText = new Text2("Level " + level + "!", {
size: 120,
fill: 0xFFFF00
});
levelUpText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(levelUpText);
// Animate level up text
tween(levelUpText, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(levelUpText, {
scaleX: 1,
scaleY: 1,
alpha: 0
}, {
duration: 500,
easing: tween.easeIn,
onFinish: function onFinish() {
levelUpText.destroy();
}
});
}
});
}
// Game update function
game.update = function () {
// Update mouth position from facekit
mouthPosition.x = facekit.mouthCenter.x;
mouthPosition.y = facekit.mouthCenter.y;
isEatingMode = facekit.mouthOpen;
// Update player position to follow mouth
player.x = mouthPosition.x;
player.y = mouthPosition.y;
// Visual feedback for mouth state
if (isEatingMode) {
// Mouth open - eating mode
tween.stop(player, {
scaleX: true,
scaleY: true
});
player.scaleX = 1.5;
player.scaleY = 1.5;
player.tint = 0x00ff00;
} else {
// Mouth closed - neutral mode
tween.stop(player, {
scaleX: true,
scaleY: true
});
player.scaleX = 1;
player.scaleY = 1;
player.tint = 0x4287f5;
}
// Track mouth state changes
if (isEatingMode !== lastMouthState) {
lastMouthState = isEatingMode;
}
// Spawn monsters
if (monstersToSpawn > 0 && monsters.length < 10) {
monsterSpawnTimer++;
if (monsterSpawnTimer >= monsterSpawnDelay) {
monsterSpawnTimer = 0;
spawnMonster();
monstersToSpawn--;
}
}
// Check if level is complete
if (monstersToSpawn === 0 && monsters.length === 0) {
startNextLevel();
}
// Update all monsters
for (var i = monsters.length - 1; i >= 0; i--) {
var monster = monsters[i];
// Detect if monster is eaten
if (isEatingMode && !monster.isEaten) {
var dx = monster.x - mouthPosition.x;
var dy = monster.y - mouthPosition.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < player.width / 2) {
// Monster is eaten
score += monster.eat();
scoreText.setText("Score: " + score);
// Update highscore if needed
if (score > highscore) {
highscore = score;
storage.highscore = highscore;
}
// Play cute munch sound
LK.getSound('cuteMunch').play();
// Remove from array
monsters.splice(i, 1);
}
}
}
};
// Game click/touch handlers (for testing on desktop)
game.down = function (x, y, obj) {
// Not needed for face tracking game, but could be used for additional controls
};
game.up = function (x, y, obj) {
// Not needed for face tracking game
};
game.move = function (x, y, obj) {
// Not needed for face tracking game
};