/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Plane class var Plane = Container.expand(function () { var self = Container.call(this); var planeSprite = self.attachAsset('plane', { anchorX: 0.5, anchorY: 0.5 }); // Physics self.vy = 0; // vertical velocity self.gravity = 1.2; // gravity per frame self.flapStrength = -22; // negative for upward // Flap method self.flap = function () { self.vy = self.flapStrength; // Animate a quick upward tilt tween.stop(planeSprite, { rotation: true }); planeSprite.rotation = -0.25; tween(planeSprite, { rotation: 0.15 }, { duration: 350, easing: tween.easeOut }); }; // Update method self.update = function () { self.vy += self.gravity; self.y += self.vy; // Clamp rotation based on vy var targetRot = Math.max(-0.3, Math.min(0.4, self.vy / 40)); planeSprite.rotation += (targetRot - planeSprite.rotation) * 0.2; }; return self; }); // TowerPair class (top and bottom towers with a gap) var TowerPair = Container.expand(function () { var self = Container.call(this); // Constants var towerWidth = 220; var towerHeight = 900; var gapHeight = 520; // Top tower var topTower = self.attachAsset('tower', { anchorX: 0.5, anchorY: 1.0 }); // Bottom tower var bottomTower = self.attachAsset('tower', { anchorX: 0.5, anchorY: 0.0 }); // Set positions self.setGapY = function (gapY) { // Clamp gapY so that the gap is always fully on screen, and towers start exactly at the top and bottom var minGapY = gapHeight / 2; var maxGapY = GAME_HEIGHT - GROUND_HEIGHT - gapHeight / 2; if (gapY < minGapY) gapY = minGapY; if (gapY > maxGapY) gapY = maxGapY; // Place top tower so its bottom aligns with the top of the gap, and its top touches the top of the screen topTower.x = 0; topTower.y = 0; topTower.height = gapY - gapHeight / 2; // The anchorY is 1.0, so height stretches from y up to 0 if (topTower.height < 0) topTower.height = 0; // Place bottom tower so its top aligns with the bottom of the gap, and its bottom touches the ground bottomTower.x = 0; bottomTower.y = gapY + gapHeight / 2; bottomTower.height = GAME_HEIGHT - GROUND_HEIGHT - bottomTower.y; if (bottomTower.height < 0) bottomTower.height = 0; }; // For collision detection self.getTopTower = function () { return topTower; }; self.getBottomTower = function () { return bottomTower; }; // For scoring self.passed = false; // Add update method: top tower rotates 180deg, bottom tower is pinned self.update = function () { if (self.lastX === undefined) self.lastX = self.x; // Top tower: rotate 180deg (PI radians) self.getTopTower().rotation = Math.PI; // Bottom tower: no rotation self.getBottomTower().rotation = 0; self.lastX = self.x; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ // No title, no description backgroundColor: 0x87ceeb }); /**** * Game Code ****/ // Sound: hit // Sound: point // Ground: brown box, 2048x120 // Tower: green box, 220x900 // Plane: yellow box, 120x80 // Game constants var GAME_WIDTH = 2048; var GAME_HEIGHT = 2732; var GROUND_HEIGHT = 120; var PLANE_START_X = 600; var PLANE_START_Y = 1100; var TOWER_GAP = 520; var TOWER_WIDTH = 220; var TOWER_HEIGHT = 900; var TOWER_MIN_Y = 600 + TOWER_GAP / 2; var TOWER_MAX_Y = GAME_HEIGHT - GROUND_HEIGHT - 400 - TOWER_GAP / 2; var TOWER_INTERVAL = 700; // horizontal distance between towers var TOWER_SPEED = 14; // px per frame // Game state var plane; var towers = []; var ground; var score = 0; var scoreTxt; var gameStarted = false; var gameOver = false; var lastTowerX = 0; // Add ground ground = LK.getAsset('ground', { anchorX: 0, anchorY: 0, x: 0, y: GAME_HEIGHT - GROUND_HEIGHT }); game.addChild(ground); // Add plane plane = new Plane(); game.addChild(plane); plane.x = PLANE_START_X; plane.y = PLANE_START_Y; // Add score text scoreTxt = new Text2('0', { size: 150, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Helper: reset game state function resetGame() { // Remove towers for (var i = 0; i < towers.length; i++) { towers[i].destroy(); } towers = []; // Reset plane plane.x = PLANE_START_X; plane.y = PLANE_START_Y; plane.vy = 0; // Reset score score = 0; scoreTxt.setText(score); // Reset state gameStarted = false; gameOver = false; lastTowerX = GAME_WIDTH + 600; // Add initial towers for (var i = 0; i < 3; i++) { spawnTower(GAME_WIDTH + 600 + i * TOWER_INTERVAL); } } // Helper: spawn a tower pair at x function spawnTower(x) { var gapY = TOWER_MIN_Y + Math.random() * (TOWER_MAX_Y - TOWER_MIN_Y); var towerPair = new TowerPair(); towerPair.x = x; towerPair.setGapY(gapY); game.addChild(towerPair); towers.push(towerPair); } // Start game resetGame(); // Input: tap anywhere to flap game.down = function (x, y, obj) { if (gameOver) return; if (!gameStarted) { gameStarted = true; } plane.flap(); }; // Main update loop game.update = function () { if (gameOver) return; if (gameStarted) { // Move towers for (var i = towers.length - 1; i >= 0; i--) { var t = towers[i]; t.x -= TOWER_SPEED; // Update tower (for rotation) if (typeof t.update === "function") t.update(); // Remove towers off screen if (t.x < -TOWER_WIDTH) { t.destroy(); towers.splice(i, 1); } } // Spawn new towers if (towers.length === 0 || towers[towers.length - 1].x < GAME_WIDTH - TOWER_INTERVAL) { spawnTower(GAME_WIDTH + TOWER_WIDTH); } // Plane physics plane.update(); // Collision: ground if (plane.y + 40 > GAME_HEIGHT - GROUND_HEIGHT) { plane.y = GAME_HEIGHT - GROUND_HEIGHT - 40; endGame(); return; } // Collision: ceiling if (plane.y - 40 < 0) { plane.y = 40; plane.vy = 0; } // Collision: towers for (var i = 0; i < towers.length; i++) { var t = towers[i]; // Only check if in range if (Math.abs(t.x - plane.x) < TOWER_WIDTH) { // Top if (plane.intersects(t.getTopTower()) || plane.intersects(t.getBottomTower())) { // Explosion effect: flash screen and shake plane LK.effects.flashScreen(0xffcc00, 300); // Fire effect: tint plane orange for 2 seconds var fireTween = tween(plane, { tint: 0xff6600 }, { duration: 200, yoyo: true, repeat: 9 // 10 flashes (200ms * 10 = 2s) }); // Shake plane using tween tween(plane, { x: plane.x + 30 }, { duration: 60, yoyo: true, repeat: 3, onComplete: function onComplete() { plane.x = PLANE_START_X; } }); endGame(); return; } } // Scoring: passed tower if (!t.passed && t.x + TOWER_WIDTH / 2 < plane.x) { t.passed = true; score += 1; scoreTxt.setText(score); LK.getSound('point').play(); } } } else { // Idle: plane bobs up and down plane.y = PLANE_START_Y + Math.sin(LK.ticks / 30) * 30; plane.vy = 0; } }; // End game function endGame() { if (gameOver) return; gameOver = true; LK.getSound('hit').play(); LK.effects.flashScreen(0xff0000, 2000); // After 2 seconds, show game over LK.setTimeout(function () { LK.showGameOver(); }, 2000); } // No drag or move needed for this game
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Plane class
var Plane = Container.expand(function () {
var self = Container.call(this);
var planeSprite = self.attachAsset('plane', {
anchorX: 0.5,
anchorY: 0.5
});
// Physics
self.vy = 0; // vertical velocity
self.gravity = 1.2; // gravity per frame
self.flapStrength = -22; // negative for upward
// Flap method
self.flap = function () {
self.vy = self.flapStrength;
// Animate a quick upward tilt
tween.stop(planeSprite, {
rotation: true
});
planeSprite.rotation = -0.25;
tween(planeSprite, {
rotation: 0.15
}, {
duration: 350,
easing: tween.easeOut
});
};
// Update method
self.update = function () {
self.vy += self.gravity;
self.y += self.vy;
// Clamp rotation based on vy
var targetRot = Math.max(-0.3, Math.min(0.4, self.vy / 40));
planeSprite.rotation += (targetRot - planeSprite.rotation) * 0.2;
};
return self;
});
// TowerPair class (top and bottom towers with a gap)
var TowerPair = Container.expand(function () {
var self = Container.call(this);
// Constants
var towerWidth = 220;
var towerHeight = 900;
var gapHeight = 520;
// Top tower
var topTower = self.attachAsset('tower', {
anchorX: 0.5,
anchorY: 1.0
});
// Bottom tower
var bottomTower = self.attachAsset('tower', {
anchorX: 0.5,
anchorY: 0.0
});
// Set positions
self.setGapY = function (gapY) {
// Clamp gapY so that the gap is always fully on screen, and towers start exactly at the top and bottom
var minGapY = gapHeight / 2;
var maxGapY = GAME_HEIGHT - GROUND_HEIGHT - gapHeight / 2;
if (gapY < minGapY) gapY = minGapY;
if (gapY > maxGapY) gapY = maxGapY;
// Place top tower so its bottom aligns with the top of the gap, and its top touches the top of the screen
topTower.x = 0;
topTower.y = 0;
topTower.height = gapY - gapHeight / 2; // The anchorY is 1.0, so height stretches from y up to 0
if (topTower.height < 0) topTower.height = 0;
// Place bottom tower so its top aligns with the bottom of the gap, and its bottom touches the ground
bottomTower.x = 0;
bottomTower.y = gapY + gapHeight / 2;
bottomTower.height = GAME_HEIGHT - GROUND_HEIGHT - bottomTower.y;
if (bottomTower.height < 0) bottomTower.height = 0;
};
// For collision detection
self.getTopTower = function () {
return topTower;
};
self.getBottomTower = function () {
return bottomTower;
};
// For scoring
self.passed = false;
// Add update method: top tower rotates 180deg, bottom tower is pinned
self.update = function () {
if (self.lastX === undefined) self.lastX = self.x;
// Top tower: rotate 180deg (PI radians)
self.getTopTower().rotation = Math.PI;
// Bottom tower: no rotation
self.getBottomTower().rotation = 0;
self.lastX = self.x;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
// No title, no description
backgroundColor: 0x87ceeb
});
/****
* Game Code
****/
// Sound: hit
// Sound: point
// Ground: brown box, 2048x120
// Tower: green box, 220x900
// Plane: yellow box, 120x80
// Game constants
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var GROUND_HEIGHT = 120;
var PLANE_START_X = 600;
var PLANE_START_Y = 1100;
var TOWER_GAP = 520;
var TOWER_WIDTH = 220;
var TOWER_HEIGHT = 900;
var TOWER_MIN_Y = 600 + TOWER_GAP / 2;
var TOWER_MAX_Y = GAME_HEIGHT - GROUND_HEIGHT - 400 - TOWER_GAP / 2;
var TOWER_INTERVAL = 700; // horizontal distance between towers
var TOWER_SPEED = 14; // px per frame
// Game state
var plane;
var towers = [];
var ground;
var score = 0;
var scoreTxt;
var gameStarted = false;
var gameOver = false;
var lastTowerX = 0;
// Add ground
ground = LK.getAsset('ground', {
anchorX: 0,
anchorY: 0,
x: 0,
y: GAME_HEIGHT - GROUND_HEIGHT
});
game.addChild(ground);
// Add plane
plane = new Plane();
game.addChild(plane);
plane.x = PLANE_START_X;
plane.y = PLANE_START_Y;
// Add score text
scoreTxt = new Text2('0', {
size: 150,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Helper: reset game state
function resetGame() {
// Remove towers
for (var i = 0; i < towers.length; i++) {
towers[i].destroy();
}
towers = [];
// Reset plane
plane.x = PLANE_START_X;
plane.y = PLANE_START_Y;
plane.vy = 0;
// Reset score
score = 0;
scoreTxt.setText(score);
// Reset state
gameStarted = false;
gameOver = false;
lastTowerX = GAME_WIDTH + 600;
// Add initial towers
for (var i = 0; i < 3; i++) {
spawnTower(GAME_WIDTH + 600 + i * TOWER_INTERVAL);
}
}
// Helper: spawn a tower pair at x
function spawnTower(x) {
var gapY = TOWER_MIN_Y + Math.random() * (TOWER_MAX_Y - TOWER_MIN_Y);
var towerPair = new TowerPair();
towerPair.x = x;
towerPair.setGapY(gapY);
game.addChild(towerPair);
towers.push(towerPair);
}
// Start game
resetGame();
// Input: tap anywhere to flap
game.down = function (x, y, obj) {
if (gameOver) return;
if (!gameStarted) {
gameStarted = true;
}
plane.flap();
};
// Main update loop
game.update = function () {
if (gameOver) return;
if (gameStarted) {
// Move towers
for (var i = towers.length - 1; i >= 0; i--) {
var t = towers[i];
t.x -= TOWER_SPEED;
// Update tower (for rotation)
if (typeof t.update === "function") t.update();
// Remove towers off screen
if (t.x < -TOWER_WIDTH) {
t.destroy();
towers.splice(i, 1);
}
}
// Spawn new towers
if (towers.length === 0 || towers[towers.length - 1].x < GAME_WIDTH - TOWER_INTERVAL) {
spawnTower(GAME_WIDTH + TOWER_WIDTH);
}
// Plane physics
plane.update();
// Collision: ground
if (plane.y + 40 > GAME_HEIGHT - GROUND_HEIGHT) {
plane.y = GAME_HEIGHT - GROUND_HEIGHT - 40;
endGame();
return;
}
// Collision: ceiling
if (plane.y - 40 < 0) {
plane.y = 40;
plane.vy = 0;
}
// Collision: towers
for (var i = 0; i < towers.length; i++) {
var t = towers[i];
// Only check if in range
if (Math.abs(t.x - plane.x) < TOWER_WIDTH) {
// Top
if (plane.intersects(t.getTopTower()) || plane.intersects(t.getBottomTower())) {
// Explosion effect: flash screen and shake plane
LK.effects.flashScreen(0xffcc00, 300);
// Fire effect: tint plane orange for 2 seconds
var fireTween = tween(plane, {
tint: 0xff6600
}, {
duration: 200,
yoyo: true,
repeat: 9 // 10 flashes (200ms * 10 = 2s)
});
// Shake plane using tween
tween(plane, {
x: plane.x + 30
}, {
duration: 60,
yoyo: true,
repeat: 3,
onComplete: function onComplete() {
plane.x = PLANE_START_X;
}
});
endGame();
return;
}
}
// Scoring: passed tower
if (!t.passed && t.x + TOWER_WIDTH / 2 < plane.x) {
t.passed = true;
score += 1;
scoreTxt.setText(score);
LK.getSound('point').play();
}
}
} else {
// Idle: plane bobs up and down
plane.y = PLANE_START_Y + Math.sin(LK.ticks / 30) * 30;
plane.vy = 0;
}
};
// End game
function endGame() {
if (gameOver) return;
gameOver = true;
LK.getSound('hit').play();
LK.effects.flashScreen(0xff0000, 2000);
// After 2 seconds, show game over
LK.setTimeout(function () {
LK.showGameOver();
}, 2000);
}
// No drag or move needed for this game