/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var facekit = LK.import("@upit/facekit.v1");
/****
* Classes
****/
// Bonus class: falls from top, destroys itself when off screen
var Bonus = Container.expand(function () {
var self = Container.call(this);
var gfx = self.attachAsset('bonus', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 10; // Will be increased as game progresses
self.update = function () {
self.y += self.speed;
};
return self;
});
// Obstacle class: falls from top, destroys itself when off screen
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var gfx = 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: follows face position
var Player = Container.expand(function () {
var self = Container.call(this);
var gfx = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
// No update needed; position is set by facekit in main game loop
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Player: blue ellipse
// Bonus: yellow ellipse
// Obstacles: gray boxes
// Game variables
var player = new Player();
game.addChild(player);
player.x = 2048 / 2;
player.y = 2732 - 350; // Start near bottom
var obstacles = [];
var bonuses = [];
var score = 0;
var bonusScore = 0;
var gameOver = false;
var ticksSurvived = 0;
// Score text
var scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Bonus text
var bonusTxt = new Text2('', {
size: 80,
fill: 0xFFE066
});
bonusTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(bonusTxt);
bonusTxt.y = 130;
// Difficulty progression
var obstacleInterval = 60; // ticks between obstacles (start: 1/sec)
var bonusInterval = 180; // ticks between bonuses (start: 1/3sec)
var minObstacleInterval = 18; // fastest: ~3/sec
var minBonusInterval = 240; // slowest: ~1/4sec
var lastObstacleTick = 0;
var lastBonusTick = 0;
var obstacleSpeed = 12;
var bonusSpeed = 10;
var speedIncreaseEvery = 600; // every 10 seconds
// Main update loop
game.update = function () {
if (gameOver) return;
// Update player position from facekit
// Clamp to game area
var fx = facekit.mouthCenter && facekit.mouthCenter.x ? facekit.mouthCenter.x : 2048 / 2;
var fy = facekit.mouthCenter && facekit.mouthCenter.y ? facekit.mouthCenter.y : 2732 - 350;
// Keep player inside bounds
var px = Math.max(player.width / 2, Math.min(2048 - player.width / 2, fx));
var py = Math.max(player.height / 2 + 100, Math.min(2732 - player.height / 2, fy));
player.x = px;
player.y = py;
// Spawn obstacles
if (LK.ticks - lastObstacleTick >= obstacleInterval) {
var obs = new Obstacle();
obs.x = Math.random() * (2048 - 200) + 100;
obs.y = -obs.height / 2;
obs.speed = obstacleSpeed;
obstacles.push(obs);
game.addChild(obs);
lastObstacleTick = LK.ticks;
}
// Spawn bonuses
if (LK.ticks - lastBonusTick >= bonusInterval) {
// 60% chance to spawn a bonus
if (Math.random() < 0.6) {
var bon = new Bonus();
bon.x = Math.random() * (2048 - 160) + 80;
bon.y = -bon.height / 2;
bon.speed = bonusSpeed;
bonuses.push(bon);
game.addChild(bon);
}
lastBonusTick = LK.ticks;
}
// Increase difficulty over time
if (LK.ticks % speedIncreaseEvery === 0 && LK.ticks > 0) {
if (obstacleInterval > minObstacleInterval) obstacleInterval -= 4;
if (bonusInterval < minBonusInterval) bonusInterval += 8;
obstacleSpeed += 1.5;
bonusSpeed += 1;
}
// Update obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
var obs = obstacles[i];
obs.update();
// Remove if off screen
if (obs.y - obs.height / 2 > 2732 + 50) {
obs.destroy();
obstacles.splice(i, 1);
continue;
}
// Collision with player
if (obs.intersects(player)) {
// Game over
LK.effects.flashScreen(0xff0000, 800);
gameOver = true;
tween(player, {
alpha: 0.2
}, {
duration: 400,
easing: tween.easeOut
});
LK.setTimeout(function () {
LK.showGameOver();
}, 900);
return;
}
}
// Update bonuses
for (var j = bonuses.length - 1; j >= 0; j--) {
var bon = bonuses[j];
bon.update();
// Remove if off screen
if (bon.y - bon.height / 2 > 2732 + 50) {
bon.destroy();
bonuses.splice(j, 1);
continue;
}
// Collision with player
if (bon.intersects(player)) {
bonusScore += 1;
score += 5;
scoreTxt.setText(score);
bonusTxt.setText('Bonuses: ' + bonusScore);
// Flash bonus
LK.effects.flashObject(bon, 0xffff00, 300);
bon.destroy();
bonuses.splice(j, 1);
continue;
}
}
// Score: 1 point per 30 ticks survived
ticksSurvived++;
if (ticksSurvived % 30 === 0) {
score += 1;
scoreTxt.setText(score);
}
};
// Reset game state on restart
game.on('reset', function () {
// Remove all obstacles and bonuses
for (var i = 0; i < obstacles.length; i++) {
obstacles[i].destroy();
}
for (var j = 0; j < bonuses.length; j++) {
bonuses[j].destroy();
}
obstacles = [];
bonuses = [];
score = 0;
bonusScore = 0;
ticksSurvived = 0;
gameOver = false;
player.x = 2048 / 2;
player.y = 2732 - 350;
scoreTxt.setText('0');
bonusTxt.setText('');
obstacleInterval = 60;
bonusInterval = 180;
obstacleSpeed = 12;
bonusSpeed = 10;
lastObstacleTick = 0;
lastBonusTick = 0;
});
// Show camera background
game.setBackgroundColor(0x000000); // Camera will show behind game elements
// No need to handle music, pause, or leaderboard ===================================================================
--- original.js
+++ change.js