/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var facekit = LK.import("@upit/facekit.v1"); /**** * Classes ****/ // Player character controlled by face var FaceChar = Container.expand(function () { var self = Container.call(this); var _char = self.attachAsset('faceChar', { anchorX: 0.5, anchorY: 0.5 }); // For hit feedback self.flash = function () { tween(self, { alpha: 0.3 }, { duration: 80, onFinish: function onFinish() { tween(self, { alpha: 1 }, { duration: 120 }); } }); }; return self; }); // Obstacle class var Obstacle = Container.expand(function () { var self = Container.call(this); var obs = self.attachAsset('obstacle', { anchorX: 0.5, anchorY: 0.5 }); // Speed is set on creation self.speed = 0; self.update = function () { self.y += self.speed; }; return self; }); // Point collectible class var PointItem = Container.expand(function () { var self = Container.call(this); var pt = self.attachAsset('point', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 0; self.update = function () { self.y += self.speed; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x181c24 }); /**** * Game Code ****/ /* We use simple shapes for the character, obstacles, and points. */ // Game area: 2048x2732 // Center Y for spawn var GAME_W = 2048; var GAME_H = 2732; // Main character var faceChar = new FaceChar(); faceChar.x = GAME_W / 2; faceChar.y = GAME_H - 400; game.addChild(faceChar); // Arrays for obstacles and points var obstacles = []; var points = []; // Score display var scoreTxt = new Text2('0', { size: 120, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Best score display (optional, not persistent) var bestScore = 0; // Game state var isDead = false; var lastScore = 0; // Spawning timers var obstacleTimer = 0; var pointTimer = 0; // For face smoothing var lastFaceX = faceChar.x; var lastFaceY = faceChar.y; // Helper: clamp function clamp(val, min, max) { return Math.max(min, Math.min(max, val)); } // Main update loop game.update = function () { if (isDead) return; // --- Face tracking movement --- // Use mouthCenter if available, else fallback to noseTip var fx = facekit.mouthCenter && facekit.mouthCenter.x ? facekit.mouthCenter.x : facekit.noseTip && facekit.noseTip.x ? facekit.noseTip.x : GAME_W / 2; var fy = facekit.mouthCenter && facekit.mouthCenter.y ? facekit.mouthCenter.y : facekit.noseTip && facekit.noseTip.y ? facekit.noseTip.y : GAME_H - 400; // Clamp to game area, keep character fully visible var charR = faceChar.width / 2; fx = clamp(fx, charR, GAME_W - charR); fy = clamp(fy, charR + 120, GAME_H - charR - 40); // Smooth movement (lerp) lastFaceX += (fx - lastFaceX) * 0.35; lastFaceY += (fy - lastFaceY) * 0.35; faceChar.x = lastFaceX; faceChar.y = lastFaceY; // --- Spawning obstacles --- obstacleTimer--; if (obstacleTimer <= 0) { var obs = new Obstacle(); // Random X, avoid left 100px (menu) var minX = 120 + obs.width / 2; var maxX = GAME_W - obs.width / 2; obs.x = minX + Math.random() * (maxX - minX); obs.y = -obs.height / 2; // Speed increases with score obs.speed = 12 + LK.getScore() * 0.12; obstacles.push(obs); game.addChild(obs); // Next spawn: 36-60 ticks obstacleTimer = 36 + Math.floor(Math.random() * 24); } // --- Spawning points --- pointTimer--; if (pointTimer <= 0) { var pt = new PointItem(); var minX = 120 + pt.width / 2; var maxX = GAME_W - pt.width / 2; pt.x = minX + Math.random() * (maxX - minX); pt.y = -pt.height / 2; pt.speed = 10 + LK.getScore() * 0.10; points.push(pt); game.addChild(pt); // Next spawn: 60-120 ticks pointTimer = 60 + Math.floor(Math.random() * 60); } // --- Update obstacles --- for (var i = obstacles.length - 1; i >= 0; i--) { var o = obstacles[i]; o.update(); // Remove if off screen if (o.y - o.height / 2 > GAME_H + 40) { o.destroy(); obstacles.splice(i, 1); continue; } // Collision with player if (o.intersects(faceChar)) { // Game over isDead = true; faceChar.flash(); LK.effects.flashScreen(0xff0000, 800); LK.showGameOver(); return; } } // --- Update points --- for (var j = points.length - 1; j >= 0; j--) { var p = points[j]; p.update(); // Remove if off screen if (p.y - p.height / 2 > GAME_H + 40) { p.destroy(); points.splice(j, 1); continue; } // Collect if (p.intersects(faceChar)) { LK.setScore(LK.getScore() + 1); scoreTxt.setText(LK.getScore()); // Play point collect sound LK.getSound('pointCollect').play(); // Feedback tween(p, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 180, onFinish: function onFinish() { p.destroy(); } }); points.splice(j, 1); continue; } } }; // Reset state on new game 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 = []; isDead = false; LK.setScore(0); scoreTxt.setText('0'); // Reset faceChar position faceChar.x = GAME_W / 2; faceChar.y = GAME_H - 400; lastFaceX = faceChar.x; lastFaceY = faceChar.y; obstacleTimer = 30; pointTimer = 60; }); // Also reset on game over (for safety) game.on('gameover', function () { isDead = true; }); // Touch fallback: allow dragging character if facekit not available var dragNode = null; function handleMove(x, y, obj) { if (dragNode) { // Clamp to game area var charR = faceChar.width / 2; var nx = clamp(x, charR, GAME_W - charR); var ny = clamp(y, charR + 120, GAME_H - charR - 40); faceChar.x = nx; faceChar.y = ny; lastFaceX = nx; lastFaceY = ny; } } game.move = handleMove; game.down = function (x, y, obj) { // Only allow drag if facekit not available if (!facekit.mouthCenter || !facekit.mouthCenter.x) { dragNode = faceChar; handleMove(x, y, obj); } }; game.up = function (x, y, obj) { dragNode = null; };
===================================================================
--- original.js
+++ change.js
@@ -185,8 +185,10 @@
// Collect
if (p.intersects(faceChar)) {
LK.setScore(LK.getScore() + 1);
scoreTxt.setText(LK.getScore());
+ // Play point collect sound
+ LK.getSound('pointCollect').play();
// Feedback
tween(p, {
scaleX: 1.5,
scaleY: 1.5,