/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// BigBirdTarget: larger, easier to tap, unique image with orange beak
var BigBirdTarget = Container.expand(function () {
var self = Container.call(this);
var targetAsset = self.attachAsset('birdBig', {
anchorX: 0.5,
anchorY: 0.5,
width: 180 * 4.0,
height: 180 * 4.0
});
// Add a "beak" using a small orange ellipse overlay
var beak = self.attachAsset('targetCircle', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 60,
width: 60,
height: 30,
color: 0xff9900,
shape: 'ellipse'
});
self.isHit = false;
self.down = function (x, y, obj) {
if (self.isHit) {
return;
}
self.isHit = true;
tween(self, {
scaleX: 1.4,
scaleY: 1.4,
alpha: 0
}, {
duration: 120,
easing: tween.cubicOut,
onFinish: function onFinish() {
self.destroy();
}
});
if (typeof onTargetHit === 'function') {
onTargetHit(self);
}
};
self.alpha = 0;
self.scaleX = self.scaleY = 0.7;
tween(self, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 120,
easing: tween.cubicOut
});
return self;
});
// MovingBirdTarget: moves horizontally and vertically, mirrors on direction change, unique image with white "wing"
var MovingBirdTarget = Container.expand(function () {
var self = Container.call(this);
var targetAsset = self.attachAsset('birdMoving', {
anchorX: 0.5,
anchorY: 0.5,
width: 180 * 2.0,
height: 180 * 2.0
});
// Add a "wing" using a white ellipse overlay
var wing = self.attachAsset('targetCircle', {
anchorX: 0.5,
anchorY: 0.5,
x: 40,
y: 0,
width: 60,
height: 30,
color: 0xffffff,
shape: 'ellipse'
});
self.isHit = false;
// Movement direction: horizontal and vertical
self._direction = Math.random() < 0.5 ? 1 : -1; // left or right
self._verticalDir = Math.random() < 0.5 ? 1 : -1; // up or down
self._speed = 8 + Math.random() * 6; // px per frame (horizontal)
self._verticalSpeed = 3 + Math.random() * 3; // px per frame (vertical)
self._minX = 120 + targetAsset.width / 2;
self._maxX = 2048 - 120 - targetAsset.width / 2;
self._minY = 120 + targetAsset.height / 2;
self._maxY = 2732 - 120 - targetAsset.height / 2;
self.update = function () {
if (self.isHit) {
return;
}
if (typeof self.lastX === "undefined") {
self.lastX = self.x;
}
if (typeof self.lastY === "undefined") {
self.lastY = self.y;
}
var prevX = self.x;
var prevY = self.y;
// Move horizontally and vertically
self.x += self._direction * self._speed;
self.y += self._verticalDir * self._verticalSpeed;
// Mirror horizontally based on direction
targetAsset.scaleX = self._direction > 0 ? 1 : -1;
// Bounce at horizontal edges
if (self._direction > 0 && self.x > self._maxX || self._direction < 0 && self.x < self._minX) {
self._direction *= -1;
self.x = Math.max(Math.min(self.x, self._maxX), self._minX);
// Mirror handled above
}
// Bounce at vertical edges
if (self._verticalDir > 0 && self.y > self._maxY || self._verticalDir < 0 && self.y < self._minY) {
self._verticalDir *= -1;
self.y = Math.max(Math.min(self.y, self._maxY), self._minY);
}
self.lastX = self.x;
self.lastY = self.y;
};
self.down = function (x, y, obj) {
if (self.isHit) {
return;
}
self.isHit = true;
tween(self, {
scaleX: 1.4,
scaleY: 1.4,
alpha: 0
}, {
duration: 120,
easing: tween.cubicOut,
onFinish: function onFinish() {
self.destroy();
}
});
if (typeof onTargetHit === 'function') {
onTargetHit(self);
}
};
self.alpha = 0;
self.scaleX = self.scaleY = 0.7;
tween(self, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 120,
easing: tween.cubicOut
});
return self;
});
// SmallBirdTarget: smaller, harder to tap, unique image with pink "tail"
var SmallBirdTarget = Container.expand(function () {
var self = Container.call(this);
var targetAsset = self.attachAsset('birdSmall', {
anchorX: 0.5,
anchorY: 0.5,
width: 180 * 1.2,
height: 180 * 1.2
});
// Add a "tail" using a pink ellipse overlay
var tail = self.attachAsset('targetCircle', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 40,
width: 30,
height: 60,
color: 0xff66cc,
shape: 'ellipse'
});
self.isHit = false;
self.down = function (x, y, obj) {
if (self.isHit) {
return;
}
self.isHit = true;
tween(self, {
scaleX: 1.4,
scaleY: 1.4,
alpha: 0
}, {
duration: 120,
easing: tween.cubicOut,
onFinish: function onFinish() {
self.destroy();
}
});
if (typeof onTargetHit === 'function') {
onTargetHit(self);
}
};
self.alpha = 0;
self.scaleX = self.scaleY = 0.7;
tween(self, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 120,
easing: tween.cubicOut
});
return self;
});
// Target class: a tappable target that appears and disappears after a timeout, normal bird with unique image and white "eye"
var Target = Container.expand(function () {
var self = Container.call(this);
// Attach a unique image for normal bird
var targetAsset = self.attachAsset('birdNormal', {
anchorX: 0.5,
anchorY: 0.5,
width: 180 * 2.5,
height: 180 * 2.5
});
// Add a "white eye" using a small white ellipse overlay
var eye = self.attachAsset('targetCircle', {
anchorX: 0.5,
anchorY: 0.5,
x: 40,
y: -40,
width: 40,
height: 40,
color: 0xffffff,
shape: 'ellipse'
});
// Mark as not yet hit
self.isHit = false;
// Called when the target is tapped
self.down = function (x, y, obj) {
if (self.isHit) {
return;
}
self.isHit = true;
// Animate: scale up and fade out
tween(self, {
scaleX: 1.4,
scaleY: 1.4,
alpha: 0
}, {
duration: 120,
easing: tween.cubicOut,
onFinish: function onFinish() {
self.destroy();
}
});
// Notify game that this target was hit
if (typeof onTargetHit === 'function') {
onTargetHit(self);
}
};
// For tap feedback, animate in
self.alpha = 0;
self.scaleX = self.scaleY = 0.7;
tween(self, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 120,
easing: tween.cubicOut
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181818
});
/****
* Game Code
****/
// Add background shape first so it is always behind all menu buttons and UI elements
// Dedicated image asset for the menu title (replace id with any photo you want)
// Difficulty button assets
// Unique images for each bird type
// Add a background shape behind all game elements
// Sound assets for game events
var backgroundShape = LK.getAsset('backgroundShape', {
width: 2048,
height: 2048,
color: 0x222a38,
shape: 'box',
anchorX: 0,
anchorY: 0,
x: 0,
// Center the square background vertically in 2732px canvas
y: (2732 - 2048) / 2
});
game.addChild(backgroundShape);
// --- Difficulty settings ---
var difficulties = [{
name: "Easy",
key: "easy",
time: 800
}, {
name: "Medium",
key: "medium",
time: 600
}, {
name: "Difficult",
key: "hard",
time: 400
}, {
name: "Most Difficult",
key: "insane",
time: 250
}];
// --- State variables ---
var currentDifficulty = null; // {name, key, time}
var roundTargets = 20;
var targetsSpawned = 0;
var targetsHit = 0;
var targetTimeout = null;
var activeTarget = null;
var inGame = false;
// --- Best scores ---
if (!storage.bestScores) {
storage.bestScores = {};
}
var bestScores = storage.bestScores;
// --- UI elements ---
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var menuContainer = new Container();
LK.gui.center.addChild(menuContainer);
var bestScoreTxts = []; // Array of Text2 for best scores per difficulty
// --- Helper: Show menu ---
function showMenu() {
inGame = false;
// Clear any active target
if (activeTarget) {
activeTarget.destroy();
activeTarget = null;
}
if (targetTimeout) {
LK.clearTimeout(targetTimeout);
targetTimeout = null;
}
// Reset score display
scoreTxt.setText('');
// Remove previous menu items
while (menuContainer.children.length) {
menuContainer.removeChild(menuContainer.children[0]);
}
bestScoreTxts = [];
// Move menuContainer up so all buttons are higher on the screen
menuContainer.y = -220;
// Move scoreTxt to the top of the square area (move further up)
scoreTxt.y = -10;
// Title at the very top, visually separated from difficulty (now as a unique asset)
// Use a dedicated image asset for the title, so any photo can be used
// Remove any previous menuTitlePhoto asset from LK.gui.top before adding a new one
for (var i = LK.gui.top.children.length - 1; i >= 0; i--) {
var child = LK.gui.top.children[i];
if (child && child.assetId === 'menuTitlePhoto') {
LK.gui.top.removeChild(child);
}
}
var titleAsset = LK.getAsset('menuTitlePhoto', {
anchorX: 0.5,
anchorY: 0,
x: 0,
y: 380,
// Lowered by 400 units from previous 80
width: 800,
height: 240
});
titleAsset.assetId = 'menuTitlePhoto';
LK.gui.top.addChild(titleAsset);
// All difficulty buttons: visible, non-overlapping, distributed vertically under the menu title photo
var btnSizes = [{
width: 300,
height: 300
},
// Easy
{
width: 300,
height: 300
},
// Medium
{
width: 500,
height: 500
},
// Hard
{
width: 500,
height: 500
} // Most Difficult
];
// Layout parameters
var spacingY = 60;
var centerX = 0;
var underTitleY = 380 + 240 + 60; // title y + title height + margin
// --- Add main menu button to open/close difficulty buttons ---
var menuMainBtn = LK.getAsset('choseDifficultyButton', {
anchorX: 0.5,
anchorY: 0.5,
x: centerX,
y: underTitleY + 80,
// visually centered under title
width: 700,
height: 180
});
menuContainer.addChild(menuMainBtn);
var menuMainLabel = new Text2("Choose Difficulty", {
size: 70,
fill: "#fff"
});
menuMainLabel.anchor.set(0.5, 0.5);
menuMainLabel.x = menuMainBtn.x;
menuMainLabel.y = menuMainBtn.y;
menuContainer.addChild(menuMainLabel);
// Track if difficulty buttons are open
var difficultyButtonsOpen = false;
var difficultyBtns = [];
var difficultyLabels = [];
var difficultyBestTxts = [];
// Helper to show/hide difficulty buttons
function setDifficultyButtonsVisible(visible) {
for (var i = 0; i < difficultyBtns.length; i++) {
difficultyBtns[i].visible = visible;
difficultyLabels[i].visible = visible;
difficultyBestTxts[i].visible = visible;
}
}
// Pre-create difficulty buttons, but don't show them yet
var btnYs = [];
var yCursor = underTitleY + 180 + 40; // below the main menu button
for (var i = 0; i < difficulties.length; i++) {
btnYs.push(yCursor + btnSizes[i].height / 2);
yCursor += btnSizes[i].height + spacingY;
}
for (var i = 0; i < difficulties.length; i++) {
// Assign menuBtn0-3 asset for each difficulty button
var btnAssetId = 'menuBtn' + i;
var btn = LK.getAsset(btnAssetId, {
anchorX: 0.5,
anchorY: 0.5,
x: centerX,
y: btnYs[i],
width: btnSizes[i].width,
height: btnSizes[i].height
});
btn.visible = false;
menuContainer.addChild(btn);
var label = new Text2(difficulties[i].name, {
size: 70,
fill: "#fff"
});
label.anchor.set(0.5, 0.5);
label.x = btn.x;
label.y = btn.y;
label.visible = false;
menuContainer.addChild(label);
var best = bestScores[difficulties[i].key] || 0;
var bestTxt = new Text2("Best: " + best + " / " + roundTargets, {
size: 40,
fill: 0xFFEC00
});
bestTxt.anchor.set(0.5, 0.5);
bestTxt.x = btn.x + 220;
bestTxt.y = btn.y + 38;
bestTxt.visible = false;
menuContainer.addChild(bestTxt);
bestScoreTxts.push(bestTxt);
difficultyBtns.push(btn);
difficultyLabels.push(label);
difficultyBestTxts.push(bestTxt);
(function (idx, button) {
button.down = function (x, y, obj) {
startGame(difficulties[idx]);
};
})(i, btn);
}
// Main menu button toggles difficulty buttons
menuMainBtn.down = function (x, y, obj) {
// Hide the main menu button and its label
menuMainBtn.visible = false;
menuMainLabel.visible = false;
difficultyButtonsOpen = true;
// Hide menuTitlePhoto if present
for (var i = LK.gui.top.children.length - 1; i >= 0; i--) {
var child = LK.gui.top.children[i];
if (child && child.assetId === 'menuTitlePhoto') {
child.visible = false;
}
}
// Place difficulty buttons closer to the center and show them
// New positions: all buttons are closer to the center (±420 px from center)
var btnOffset = 420;
var bestOffsetX = 180;
var bestOffsetY = 38;
// Top left (closer to center)
difficultyBtns[0].x = -btnOffset;
difficultyBtns[0].y = -btnOffset;
difficultyLabels[0].x = -btnOffset;
difficultyLabels[0].y = -btnOffset;
difficultyBestTxts[0].x = -btnOffset + bestOffsetX;
difficultyBestTxts[0].y = -btnOffset + bestOffsetY;
// Lower left (closer to center)
difficultyBtns[1].x = -btnOffset;
difficultyBtns[1].y = btnOffset;
difficultyLabels[1].x = -btnOffset;
difficultyLabels[1].y = btnOffset;
difficultyBestTxts[1].x = -btnOffset + bestOffsetX;
difficultyBestTxts[1].y = btnOffset + bestOffsetY;
// Top right (closer to center)
difficultyBtns[2].x = btnOffset;
difficultyBtns[2].y = -btnOffset;
difficultyLabels[2].x = btnOffset;
difficultyLabels[2].y = -btnOffset;
difficultyBestTxts[2].x = btnOffset + bestOffsetX;
difficultyBestTxts[2].y = -btnOffset + bestOffsetY;
// Lower right (closer to center)
difficultyBtns[3].x = btnOffset;
difficultyBtns[3].y = btnOffset;
difficultyLabels[3].x = btnOffset;
difficultyLabels[3].y = btnOffset;
difficultyBestTxts[3].x = btnOffset + bestOffsetX;
difficultyBestTxts[3].y = btnOffset + bestOffsetY;
setDifficultyButtonsVisible(true);
};
setDifficultyButtonsVisible(false);
}
// --- Helper: Start game with selected difficulty ---
function startGame(diff) {
inGame = true;
currentDifficulty = diff;
targetsSpawned = 0;
targetsHit = 0;
scoreTxt.setText("0 / " + roundTargets);
// Play start sound
LK.getSound('start').play();
// Remove menu
while (menuContainer.children.length) {
menuContainer.removeChild(menuContainer.children[0]);
}
// Remove menuTitlePhoto from LK.gui.top if present
for (var i = LK.gui.top.children.length - 1; i >= 0; i--) {
var child = LK.gui.top.children[i];
if (child && child.assetId === 'menuTitlePhoto') {
LK.gui.top.removeChild(child);
}
}
// Start first target
spawnNextTarget();
}
// --- Helper: Spawn a target at a random position ---
function spawnNextTarget() {
if (!inGame) {
return;
}
if (activeTarget) {
activeTarget.destroy();
activeTarget = null;
}
if (targetsSpawned >= roundTargets) {
endGame();
return;
}
targetsSpawned++;
// Randomly choose a target type: 0=normal, 1=small, 2=big, 3=moving
var margin = 120;
var type = Math.floor(Math.random() * 4);
var x, y, target;
if (type === 1) {
// Small bird
x = margin + 60 + Math.random() * (2048 - 2 * (margin + 60));
y = margin + 60 + Math.random() * (2048 - 2 * (margin + 60));
target = new SmallBirdTarget();
target.x = x;
target.y = y;
} else if (type === 2) {
// Big bird
x = margin + 180 + Math.random() * (2048 - 2 * (margin + 180));
y = margin + 180 + Math.random() * (2048 - 2 * (margin + 180));
target = new BigBirdTarget();
target.x = x;
target.y = y;
} else if (type === 3) {
// Moving bird
y = margin + Math.random() * (2048 - 2 * margin);
// Start at left or right edge
if (Math.random() < 0.5) {
x = margin + 180;
} else {
x = 2048 - margin - 180;
}
target = new MovingBirdTarget();
target.x = x;
target.y = y;
} else {
// Normal
x = margin + Math.random() * (2048 - 2 * margin);
y = margin + Math.random() * (2048 - 2 * margin);
target = new Target();
target.x = x;
target.y = y;
}
activeTarget = target;
game.addChild(target);
// Timeout: if not hit in time, remove and spawn next
targetTimeout = LK.setTimeout(function () {
if (activeTarget === target && !target.isHit) {
// Animate out
tween(target, {
alpha: 0
}, {
duration: 100,
onFinish: function onFinish() {
target.destroy();
}
});
activeTarget = null;
spawnNextTarget();
}
}, currentDifficulty.time);
}
// --- Helper: Called when a target is hit ---
function onTargetHit(target) {
if (!inGame) {
return;
}
if (activeTarget === target) {
targetsHit++;
scoreTxt.setText(targetsHit + " / " + roundTargets);
// Play hit sound
LK.getSound('hit').play();
activeTarget = null;
if (targetTimeout) {
LK.clearTimeout(targetTimeout);
targetTimeout = null;
}
// Next target after a short delay for feedback
LK.setTimeout(spawnNextTarget, 80);
}
}
// --- Helper: End of round ---
function endGame() {
inGame = false;
// Play end sound
LK.getSound('end').play();
// Update best score if needed
var key = currentDifficulty.key;
var isBest = false;
if (!bestScores[key] || targetsHit > bestScores[key]) {
bestScores[key] = targetsHit;
storage.bestScores = bestScores;
isBest = true;
}
// Play best score sound if new best
if (isBest) {
LK.getSound('best').play();
}
// Show result popup
var resultTxt = new Text2("You hit " + targetsHit + " / " + roundTargets + "!", {
size: 100,
fill: "#fff"
});
resultTxt.anchor.set(0.5, 0.5);
resultTxt.y = -120;
menuContainer.addChild(resultTxt);
var best = bestScores[key] || 0;
var bestTxt = new Text2("Best: " + best + " / " + roundTargets, {
size: 60,
fill: 0xFFEC00
});
bestTxt.anchor.set(0.5, 0.5);
bestTxt.y = 0;
menuContainer.addChild(bestTxt);
var againBtn = LK.getAsset('againBtn', {
width: 500,
height: 120,
color: 0x44bb44,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 180
});
menuContainer.addChild(againBtn);
var againLabel = new Text2("Menu", {
size: 60,
fill: "#fff"
});
againLabel.anchor.set(0.5, 0.5);
againLabel.x = againBtn.x;
againLabel.y = againBtn.y;
menuContainer.addChild(againLabel);
againBtn.down = function (x, y, obj) {
// Remove result elements
while (menuContainer.children.length) {
menuContainer.removeChild(menuContainer.children[0]);
}
// Show menuTitlePhoto if present, or add it if not present
var foundTitlePhoto = false;
for (var i = LK.gui.top.children.length - 1; i >= 0; i--) {
var child = LK.gui.top.children[i];
if (child && child.assetId === 'menuTitlePhoto') {
child.visible = true;
foundTitlePhoto = true;
}
}
if (!foundTitlePhoto) {
var titleAsset = LK.getAsset('menuTitlePhoto', {
anchorX: 0.5,
anchorY: 0,
x: 0,
y: 380,
width: 800,
height: 240
});
titleAsset.assetId = 'menuTitlePhoto';
LK.gui.top.addChild(titleAsset);
}
showMenu();
};
}
// Animate moving bird targets (if any)
game.update = function () {
if (activeTarget && typeof activeTarget.update === "function") {
activeTarget.update();
}
};
// --- Game move handler (no drag, but required for touch compatibility) ---
game.move = function (x, y, obj) {};
// --- Start with menu ---
showMenu(); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// BigBirdTarget: larger, easier to tap, unique image with orange beak
var BigBirdTarget = Container.expand(function () {
var self = Container.call(this);
var targetAsset = self.attachAsset('birdBig', {
anchorX: 0.5,
anchorY: 0.5,
width: 180 * 4.0,
height: 180 * 4.0
});
// Add a "beak" using a small orange ellipse overlay
var beak = self.attachAsset('targetCircle', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 60,
width: 60,
height: 30,
color: 0xff9900,
shape: 'ellipse'
});
self.isHit = false;
self.down = function (x, y, obj) {
if (self.isHit) {
return;
}
self.isHit = true;
tween(self, {
scaleX: 1.4,
scaleY: 1.4,
alpha: 0
}, {
duration: 120,
easing: tween.cubicOut,
onFinish: function onFinish() {
self.destroy();
}
});
if (typeof onTargetHit === 'function') {
onTargetHit(self);
}
};
self.alpha = 0;
self.scaleX = self.scaleY = 0.7;
tween(self, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 120,
easing: tween.cubicOut
});
return self;
});
// MovingBirdTarget: moves horizontally and vertically, mirrors on direction change, unique image with white "wing"
var MovingBirdTarget = Container.expand(function () {
var self = Container.call(this);
var targetAsset = self.attachAsset('birdMoving', {
anchorX: 0.5,
anchorY: 0.5,
width: 180 * 2.0,
height: 180 * 2.0
});
// Add a "wing" using a white ellipse overlay
var wing = self.attachAsset('targetCircle', {
anchorX: 0.5,
anchorY: 0.5,
x: 40,
y: 0,
width: 60,
height: 30,
color: 0xffffff,
shape: 'ellipse'
});
self.isHit = false;
// Movement direction: horizontal and vertical
self._direction = Math.random() < 0.5 ? 1 : -1; // left or right
self._verticalDir = Math.random() < 0.5 ? 1 : -1; // up or down
self._speed = 8 + Math.random() * 6; // px per frame (horizontal)
self._verticalSpeed = 3 + Math.random() * 3; // px per frame (vertical)
self._minX = 120 + targetAsset.width / 2;
self._maxX = 2048 - 120 - targetAsset.width / 2;
self._minY = 120 + targetAsset.height / 2;
self._maxY = 2732 - 120 - targetAsset.height / 2;
self.update = function () {
if (self.isHit) {
return;
}
if (typeof self.lastX === "undefined") {
self.lastX = self.x;
}
if (typeof self.lastY === "undefined") {
self.lastY = self.y;
}
var prevX = self.x;
var prevY = self.y;
// Move horizontally and vertically
self.x += self._direction * self._speed;
self.y += self._verticalDir * self._verticalSpeed;
// Mirror horizontally based on direction
targetAsset.scaleX = self._direction > 0 ? 1 : -1;
// Bounce at horizontal edges
if (self._direction > 0 && self.x > self._maxX || self._direction < 0 && self.x < self._minX) {
self._direction *= -1;
self.x = Math.max(Math.min(self.x, self._maxX), self._minX);
// Mirror handled above
}
// Bounce at vertical edges
if (self._verticalDir > 0 && self.y > self._maxY || self._verticalDir < 0 && self.y < self._minY) {
self._verticalDir *= -1;
self.y = Math.max(Math.min(self.y, self._maxY), self._minY);
}
self.lastX = self.x;
self.lastY = self.y;
};
self.down = function (x, y, obj) {
if (self.isHit) {
return;
}
self.isHit = true;
tween(self, {
scaleX: 1.4,
scaleY: 1.4,
alpha: 0
}, {
duration: 120,
easing: tween.cubicOut,
onFinish: function onFinish() {
self.destroy();
}
});
if (typeof onTargetHit === 'function') {
onTargetHit(self);
}
};
self.alpha = 0;
self.scaleX = self.scaleY = 0.7;
tween(self, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 120,
easing: tween.cubicOut
});
return self;
});
// SmallBirdTarget: smaller, harder to tap, unique image with pink "tail"
var SmallBirdTarget = Container.expand(function () {
var self = Container.call(this);
var targetAsset = self.attachAsset('birdSmall', {
anchorX: 0.5,
anchorY: 0.5,
width: 180 * 1.2,
height: 180 * 1.2
});
// Add a "tail" using a pink ellipse overlay
var tail = self.attachAsset('targetCircle', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 40,
width: 30,
height: 60,
color: 0xff66cc,
shape: 'ellipse'
});
self.isHit = false;
self.down = function (x, y, obj) {
if (self.isHit) {
return;
}
self.isHit = true;
tween(self, {
scaleX: 1.4,
scaleY: 1.4,
alpha: 0
}, {
duration: 120,
easing: tween.cubicOut,
onFinish: function onFinish() {
self.destroy();
}
});
if (typeof onTargetHit === 'function') {
onTargetHit(self);
}
};
self.alpha = 0;
self.scaleX = self.scaleY = 0.7;
tween(self, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 120,
easing: tween.cubicOut
});
return self;
});
// Target class: a tappable target that appears and disappears after a timeout, normal bird with unique image and white "eye"
var Target = Container.expand(function () {
var self = Container.call(this);
// Attach a unique image for normal bird
var targetAsset = self.attachAsset('birdNormal', {
anchorX: 0.5,
anchorY: 0.5,
width: 180 * 2.5,
height: 180 * 2.5
});
// Add a "white eye" using a small white ellipse overlay
var eye = self.attachAsset('targetCircle', {
anchorX: 0.5,
anchorY: 0.5,
x: 40,
y: -40,
width: 40,
height: 40,
color: 0xffffff,
shape: 'ellipse'
});
// Mark as not yet hit
self.isHit = false;
// Called when the target is tapped
self.down = function (x, y, obj) {
if (self.isHit) {
return;
}
self.isHit = true;
// Animate: scale up and fade out
tween(self, {
scaleX: 1.4,
scaleY: 1.4,
alpha: 0
}, {
duration: 120,
easing: tween.cubicOut,
onFinish: function onFinish() {
self.destroy();
}
});
// Notify game that this target was hit
if (typeof onTargetHit === 'function') {
onTargetHit(self);
}
};
// For tap feedback, animate in
self.alpha = 0;
self.scaleX = self.scaleY = 0.7;
tween(self, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 120,
easing: tween.cubicOut
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181818
});
/****
* Game Code
****/
// Add background shape first so it is always behind all menu buttons and UI elements
// Dedicated image asset for the menu title (replace id with any photo you want)
// Difficulty button assets
// Unique images for each bird type
// Add a background shape behind all game elements
// Sound assets for game events
var backgroundShape = LK.getAsset('backgroundShape', {
width: 2048,
height: 2048,
color: 0x222a38,
shape: 'box',
anchorX: 0,
anchorY: 0,
x: 0,
// Center the square background vertically in 2732px canvas
y: (2732 - 2048) / 2
});
game.addChild(backgroundShape);
// --- Difficulty settings ---
var difficulties = [{
name: "Easy",
key: "easy",
time: 800
}, {
name: "Medium",
key: "medium",
time: 600
}, {
name: "Difficult",
key: "hard",
time: 400
}, {
name: "Most Difficult",
key: "insane",
time: 250
}];
// --- State variables ---
var currentDifficulty = null; // {name, key, time}
var roundTargets = 20;
var targetsSpawned = 0;
var targetsHit = 0;
var targetTimeout = null;
var activeTarget = null;
var inGame = false;
// --- Best scores ---
if (!storage.bestScores) {
storage.bestScores = {};
}
var bestScores = storage.bestScores;
// --- UI elements ---
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var menuContainer = new Container();
LK.gui.center.addChild(menuContainer);
var bestScoreTxts = []; // Array of Text2 for best scores per difficulty
// --- Helper: Show menu ---
function showMenu() {
inGame = false;
// Clear any active target
if (activeTarget) {
activeTarget.destroy();
activeTarget = null;
}
if (targetTimeout) {
LK.clearTimeout(targetTimeout);
targetTimeout = null;
}
// Reset score display
scoreTxt.setText('');
// Remove previous menu items
while (menuContainer.children.length) {
menuContainer.removeChild(menuContainer.children[0]);
}
bestScoreTxts = [];
// Move menuContainer up so all buttons are higher on the screen
menuContainer.y = -220;
// Move scoreTxt to the top of the square area (move further up)
scoreTxt.y = -10;
// Title at the very top, visually separated from difficulty (now as a unique asset)
// Use a dedicated image asset for the title, so any photo can be used
// Remove any previous menuTitlePhoto asset from LK.gui.top before adding a new one
for (var i = LK.gui.top.children.length - 1; i >= 0; i--) {
var child = LK.gui.top.children[i];
if (child && child.assetId === 'menuTitlePhoto') {
LK.gui.top.removeChild(child);
}
}
var titleAsset = LK.getAsset('menuTitlePhoto', {
anchorX: 0.5,
anchorY: 0,
x: 0,
y: 380,
// Lowered by 400 units from previous 80
width: 800,
height: 240
});
titleAsset.assetId = 'menuTitlePhoto';
LK.gui.top.addChild(titleAsset);
// All difficulty buttons: visible, non-overlapping, distributed vertically under the menu title photo
var btnSizes = [{
width: 300,
height: 300
},
// Easy
{
width: 300,
height: 300
},
// Medium
{
width: 500,
height: 500
},
// Hard
{
width: 500,
height: 500
} // Most Difficult
];
// Layout parameters
var spacingY = 60;
var centerX = 0;
var underTitleY = 380 + 240 + 60; // title y + title height + margin
// --- Add main menu button to open/close difficulty buttons ---
var menuMainBtn = LK.getAsset('choseDifficultyButton', {
anchorX: 0.5,
anchorY: 0.5,
x: centerX,
y: underTitleY + 80,
// visually centered under title
width: 700,
height: 180
});
menuContainer.addChild(menuMainBtn);
var menuMainLabel = new Text2("Choose Difficulty", {
size: 70,
fill: "#fff"
});
menuMainLabel.anchor.set(0.5, 0.5);
menuMainLabel.x = menuMainBtn.x;
menuMainLabel.y = menuMainBtn.y;
menuContainer.addChild(menuMainLabel);
// Track if difficulty buttons are open
var difficultyButtonsOpen = false;
var difficultyBtns = [];
var difficultyLabels = [];
var difficultyBestTxts = [];
// Helper to show/hide difficulty buttons
function setDifficultyButtonsVisible(visible) {
for (var i = 0; i < difficultyBtns.length; i++) {
difficultyBtns[i].visible = visible;
difficultyLabels[i].visible = visible;
difficultyBestTxts[i].visible = visible;
}
}
// Pre-create difficulty buttons, but don't show them yet
var btnYs = [];
var yCursor = underTitleY + 180 + 40; // below the main menu button
for (var i = 0; i < difficulties.length; i++) {
btnYs.push(yCursor + btnSizes[i].height / 2);
yCursor += btnSizes[i].height + spacingY;
}
for (var i = 0; i < difficulties.length; i++) {
// Assign menuBtn0-3 asset for each difficulty button
var btnAssetId = 'menuBtn' + i;
var btn = LK.getAsset(btnAssetId, {
anchorX: 0.5,
anchorY: 0.5,
x: centerX,
y: btnYs[i],
width: btnSizes[i].width,
height: btnSizes[i].height
});
btn.visible = false;
menuContainer.addChild(btn);
var label = new Text2(difficulties[i].name, {
size: 70,
fill: "#fff"
});
label.anchor.set(0.5, 0.5);
label.x = btn.x;
label.y = btn.y;
label.visible = false;
menuContainer.addChild(label);
var best = bestScores[difficulties[i].key] || 0;
var bestTxt = new Text2("Best: " + best + " / " + roundTargets, {
size: 40,
fill: 0xFFEC00
});
bestTxt.anchor.set(0.5, 0.5);
bestTxt.x = btn.x + 220;
bestTxt.y = btn.y + 38;
bestTxt.visible = false;
menuContainer.addChild(bestTxt);
bestScoreTxts.push(bestTxt);
difficultyBtns.push(btn);
difficultyLabels.push(label);
difficultyBestTxts.push(bestTxt);
(function (idx, button) {
button.down = function (x, y, obj) {
startGame(difficulties[idx]);
};
})(i, btn);
}
// Main menu button toggles difficulty buttons
menuMainBtn.down = function (x, y, obj) {
// Hide the main menu button and its label
menuMainBtn.visible = false;
menuMainLabel.visible = false;
difficultyButtonsOpen = true;
// Hide menuTitlePhoto if present
for (var i = LK.gui.top.children.length - 1; i >= 0; i--) {
var child = LK.gui.top.children[i];
if (child && child.assetId === 'menuTitlePhoto') {
child.visible = false;
}
}
// Place difficulty buttons closer to the center and show them
// New positions: all buttons are closer to the center (±420 px from center)
var btnOffset = 420;
var bestOffsetX = 180;
var bestOffsetY = 38;
// Top left (closer to center)
difficultyBtns[0].x = -btnOffset;
difficultyBtns[0].y = -btnOffset;
difficultyLabels[0].x = -btnOffset;
difficultyLabels[0].y = -btnOffset;
difficultyBestTxts[0].x = -btnOffset + bestOffsetX;
difficultyBestTxts[0].y = -btnOffset + bestOffsetY;
// Lower left (closer to center)
difficultyBtns[1].x = -btnOffset;
difficultyBtns[1].y = btnOffset;
difficultyLabels[1].x = -btnOffset;
difficultyLabels[1].y = btnOffset;
difficultyBestTxts[1].x = -btnOffset + bestOffsetX;
difficultyBestTxts[1].y = btnOffset + bestOffsetY;
// Top right (closer to center)
difficultyBtns[2].x = btnOffset;
difficultyBtns[2].y = -btnOffset;
difficultyLabels[2].x = btnOffset;
difficultyLabels[2].y = -btnOffset;
difficultyBestTxts[2].x = btnOffset + bestOffsetX;
difficultyBestTxts[2].y = -btnOffset + bestOffsetY;
// Lower right (closer to center)
difficultyBtns[3].x = btnOffset;
difficultyBtns[3].y = btnOffset;
difficultyLabels[3].x = btnOffset;
difficultyLabels[3].y = btnOffset;
difficultyBestTxts[3].x = btnOffset + bestOffsetX;
difficultyBestTxts[3].y = btnOffset + bestOffsetY;
setDifficultyButtonsVisible(true);
};
setDifficultyButtonsVisible(false);
}
// --- Helper: Start game with selected difficulty ---
function startGame(diff) {
inGame = true;
currentDifficulty = diff;
targetsSpawned = 0;
targetsHit = 0;
scoreTxt.setText("0 / " + roundTargets);
// Play start sound
LK.getSound('start').play();
// Remove menu
while (menuContainer.children.length) {
menuContainer.removeChild(menuContainer.children[0]);
}
// Remove menuTitlePhoto from LK.gui.top if present
for (var i = LK.gui.top.children.length - 1; i >= 0; i--) {
var child = LK.gui.top.children[i];
if (child && child.assetId === 'menuTitlePhoto') {
LK.gui.top.removeChild(child);
}
}
// Start first target
spawnNextTarget();
}
// --- Helper: Spawn a target at a random position ---
function spawnNextTarget() {
if (!inGame) {
return;
}
if (activeTarget) {
activeTarget.destroy();
activeTarget = null;
}
if (targetsSpawned >= roundTargets) {
endGame();
return;
}
targetsSpawned++;
// Randomly choose a target type: 0=normal, 1=small, 2=big, 3=moving
var margin = 120;
var type = Math.floor(Math.random() * 4);
var x, y, target;
if (type === 1) {
// Small bird
x = margin + 60 + Math.random() * (2048 - 2 * (margin + 60));
y = margin + 60 + Math.random() * (2048 - 2 * (margin + 60));
target = new SmallBirdTarget();
target.x = x;
target.y = y;
} else if (type === 2) {
// Big bird
x = margin + 180 + Math.random() * (2048 - 2 * (margin + 180));
y = margin + 180 + Math.random() * (2048 - 2 * (margin + 180));
target = new BigBirdTarget();
target.x = x;
target.y = y;
} else if (type === 3) {
// Moving bird
y = margin + Math.random() * (2048 - 2 * margin);
// Start at left or right edge
if (Math.random() < 0.5) {
x = margin + 180;
} else {
x = 2048 - margin - 180;
}
target = new MovingBirdTarget();
target.x = x;
target.y = y;
} else {
// Normal
x = margin + Math.random() * (2048 - 2 * margin);
y = margin + Math.random() * (2048 - 2 * margin);
target = new Target();
target.x = x;
target.y = y;
}
activeTarget = target;
game.addChild(target);
// Timeout: if not hit in time, remove and spawn next
targetTimeout = LK.setTimeout(function () {
if (activeTarget === target && !target.isHit) {
// Animate out
tween(target, {
alpha: 0
}, {
duration: 100,
onFinish: function onFinish() {
target.destroy();
}
});
activeTarget = null;
spawnNextTarget();
}
}, currentDifficulty.time);
}
// --- Helper: Called when a target is hit ---
function onTargetHit(target) {
if (!inGame) {
return;
}
if (activeTarget === target) {
targetsHit++;
scoreTxt.setText(targetsHit + " / " + roundTargets);
// Play hit sound
LK.getSound('hit').play();
activeTarget = null;
if (targetTimeout) {
LK.clearTimeout(targetTimeout);
targetTimeout = null;
}
// Next target after a short delay for feedback
LK.setTimeout(spawnNextTarget, 80);
}
}
// --- Helper: End of round ---
function endGame() {
inGame = false;
// Play end sound
LK.getSound('end').play();
// Update best score if needed
var key = currentDifficulty.key;
var isBest = false;
if (!bestScores[key] || targetsHit > bestScores[key]) {
bestScores[key] = targetsHit;
storage.bestScores = bestScores;
isBest = true;
}
// Play best score sound if new best
if (isBest) {
LK.getSound('best').play();
}
// Show result popup
var resultTxt = new Text2("You hit " + targetsHit + " / " + roundTargets + "!", {
size: 100,
fill: "#fff"
});
resultTxt.anchor.set(0.5, 0.5);
resultTxt.y = -120;
menuContainer.addChild(resultTxt);
var best = bestScores[key] || 0;
var bestTxt = new Text2("Best: " + best + " / " + roundTargets, {
size: 60,
fill: 0xFFEC00
});
bestTxt.anchor.set(0.5, 0.5);
bestTxt.y = 0;
menuContainer.addChild(bestTxt);
var againBtn = LK.getAsset('againBtn', {
width: 500,
height: 120,
color: 0x44bb44,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 180
});
menuContainer.addChild(againBtn);
var againLabel = new Text2("Menu", {
size: 60,
fill: "#fff"
});
againLabel.anchor.set(0.5, 0.5);
againLabel.x = againBtn.x;
againLabel.y = againBtn.y;
menuContainer.addChild(againLabel);
againBtn.down = function (x, y, obj) {
// Remove result elements
while (menuContainer.children.length) {
menuContainer.removeChild(menuContainer.children[0]);
}
// Show menuTitlePhoto if present, or add it if not present
var foundTitlePhoto = false;
for (var i = LK.gui.top.children.length - 1; i >= 0; i--) {
var child = LK.gui.top.children[i];
if (child && child.assetId === 'menuTitlePhoto') {
child.visible = true;
foundTitlePhoto = true;
}
}
if (!foundTitlePhoto) {
var titleAsset = LK.getAsset('menuTitlePhoto', {
anchorX: 0.5,
anchorY: 0,
x: 0,
y: 380,
width: 800,
height: 240
});
titleAsset.assetId = 'menuTitlePhoto';
LK.gui.top.addChild(titleAsset);
}
showMenu();
};
}
// Animate moving bird targets (if any)
game.update = function () {
if (activeTarget && typeof activeTarget.update === "function") {
activeTarget.update();
}
};
// --- Game move handler (no drag, but required for touch compatibility) ---
game.move = function (x, y, obj) {};
// --- Start with menu ---
showMenu();
Sunset between the mountains ( natural) piksel art. In-Game asset. 2d. High contrast. No shadows
Big fat flaying bird ( Piksel art). Colorful In-Game asset. 2d. High contrast.
Rectangle
Big fat flaying bird ( Piksel art). Colorful. Cute. In-Game asset. 2d. High contrast.
Big fat flaying bird ( Piksel art). Cute. Colorful In-Game asset. 2d. High contrast.
Fastest flaying bird ( Piksel art). Cute. Colorful In-Game asset. 2d. High contrast.
Target for weapon.(piksel art) In-Game asset. 2d. High contrast.
(700x140) button (cute pixel art). no writing No shadows
A pixel art sign that says BIRDS SHOOTING.(800x240 size) No shadows
700x700(total size) black red button (like hell fire around and into button) (cute pixel art). no writing No shadows