User prompt
text on the screen which level is playing
User prompt
If the player uses all arrows show "Game Over."
User prompt
triggger game over when arrows count become 0
User prompt
Show 'Game Over' if all arrows are used and become 0 and targets are left
User prompt
Show 'Game Over' if all arrows are used and targets are left 🔄 Advance to next level if all targets are hit
User prompt
Level Structure: Level 1: 3 targets,5 arrows Level 2: 5 targets,7 arrows Level 3: 7 targets, 9 arrows Level 4: 9 targets, 11 arrows - Increase difficulty with each level.
User prompt
Make the arrow disappear from a screen when it hits a target
User prompt
Disappear arrow also like the target
User prompt
Add the following logic: - Multiple targets move left and right on the field. - When an arrow hits a target, only the hit target should disappear with a blast effect (explosion animation or particles). - Other targets should continue moving as usual. - Play a sound or visual effect to indicate a successful hit. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Add this specific logic: - Multiple targets move left and right on the field. - When an arrow hits a target, stop only that specific target's movement. - Other targets should continue moving normally. - The hit target can show a visual effect or change (e.g., flash or turn red). - Do not reset or stop all targets—only the one that was hit.
User prompt
Increase the maximum power of the bow to ensure the arrow reaches the target.
User prompt
Make sure the arrow has enough speed to reach the target.
User prompt
Position of the bow in the middle
Code edit (1 edits merged)
Please save this source code
User prompt
Archery Master 3D
Initial prompt
Create a 3D-style Archery Battle game 1. Player stands on one side of the field with a bow. 2. The target stands on the opposite side of the field and can move left/right slowly. 3. Touch and drag to aim the bow (adjust angle and power). 4. Release to shoot the arrow. The arrow should follow a curved arc (parabolic trajectory). 5. Add a score system based on how close the arrow hits the center of the target. 6. Add a simple UI: Score display and remaining arrows counter. 7. Use simple assets: bow, arrow, and target. 8. Simulate depth using scale and perspective (e.g., far objects smaller). 9. Include a background like trees or mountains for aesthetics. 10. Make the gameplay loop repeatable: 5 arrows per round, then display score and restart option.
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0
});
/****
* Classes
****/
var Arrow = Container.expand(function () {
var self = Container.call(this);
// Arrow body
var arrowBody = self.attachAsset('arrow', {
anchorX: 0.5,
anchorY: 0.5
});
// Arrow tip
var arrowTip = self.attachAsset('arrowTip', {
anchorX: 0.5,
anchorY: 0.5,
x: 65
});
// Properties
self.velocity = {
x: 0,
y: 0
};
self.gravity = 0.3;
self.friction = 0.99;
self.flying = false;
self.stuck = false;
self.targetHit = null;
// Update arrow physics
self.update = function () {
if (self.flying && !self.stuck) {
// Apply physics
self.velocity.y += self.gravity;
self.x += self.velocity.x;
self.y += self.velocity.y;
// Update rotation to match trajectory
self.rotation = Math.atan2(self.velocity.y, self.velocity.x);
// Check if arrow is off-screen
if (self.y > 2732 || self.x < -100 || self.x > 2148) {
if (!self.hasHitGround) {
self.hasHitGround = true;
LK.getSound('miss').play();
}
}
}
};
// Fire the arrow with given power and angle
self.fire = function (power, angle) {
self.flying = true;
self.velocity.x = Math.cos(angle) * power;
self.velocity.y = Math.sin(angle) * power;
self.rotation = angle;
LK.getSound('bowRelease').play();
};
// Stick arrow to target
self.stickTo = function (target, score) {
self.stuck = true;
self.flying = false;
self.targetHit = target;
self.score = score;
// Play sound
LK.getSound('targetHit').play();
};
return self;
});
var Bow = Container.expand(function () {
var self = Container.call(this);
// Bow body
var bowBody = self.attachAsset('bow', {
anchorX: 0.5,
anchorY: 0.5
});
// Bow string
var bowString = self.attachAsset('bowString', {
anchorX: 0.5,
anchorY: 0.5,
x: -10
});
// Properties
self.aimAngle = -Math.PI / 4; // Default angle (slightly up)
self.power = 0;
self.maxPower = 35;
self.charging = false;
// Update bow
self.update = function () {
// Update bow rotation to match aim angle
self.rotation = self.aimAngle;
// Update string position/appearance based on pull
if (self.charging) {
var pullFactor = self.power / self.maxPower;
bowString.scaleX = 1 + pullFactor * 0.5; // Thicker when pulled
} else {
bowString.scaleX = 1;
}
};
// Start charging (pulling back)
self.startCharging = function () {
self.charging = true;
self.power = 0;
};
// Continue charging
self.updateCharging = function () {
if (self.charging) {
self.power += 0.4;
if (self.power > self.maxPower) {
self.power = self.maxPower;
}
}
};
// Release arrow
self.releaseArrow = function () {
var finalPower = self.power;
self.charging = false;
self.power = 0;
return finalPower;
};
return self;
});
var PowerMeter = Container.expand(function () {
var self = Container.call(this);
// Background
var background = self.attachAsset('powerMeter', {
anchorX: 0.5,
anchorY: 0.5
});
// Fill
var fill = self.attachAsset('powerFill', {
anchorX: 0.5,
anchorY: 1,
// Anchor to bottom
y: 195 // Half height of background
});
fill.height = 0; // Start empty
// Update power meter
self.updatePower = function (power, maxPower) {
var percentage = power / maxPower;
fill.height = 390 * percentage; // 390 is the max height of fill
// Change color based on power
if (percentage < 0.33) {
fill.tint = 0x00FF00; // Green
} else if (percentage < 0.66) {
fill.tint = 0xFFFF00; // Yellow
} else {
fill.tint = 0xFF0000; // Red
}
};
return self;
});
var Target = Container.expand(function () {
var self = Container.call(this);
// Create rings from outside to inside
var outerRing = self.attachAsset('target', {
anchorX: 0.5,
anchorY: 0.5
});
var ring1 = self.attachAsset('targetRing1', {
anchorX: 0.5,
anchorY: 0.5
});
var ring2 = self.attachAsset('targetRing2', {
anchorX: 0.5,
anchorY: 0.5
});
var ring3 = self.attachAsset('targetRing3', {
anchorX: 0.5,
anchorY: 0.5
});
var bullseye = self.attachAsset('targetRing4', {
anchorX: 0.5,
anchorY: 0.5
});
// Target properties
self.radius = 100; // Outer radius
self.direction = 1; // 1 for right, -1 for left
self.speed = 2;
self.minX = 300;
self.maxX = 1748; // 2048 - 300
// Score values for each ring
self.scoreValues = [10, 25, 50, 75, 100]; // Outer to inner
// Update target movement
self.update = function () {
if (!self["static"]) {
self.x += self.speed * self.direction;
// Reverse direction at edges
if (self.x > self.maxX || self.x < self.minX) {
self.direction *= -1;
}
}
};
// Get score based on distance from center
self.getScore = function (hitX, hitY) {
// Calculate distance from center
var dx = hitX - self.x;
var dy = hitY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Determine which ring was hit
if (distance < 20) {
return self.scoreValues[4]; // Bullseye
} else if (distance < 40) {
return self.scoreValues[3];
} else if (distance < 60) {
return self.scoreValues[2];
} else if (distance < 80) {
return self.scoreValues[1];
} else if (distance < 100) {
return self.scoreValues[0]; // Outer ring
}
return 0; // Miss
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB // Sky blue
});
/****
* Game Code
****/
// Game state variables
var arrows = [];
var targets = [];
var currentArrow = null;
var arrowCount = 0;
var levels = [{
targets: 3,
arrows: 5
}, {
targets: 5,
arrows: 7
}, {
targets: 7,
arrows: 9
}, {
targets: 9,
arrows: 11
}];
var currentLevel = 0;
var maxArrows = levels[currentLevel].arrows;
var totalScore = 0;
var gameActive = true;
var aimPoint = {
x: 0,
y: 0
};
var dragStart = {
x: 0,
y: 0
};
var isDragging = false;
// Create the background
var skyBackground = game.addChild(LK.getAsset('sky', {
anchorX: 0,
anchorY: 0,
y: 0
}));
var ground = game.addChild(LK.getAsset('ground', {
anchorX: 0,
anchorY: 0,
y: 2532 // Position at bottom of screen
}));
// Create bow
var bow = new Bow();
bow.x = 1024; // Center the bow horizontally on the screen
bow.y = 2500; // Position near bottom
game.addChild(bow);
// Create power meter
var powerMeter = new PowerMeter();
powerMeter.x = 100;
powerMeter.y = 1800;
game.addChild(powerMeter);
// Create targets
function createTarget(x, y, isStatic, speed) {
var target = new Target();
target.x = x;
target.y = y;
target["static"] = isStatic;
if (speed !== undefined) {
target.speed = speed;
}
game.addChild(target);
targets.push(target);
return target;
}
// Create initial targets based on current level
function initializeTargets() {
targets = [];
for (var i = 0; i < levels[currentLevel].targets; i++) {
var x = 300 + Math.random() * 1448; // Random x position within bounds
var y = 500 + i * 300; // Staggered y positions
var speed = 2 + Math.random() * 4; // Random speed between 2 and 6
createTarget(x, y, false, speed);
}
}
initializeTargets();
// Score display
var scoreTxt = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Arrows left display
var arrowsTxt = new Text2('Arrows: ' + (maxArrows - arrowCount) + '/' + maxArrows, {
size: 80,
fill: 0xFFFFFF
});
arrowsTxt.anchor.set(0.5, 0);
arrowsTxt.y = 100;
LK.gui.top.addChild(arrowsTxt);
// High score display
var highScoreTxt = new Text2('High Score: ' + storage.highScore, {
size: 60,
fill: 0xFFFFFF
});
highScoreTxt.anchor.set(1, 0);
highScoreTxt.y = 20;
LK.gui.topRight.addChild(highScoreTxt);
// Level display
var levelTxt = new Text2('Level: ' + (currentLevel + 1), {
size: 80,
fill: 0xFFFFFF
});
levelTxt.anchor.set(0.5, 0);
levelTxt.y = 180;
LK.gui.top.addChild(levelTxt);
// Aim guide
var aimGuideTxt = new Text2('Touch and drag to aim, release to shoot', {
size: 60,
fill: 0xFFFFFF
});
aimGuideTxt.anchor.set(0.5, 0);
aimGuideTxt.y = 200;
LK.gui.top.addChild(aimGuideTxt);
// Create a new arrow
function createArrow() {
if (arrowCount >= maxArrows) {
return null;
}
var arrow = new Arrow();
arrow.x = bow.x;
arrow.y = bow.y;
arrow.rotation = bow.aimAngle;
game.addChild(arrow);
arrows.push(arrow);
arrowCount++;
// Update arrows left display
arrowsTxt.setText('Arrows: ' + (maxArrows - arrowCount) + '/' + maxArrows);
return arrow;
}
// Handle collision between arrow and targets
function checkArrowCollisions() {
for (var i = 0; i < arrows.length; i++) {
var arrow = arrows[i];
if (arrow.flying && !arrow.stuck) {
// Check collision with each target
for (var j = 0; j < targets.length; j++) {
var target = targets[j];
// Arrow tip position
var tipX = arrow.x + Math.cos(arrow.rotation) * 65;
var tipY = arrow.y + Math.sin(arrow.rotation) * 65;
// Check if tip is inside target
var dx = tipX - target.x;
var dy = tipY - target.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= target.radius) {
// Hit! Calculate score
var score = target.getScore(tipX, tipY);
totalScore += score;
scoreTxt.setText('Score: ' + totalScore);
// Stick arrow to target and remove from screen
arrow.stickTo(target, score);
arrow.destroy();
arrows.splice(i, 1);
// Create explosion effect
var explosion = new Container();
var explosionParticle = LK.getAsset('target', {
anchorX: 0.5,
anchorY: 0.5
});
explosion.addChild(explosionParticle);
explosion.x = target.x;
explosion.y = target.y;
game.addChild(explosion);
// Animate explosion effect
tween(explosion, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
explosion.destroy();
}
});
// Remove the hit target
targets.splice(j, 1);
target.destroy();
// Create score popup
var scorePop = new Text2("+" + score, {
size: 60,
fill: 0xFFFF00
});
scorePop.x = tipX;
scorePop.y = tipY;
game.addChild(scorePop);
// Animate score popup
tween(scorePop, {
y: scorePop.y - 100,
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
scorePop.destroy();
}
});
break;
}
}
}
// Move arrows that are stuck to targets with the targets
if (arrow.stuck && arrow.targetHit) {
var targetPos = arrow.targetHit;
var dx = arrow.x - targetPos.x;
var dy = arrow.y - targetPos.y;
arrow.x = targetPos.x + dx;
arrow.y = targetPos.y + dy;
}
}
}
// Check if game is over
function checkGameOver() {
if (arrowCount >= maxArrows) {
var allArrowsLanded = true;
// Check if all arrows have landed
for (var i = 0; i < arrows.length; i++) {
if (arrows[i].flying) {
allArrowsLanded = false;
break;
}
}
if (allArrowsLanded && gameActive) {
gameActive = false;
// Update high score if needed
if (totalScore > storage.highScore) {
storage.highScore = totalScore;
highScoreTxt.setText('High Score: ' + storage.highScore);
}
// Check if all targets are hit
if (targets.length === 0 && arrowCount < maxArrows) {
currentLevel++;
if (currentLevel < levels.length) {
// Advance to next level
maxArrows = levels[currentLevel].arrows;
arrowCount = 0;
arrows = [];
initializeTargets();
gameActive = true;
arrowsTxt.setText('Arrows: ' + (maxArrows - arrowCount) + '/' + maxArrows);
} else {
// All levels completed, show game over
LK.setTimeout(function () {
LK.showGameOver();
}, 2000);
}
} else {
// Wait 2 seconds before showing game over
LK.setTimeout(function () {
LK.showGameOver();
}, 2000);
}
}
}
}
// Game loop
game.update = function () {
// Update all targets
for (var i = 0; i < targets.length; i++) {
targets[i].update();
}
// Update all arrows
for (var i = 0; i < arrows.length; i++) {
arrows[i].update();
}
// Update bow charging
if (bow.charging) {
bow.updateCharging();
powerMeter.updatePower(bow.power, bow.maxPower);
}
// Check for collisions
checkArrowCollisions();
// Check if game is over
checkGameOver();
};
// Touch/mouse events
game.down = function (x, y, obj) {
if (!gameActive || arrowCount >= maxArrows) {
return;
}
isDragging = true;
dragStart.x = x;
dragStart.y = y;
// Start charging bow
bow.startCharging();
// Hide aim guide
aimGuideTxt.alpha = 0;
// Create new arrow
currentArrow = createArrow();
};
game.move = function (x, y, obj) {
if (!isDragging || !currentArrow) {
return;
}
// Calculate aim angle
var dx = dragStart.x - x;
var dy = dragStart.y - y;
var angle = Math.atan2(dy, dx);
// Constrain angle (can't aim down into ground or directly backward)
if (angle > 0 && angle < Math.PI / 2) {
angle = 0;
} else if (angle > Math.PI / 2 && angle < Math.PI) {
angle = Math.PI;
}
bow.aimAngle = angle;
// Position current arrow
currentArrow.x = bow.x;
currentArrow.y = bow.y;
currentArrow.rotation = angle;
};
game.up = function (x, y, obj) {
if (!isDragging || !currentArrow) {
return;
}
isDragging = false;
// Fire arrow with current power and angle
var power = bow.releaseArrow();
currentArrow.fire(power, bow.aimAngle);
// Reset power meter
powerMeter.updatePower(0, bow.maxPower);
currentArrow = null;
};
// Play background music
LK.playMusic('bgMusic'); ===================================================================
--- original.js
+++ change.js
@@ -324,8 +324,16 @@
});
highScoreTxt.anchor.set(1, 0);
highScoreTxt.y = 20;
LK.gui.topRight.addChild(highScoreTxt);
+// Level display
+var levelTxt = new Text2('Level: ' + (currentLevel + 1), {
+ size: 80,
+ fill: 0xFFFFFF
+});
+levelTxt.anchor.set(0.5, 0);
+levelTxt.y = 180;
+LK.gui.top.addChild(levelTxt);
// Aim guide
var aimGuideTxt = new Text2('Touch and drag to aim, release to shoot', {
size: 60,
fill: 0xFFFFFF
a sharp cartoon-style arrow flying to the right, with colorful string, 2D game asset, minimal shading, pixel-perfect edges, isolated, flat art style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A 2D flat digital illustration of a sharp and colorful cartoonish sleek arrow tip, designed for a mobile archery game. The arrow tip should be metallic (steel or iron), with subtle gradients for a polished look. It should have a pointed triangular shape with a slightly stylized, game-friendly appearance, matching the clean, minimal aesthetic of vector-based graphics. Use a transparent background and ensure it's facing upward. Resolution: 512x512.". Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
"A 2D digital illustration of a bright, cartoony sky background for a mobile archery game. The sky is a soft gradient from light blue at the top to pale near the horizon. Include fluffy white clouds scattered naturally. Style is flat, colorful, and minimal—perfect for a fun, casual game. No sun or dramatic lighting. Resolution: 1920x1080. Seamless and loopable edges. Transparent-free, clean background.". Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows