/****
* 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