/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var facekit = LK.import("@upit/facekit.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ // Obstacle class var Obstacle = Container.expand(function () { var self = Container.call(this); var obsAsset = self.attachAsset('obstacle', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 12; // Will be increased as game progresses self.update = function () { self.y += self.speed; }; return self; }); // Player class var Player = Container.expand(function () { var self = Container.call(this); var playerAsset = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); // For hit feedback self.flash = function () { tween(self, { alpha: 0.5 }, { duration: 80, onFinish: function onFinish() { tween(self, { alpha: 1 }, { duration: 120 }); } }); }; return self; }); // Point collectible class var PointItem = Container.expand(function () { var self = Container.call(this); var pointAsset = self.attachAsset('point', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 12; self.update = function () { self.y += self.speed; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x181c2c }); /**** * Game Code ****/ // Game area: 2048x2732 // Player character: a bright ellipse // Obstacle: a red box // Point collectible: a yellow ellipse // Sound for collecting a point // Sound for hitting an obstacle // Music (background, optional) game.setBackgroundColor(0x181c2c); // Score display var scoreTxt = new Text2('0', { size: 120, fill: 0xFFF700 }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // High score variable and display var highScore = storage.highScore || 0; var highScoreTxt = new Text2('Best Score: ' + highScore, { size: 60, fill: 0xFFFFFF }); highScoreTxt.anchor.set(0.5, 0); highScoreTxt.y = scoreTxt.height + 10; LK.gui.top.addChild(highScoreTxt); // Start music LK.playMusic('bgmusic', { fade: { start: 0, end: 1, duration: 1200 } }); // Player setup var player = new Player(); game.addChild(player); // Start in lower center player.x = 2048 / 2; player.y = 2732 - 350; // Arrays for obstacles and points var obstacles = []; var points = []; // Game state var score = 0; var ticks = 0; var gameSpeed = 12; // Initial speed var spawnInterval = 60; // Ticks between spawns (1s at 60fps) var lastObstacleSpawn = 0; var lastPointSpawn = 0; var isGameOver = false; // Helper: clamp function clamp(val, min, max) { return val < min ? min : val > max ? max : val; } // Face control logic function updatePlayerPosition() { // Use facekit.mouthCenter if available, else fallback to noseTip or center var fx = facekit.mouthCenter && facekit.mouthCenter.x ? facekit.mouthCenter.x : facekit.noseTip && facekit.noseTip.x ? facekit.noseTip.x : 1024; var fy = facekit.mouthCenter && facekit.mouthCenter.y ? facekit.mouthCenter.y : facekit.noseTip && facekit.noseTip.y ? facekit.noseTip.y : 2048; // Clamp to game area, keep player fully visible var px = clamp(fx, player.width / 2, 2048 - player.width / 2); var py = clamp(fy, 2732 - 600, 2732 - player.height / 2); // Only allow movement in lower 600px // Smooth movement tween.stop(player, { x: true, y: true }); tween(player, { x: px, y: py }, { duration: 80, easing: tween.easeOut }); } // Spawn obstacle at random x function spawnObstacle() { var obs = new Obstacle(); obs.speed = gameSpeed; obs.x = clamp(200 + Math.floor(Math.random() * (2048 - 400)), obs.width / 2, 2048 - obs.width / 2); obs.y = -obs.height / 2; obstacles.push(obs); game.addChild(obs); } // Spawn point at random x function spawnPoint() { var pt = new PointItem(); pt.speed = gameSpeed; pt.x = clamp(200 + Math.floor(Math.random() * (2048 - 400)), pt.width / 2, 2048 - pt.width / 2); pt.y = -pt.height / 2; points.push(pt); game.addChild(pt); } // Game update loop game.update = function () { if (isGameOver) return; ticks++; // Increase speed every 10 seconds if (ticks % 600 === 0 && gameSpeed < 32) { gameSpeed += 2; if (spawnInterval > 30) spawnInterval -= 4; } // Face control: update player position updatePlayerPosition(); // Microphone: if volume > 0.5, give a small speed boost (visual feedback) if (facekit.volume > 0.5) { player.scale.x = 1.18; player.scale.y = 1.18; } else { player.scale.x = 1; player.scale.y = 1; } // Spawn obstacles if (ticks - lastObstacleSpawn > spawnInterval) { spawnObstacle(); lastObstacleSpawn = ticks; } // Spawn points less frequently if (ticks - lastPointSpawn > spawnInterval * 1.5) { spawnPoint(); lastPointSpawn = ticks; } // Update obstacles for (var i = obstacles.length - 1; i >= 0; i--) { var obs = obstacles[i]; obs.speed = gameSpeed; obs.update(); // Remove if off screen if (obs.y - obs.height / 2 > 2732 + 100) { obs.destroy(); obstacles.splice(i, 1); continue; } // Collision with player if (player.intersects(obs)) { // Hit! isGameOver = true; LK.getSound('blast').play(); LK.getSound('hit').play(); player.flash(); // Explosion effect at collision point (simulate with flashObject) LK.effects.flashObject(obs, 0xffa500, 700); LK.effects.flashScreen(0xff0000, 800); // Play hit sound when game ends LK.getSound('hit').play(); LK.showGameOver(); return; } } // Update points for (var j = points.length - 1; j >= 0; j--) { var pt = points[j]; pt.speed = gameSpeed; pt.update(); // Remove if off screen if (pt.y - pt.height / 2 > 2732 + 100) { pt.destroy(); points.splice(j, 1); continue; } // Collect if (player.intersects(pt)) { score += 1; LK.setScore(score); scoreTxt.setText(score); // High score update if (score > highScore) { highScore = score; storage.highScore = highScore; highScoreTxt.setText('Best Score: ' + highScore); } LK.getSound('collect').play(); // Visual feedback tween(pt, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 180, onFinish: function onFinish() { pt.destroy(); } }); points.splice(j, 1); continue; } } }; // Reset on game restart game.on('reset', function () { // Remove all obstacles and points for (var i = 0; i < obstacles.length; i++) obstacles[i].destroy(); for (var j = 0; j < points.length; j++) points[j].destroy(); obstacles = []; points = []; score = 0; LK.setScore(0); scoreTxt.setText('0'); // Reset high score display (do not reset value) highScore = storage.highScore || highScore; highScoreTxt.setText('Best Score: ' + highScore); player.x = 2048 / 2; player.y = 2732 - 350; gameSpeed = 12; spawnInterval = 60; lastObstacleSpawn = 0; lastPointSpawn = 0; ticks = 0; isGameOver = false; }); // No touch/mouse controls: face only, so no game.down/game.move/game.up // Show instructions overlay for first 2 seconds var instructTxt = new Text2('Move your face to dodge! Open mouth for boost.', { size: 80, fill: 0xFFFFFF }); instructTxt.anchor.set(0.5, 0.5); instructTxt.x = 2048 / 2; instructTxt.y = 2732 / 2 - 200; game.addChild(instructTxt); tween(instructTxt, { alpha: 0 }, { duration: 2000, onFinish: function onFinish() { instructTxt.destroy(); } });
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var facekit = LK.import("@upit/facekit.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// Obstacle class
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obsAsset = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 12; // Will be increased as game progresses
self.update = function () {
self.y += self.speed;
};
return self;
});
// Player class
var Player = Container.expand(function () {
var self = Container.call(this);
var playerAsset = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
// For hit feedback
self.flash = function () {
tween(self, {
alpha: 0.5
}, {
duration: 80,
onFinish: function onFinish() {
tween(self, {
alpha: 1
}, {
duration: 120
});
}
});
};
return self;
});
// Point collectible class
var PointItem = Container.expand(function () {
var self = Container.call(this);
var pointAsset = self.attachAsset('point', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 12;
self.update = function () {
self.y += self.speed;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181c2c
});
/****
* Game Code
****/
// Game area: 2048x2732
// Player character: a bright ellipse
// Obstacle: a red box
// Point collectible: a yellow ellipse
// Sound for collecting a point
// Sound for hitting an obstacle
// Music (background, optional)
game.setBackgroundColor(0x181c2c);
// Score display
var scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFF700
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// High score variable and display
var highScore = storage.highScore || 0;
var highScoreTxt = new Text2('Best Score: ' + highScore, {
size: 60,
fill: 0xFFFFFF
});
highScoreTxt.anchor.set(0.5, 0);
highScoreTxt.y = scoreTxt.height + 10;
LK.gui.top.addChild(highScoreTxt);
// Start music
LK.playMusic('bgmusic', {
fade: {
start: 0,
end: 1,
duration: 1200
}
});
// Player setup
var player = new Player();
game.addChild(player);
// Start in lower center
player.x = 2048 / 2;
player.y = 2732 - 350;
// Arrays for obstacles and points
var obstacles = [];
var points = [];
// Game state
var score = 0;
var ticks = 0;
var gameSpeed = 12; // Initial speed
var spawnInterval = 60; // Ticks between spawns (1s at 60fps)
var lastObstacleSpawn = 0;
var lastPointSpawn = 0;
var isGameOver = false;
// Helper: clamp
function clamp(val, min, max) {
return val < min ? min : val > max ? max : val;
}
// Face control logic
function updatePlayerPosition() {
// Use facekit.mouthCenter if available, else fallback to noseTip or center
var fx = facekit.mouthCenter && facekit.mouthCenter.x ? facekit.mouthCenter.x : facekit.noseTip && facekit.noseTip.x ? facekit.noseTip.x : 1024;
var fy = facekit.mouthCenter && facekit.mouthCenter.y ? facekit.mouthCenter.y : facekit.noseTip && facekit.noseTip.y ? facekit.noseTip.y : 2048;
// Clamp to game area, keep player fully visible
var px = clamp(fx, player.width / 2, 2048 - player.width / 2);
var py = clamp(fy, 2732 - 600, 2732 - player.height / 2); // Only allow movement in lower 600px
// Smooth movement
tween.stop(player, {
x: true,
y: true
});
tween(player, {
x: px,
y: py
}, {
duration: 80,
easing: tween.easeOut
});
}
// Spawn obstacle at random x
function spawnObstacle() {
var obs = new Obstacle();
obs.speed = gameSpeed;
obs.x = clamp(200 + Math.floor(Math.random() * (2048 - 400)), obs.width / 2, 2048 - obs.width / 2);
obs.y = -obs.height / 2;
obstacles.push(obs);
game.addChild(obs);
}
// Spawn point at random x
function spawnPoint() {
var pt = new PointItem();
pt.speed = gameSpeed;
pt.x = clamp(200 + Math.floor(Math.random() * (2048 - 400)), pt.width / 2, 2048 - pt.width / 2);
pt.y = -pt.height / 2;
points.push(pt);
game.addChild(pt);
}
// Game update loop
game.update = function () {
if (isGameOver) return;
ticks++;
// Increase speed every 10 seconds
if (ticks % 600 === 0 && gameSpeed < 32) {
gameSpeed += 2;
if (spawnInterval > 30) spawnInterval -= 4;
}
// Face control: update player position
updatePlayerPosition();
// Microphone: if volume > 0.5, give a small speed boost (visual feedback)
if (facekit.volume > 0.5) {
player.scale.x = 1.18;
player.scale.y = 1.18;
} else {
player.scale.x = 1;
player.scale.y = 1;
}
// Spawn obstacles
if (ticks - lastObstacleSpawn > spawnInterval) {
spawnObstacle();
lastObstacleSpawn = ticks;
}
// Spawn points less frequently
if (ticks - lastPointSpawn > spawnInterval * 1.5) {
spawnPoint();
lastPointSpawn = ticks;
}
// Update obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
var obs = obstacles[i];
obs.speed = gameSpeed;
obs.update();
// Remove if off screen
if (obs.y - obs.height / 2 > 2732 + 100) {
obs.destroy();
obstacles.splice(i, 1);
continue;
}
// Collision with player
if (player.intersects(obs)) {
// Hit!
isGameOver = true;
LK.getSound('blast').play();
LK.getSound('hit').play();
player.flash();
// Explosion effect at collision point (simulate with flashObject)
LK.effects.flashObject(obs, 0xffa500, 700);
LK.effects.flashScreen(0xff0000, 800);
// Play hit sound when game ends
LK.getSound('hit').play();
LK.showGameOver();
return;
}
}
// Update points
for (var j = points.length - 1; j >= 0; j--) {
var pt = points[j];
pt.speed = gameSpeed;
pt.update();
// Remove if off screen
if (pt.y - pt.height / 2 > 2732 + 100) {
pt.destroy();
points.splice(j, 1);
continue;
}
// Collect
if (player.intersects(pt)) {
score += 1;
LK.setScore(score);
scoreTxt.setText(score);
// High score update
if (score > highScore) {
highScore = score;
storage.highScore = highScore;
highScoreTxt.setText('Best Score: ' + highScore);
}
LK.getSound('collect').play();
// Visual feedback
tween(pt, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 180,
onFinish: function onFinish() {
pt.destroy();
}
});
points.splice(j, 1);
continue;
}
}
};
// Reset on game restart
game.on('reset', function () {
// Remove all obstacles and points
for (var i = 0; i < obstacles.length; i++) obstacles[i].destroy();
for (var j = 0; j < points.length; j++) points[j].destroy();
obstacles = [];
points = [];
score = 0;
LK.setScore(0);
scoreTxt.setText('0');
// Reset high score display (do not reset value)
highScore = storage.highScore || highScore;
highScoreTxt.setText('Best Score: ' + highScore);
player.x = 2048 / 2;
player.y = 2732 - 350;
gameSpeed = 12;
spawnInterval = 60;
lastObstacleSpawn = 0;
lastPointSpawn = 0;
ticks = 0;
isGameOver = false;
});
// No touch/mouse controls: face only, so no game.down/game.move/game.up
// Show instructions overlay for first 2 seconds
var instructTxt = new Text2('Move your face to dodge! Open mouth for boost.', {
size: 80,
fill: 0xFFFFFF
});
instructTxt.anchor.set(0.5, 0.5);
instructTxt.x = 2048 / 2;
instructTxt.y = 2732 / 2 - 200;
game.addChild(instructTxt);
tween(instructTxt, {
alpha: 0
}, {
duration: 2000,
onFinish: function onFinish() {
instructTxt.destroy();
}
});