/****
* 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());
// 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
@@ -1,6 +1,251 @@
-/****
+/****
+* 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: 0x000000
-});
\ No newline at end of file
+ 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());
+ // 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;
+};
\ No newline at end of file