User prompt
Shooter kare bir şekil atiyor. Bunu düzeltir misin
User prompt
Shooter kılavuz çizgisi daha açık bir renk olsun
User prompt
Shooter kılavuz çizgisi oyun baslar başlamaz gözüksün
User prompt
Scooter kılavuz çizgisi biraz daha kalın ve anlaşılır olsun açık bir renk olabilir.
User prompt
Shooter kılavuz çizgisi sürekli gözüksün
User prompt
Spiral yol noktalarına benzer sekilde shooter için de baktıği yönde bir kılavuz çizgisi çiz.
User prompt
Shooter yuvarlak atsın bazen başka şekil atıyor. Tüm kodlarda düzelt
User prompt
Shooter bazen elips harici atiyor tüm kodları kontrol et ve düzelt
User prompt
Shooter bazen kare atıyor bunu duzelt
User prompt
Toplar çok yavaş hareket etsinler
User prompt
Toplar arasındaki mesafeyi arttiŕ
User prompt
Yol noktaları arasındaki mesafeyi arttir
User prompt
Topların merkezleri yol noktalarinda olsun
User prompt
Yol noktaları arasındaki mesafeyi biraz daha arttir
User prompt
Yol noktaları arasındaki mesafeyi biraz arttir
User prompt
Toplar üst üste gelmesinler
User prompt
Topların çeperleri arasındaki mesafe 3px olsun
User prompt
Toplar daha yavaş ve 1 pixel aralıklı hareket etsinler
User prompt
Frog hep top fırlatsın
User prompt
Topların çeperleri arasındaki mesafe 0 olsun
User prompt
Toplar Çok daha yavaş hareket etsinler
User prompt
Toplar arasındaki mesafe hep sıfır kalsın. Araya top girdiğinde aralarındaki mesafe değişmesin
User prompt
Spiral yolundaki noktaların aralıkları eşit olsunlar.
User prompt
Toplar spiral yolundaki noktalarda hareket etsinler.
User prompt
Spiral yolundaki noktalar eşit aralıkta olsun.
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Ball class for both chain and shot balls
var Ball = Container.expand(function () {
var self = Container.call(this);
// color: 'red', 'blue', 'green', 'yellow'
self.color = 'red';
self.radius = 45; // 90/2, for collision
self.isChain = true; // true if part of chain, false if shot
// Attach correct asset
var assetId = 'ball_' + self.color;
var ballAsset = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
// For chain balls, we keep a t value (0-1) for position along the path
self.t = 0;
// For shot balls, we keep velocity
self.vx = 0;
self.vy = 0;
// For chain balls, we keep index in chain
self.chainIndex = 0;
// For shot balls, we keep a flag if it's active
self.active = true;
// Set color and update asset
self.setColor = function (color) {
self.color = color;
var assetId = 'ball_' + color;
// Remove old asset
if (ballAsset) ballAsset.destroy();
// Attach new asset
var newAsset = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
};
return self;
});
// Shooter class
var Shooter = Container.expand(function () {
var self = Container.call(this);
// Attach shooter asset
var shooterAsset = self.attachAsset('shooter', {
anchorX: 0.5,
anchorY: 0.5
});
// The ball to be shot (preview)
self.previewBall = null;
// The angle the shooter is aiming (radians)
self.angle = -Math.PI / 2;
// Set preview ball color
self.setPreviewColor = function (color) {
if (self.previewBall) {
self.previewBall.destroy();
}
self.previewBall = new Ball();
self.previewBall.isChain = false;
self.previewBall.setColor(color);
self.previewBall.x = 0;
self.previewBall.y = -60;
self.addChild(self.previewBall);
};
// Set shooter angle (radians)
self.setAngle = function (angle) {
self.angle = angle;
self.rotation = angle + Math.PI / 2;
if (self.previewBall) {
self.previewBall.x = Math.cos(angle) * 60;
self.previewBall.y = Math.sin(angle) * 60;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// Ball pop sound
// Ball shoot sound
// Shooter (frog or cannon, simple green ellipse for MVP)
// Ball shapes (4 colors for MVP)
// --- Spiral Path Definition ---
/*
We'll define a spiral path as a set of points (x, y) along which the chain balls move.
For MVP, use a simple Archimedean spiral centered in the screen.
We'll precompute N points along the spiral and interpolate between them.
*/
var PATH_POINTS = [];
var PATH_LENGTH = 800; // Number of points along the path (reduced for greater spacing)
var SPIRAL_TURNS = 2.2; // How many turns
var SPIRAL_RADIUS = 900; // Max radius
var PATH_CENTER_X = 2048 / 2;
var PATH_CENTER_Y = 1200; // Slightly above center
// Spiral path visual: draw as small dots
var spiralPathDots = new Container();
game.addChild(spiralPathDots);
(function generateSpiralPath() {
// First, generate a high-res spiral
var HIGH_RES = 6000;
var spiralHighRes = [];
for (var i = 0; i < HIGH_RES; i++) {
var t = i / (HIGH_RES - 1); // 0 to 1
var angle = SPIRAL_TURNS * 2 * Math.PI * (1 - t) + Math.PI; // Reverse spiral: start outside, go in
var radius = 180 + SPIRAL_RADIUS * (1 - t);
var x = PATH_CENTER_X + Math.cos(angle) * radius;
var y = PATH_CENTER_Y + Math.sin(angle) * radius;
// Mirror x around center to flip horizontally
x = PATH_CENTER_X - (x - PATH_CENTER_X);
spiralHighRes.push({
x: x,
y: y
});
}
// Compute cumulative arc length
var arcLen = [0];
for (var i = 1; i < spiralHighRes.length; i++) {
var dx = spiralHighRes[i].x - spiralHighRes[i - 1].x;
var dy = spiralHighRes[i].y - spiralHighRes[i - 1].y;
arcLen[i] = arcLen[i - 1] + Math.sqrt(dx * dx + dy * dy);
}
var totalLen = arcLen[arcLen.length - 1];
// Now, sample PATH_LENGTH points at equal arc length intervals
for (var i = 0; i < PATH_LENGTH; i++) {
var targetLen = i * totalLen / (PATH_LENGTH - 1);
// Binary search for the closest arcLen index
var lo = 0,
hi = arcLen.length - 1;
while (lo < hi) {
var mid = Math.floor((lo + hi) / 2);
if (arcLen[mid] < targetLen) lo = mid + 1;else hi = mid;
}
var idx = lo;
// Interpolate between idx-1 and idx
var p0 = spiralHighRes[Math.max(0, idx - 1)];
var p1 = spiralHighRes[idx];
var l0 = arcLen[Math.max(0, idx - 1)];
var l1 = arcLen[idx];
var frac = l1 - l0 > 0 ? (targetLen - l0) / (l1 - l0) : 0;
var x = p0.x + (p1.x - p0.x) * frac;
var y = p0.y + (p1.y - p0.y) * frac;
PATH_POINTS.push({
x: x,
y: y
});
// Draw a small dot every 8th point for performance
if (i % 8 === 0) {
var dot = LK.getAsset('ball_blue', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.18,
scaleY: 0.18,
x: x,
y: y,
alpha: 0.45
});
spiralPathDots.addChild(dot);
}
}
})();
// Helper: get position along path for t in [0,1]
function getPathPos(t) {
var idx = t * (PATH_LENGTH - 1);
var idx0 = Math.floor(idx);
var idx1 = Math.min(PATH_LENGTH - 1, idx0 + 1);
var frac = idx - idx0;
var p0 = PATH_POINTS[idx0];
var p1 = PATH_POINTS[idx1];
if (!p0 || !p1) {
// Defensive: If out of bounds, return center of screen
return {
x: PATH_CENTER_X,
y: PATH_CENTER_Y
};
}
return {
x: p0.x + (p1.x - p0.x) * frac,
y: p0.y + (p1.y - p0.y) * frac
};
}
// Helper: get tangent angle at t
function getPathAngle(t) {
var idx = t * (PATH_LENGTH - 1);
var idx0 = Math.max(0, Math.floor(idx) - 2);
var idx1 = Math.min(PATH_LENGTH - 1, idx0 + 4);
var p0 = PATH_POINTS[idx0];
var p1 = PATH_POINTS[idx1];
return Math.atan2(p1.y - p0.y, p1.x - p0.x);
}
// --- Chain State ---
var chainBalls = []; // Array of Ball (in order, head at index 0)
var chainSpacing = 90 + 3; // px between centers (ball diameter + 3px gap between edges)
var chainSpeed = 0.0005; // t per tick (much slower movement)
var chainHeadT = 0; // t of the head ball (0=start, 1=end)
var chainTailT = 0; // t of the tail ball
// --- Shooter State ---
var shooter = new Shooter();
game.addChild(shooter);
// Place shooter at spiral center (PATH_CENTER_X, PATH_CENTER_Y)
shooter.x = PATH_CENTER_X;
shooter.y = PATH_CENTER_Y;
// --- Shot Balls State ---
var shotBalls = []; // Array of Ball
// --- Game State ---
var colors = ['red', 'blue', 'green', 'yellow'];
var nextBallColor = colors[Math.floor(Math.random() * colors.length)];
var score = 0;
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// --- Initialize Chain ---
function initChain() {
chainBalls = [];
var N = 18; // Initial number of balls
var t = 0.0;
for (var i = 0; i < N; i++) {
var ball = new Ball();
var color = colors[Math.floor(Math.random() * colors.length)];
ball.setColor(color);
ball.isChain = true;
ball.t = t;
ball.chainIndex = i;
var pos = getPathPos(t);
ball.x = pos.x;
ball.y = pos.y;
game.addChild(ball);
chainBalls.push(ball);
t += chainSpacing / SPIRAL_RADIUS / (SPIRAL_TURNS * 2 * Math.PI); // Approximate t step (now with 3px edge gap)
}
chainHeadT = chainBalls[0].t;
chainTailT = chainBalls[chainBalls.length - 1].t;
}
initChain();
// --- Initialize Shooter ---
shooter.setPreviewColor(nextBallColor);
shooter.setAngle(-Math.PI / 2);
// --- Dragging / Aiming State ---
var isAiming = false;
// --- Helper: Find angle from shooter to (x, y) ---
function getShooterAngleTo(x, y) {
var dx = x - shooter.x;
var dy = y - shooter.y;
return Math.atan2(dy, dx);
}
// --- Helper: Find nearest t on path to (x, y) ---
function findNearestT(x, y) {
// Brute force for MVP
var minDist = 1e9;
var minIdx = 0;
for (var i = 0; i < PATH_POINTS.length; i += 8) {
// Step for speed
var p = PATH_POINTS[i];
var dx = p.x - x;
var dy = p.y - y;
var d = dx * dx + dy * dy;
if (d < minDist) {
minDist = d;
minIdx = i;
}
}
return minIdx / (PATH_LENGTH - 1);
}
// --- Game Events ---
// Aim shooter
game.move = function (x, y, obj) {
if (!isAiming) return;
var angle = getShooterAngleTo(x, y);
shooter.setAngle(angle);
};
// Start aiming
game.down = function (x, y, obj) {
// Only allow aiming if touch is not on top 400px (avoid accidental shots)
if (y < 400) return;
isAiming = true;
var angle = getShooterAngleTo(x, y);
shooter.setAngle(angle);
};
// Shoot ball
game.up = function (x, y, obj) {
if (!isAiming) return;
isAiming = false;
// Fire a ball in the current shooter.angle
var ball = new Ball();
ball.isChain = false;
ball.setColor(nextBallColor);
var angle = shooter.angle;
var speed = 32; // px per tick
ball.vx = Math.cos(angle) * speed;
ball.vy = Math.sin(angle) * speed;
ball.x = shooter.x + Math.cos(angle) * 60;
ball.y = shooter.y + Math.sin(angle) * 60;
ball.active = true;
shotBalls.push(ball);
game.addChild(ball);
// Play shoot sound
LK.getSound('shoot').play();
// Next ball color
nextBallColor = colors[Math.floor(Math.random() * colors.length)];
shooter.setPreviewColor(nextBallColor);
};
// --- Helper: Insert ball into chain at index ---
function insertBallIntoChain(ball, insertIdx, insertT) {
// Insert ball into chainBalls at insertIdx, set t=insertT
ball.isChain = true;
ball.t = insertT;
ball.chainIndex = insertIdx;
// Remove from shotBalls
for (var i = 0; i < shotBalls.length; i++) {
if (shotBalls[i] === ball) {
shotBalls.splice(i, 1);
break;
}
}
// Insert into chainBalls
chainBalls.splice(insertIdx, 0, ball);
// Re-index and recalculate t for all balls to keep zero gap
for (var i = 0; i < chainBalls.length; i++) {
chainBalls[i].chainIndex = i;
chainBalls[i].t = chainHeadT + i * (chainSpacing / SPIRAL_RADIUS / (SPIRAL_TURNS * 2 * Math.PI));
}
}
// --- Helper: Check for matches and pop balls ---
function checkAndPopMatches() {
// Find runs of 3+ same color
var i = 0;
while (i < chainBalls.length) {
var color = chainBalls[i].color;
var j = i + 1;
while (j < chainBalls.length && chainBalls[j].color === color) {
j++;
}
var runLen = j - i;
if (runLen >= 3) {
// Pop balls i to j-1
for (var k = i; k < j; k++) {
var b = chainBalls[k];
// Animate pop
tween(b, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 220,
easing: tween.easeOut,
onFinish: function (ball) {
return function () {
ball.destroy();
};
}(b)
});
}
// Remove from chainBalls
chainBalls.splice(i, runLen);
// Play pop sound
LK.getSound('pop').play();
// Update score
score += runLen * 10;
scoreTxt.setText(score);
// Re-index and recalculate t for all balls to keep zero gap
for (var m = 0; m < chainBalls.length; m++) {
chainBalls[m].chainIndex = m;
chainBalls[m].t = chainHeadT + m * (chainSpacing / SPIRAL_RADIUS / (SPIRAL_TURNS * 2 * Math.PI));
}
// After popping, check again from start
return true;
}
i = j;
}
return false;
}
// --- Game Update Loop ---
game.update = function () {
// --- Move chain balls forward ---
if (chainBalls.length > 0) {
// Move head forward
chainHeadT += chainSpeed;
// Clamp
if (chainHeadT > 1) chainHeadT = 1;
// Each ball follows the previous at zero gap (all balls touch)
for (var i = 0; i < chainBalls.length; i++) {
// Calculate t for this ball
var t = chainHeadT + i * (chainSpacing / SPIRAL_RADIUS / (SPIRAL_TURNS * 2 * Math.PI));
// Clamp t to [0,1]
if (t > 1) t = 1;
var ball = chainBalls[i];
// Prevent overlap: if not the first ball, ensure this ball is at least chainSpacing away from the previous
if (i > 0) {
var prevBall = chainBalls[i - 1];
// Calculate the minimum t needed to keep spacing
var minT = prevBall.t + chainSpacing / SPIRAL_RADIUS / (SPIRAL_TURNS * 2 * Math.PI);
if (t < minT) t = minT;
}
ball.t = t;
var pos = getPathPos(t);
ball.x = pos.x;
ball.y = pos.y;
}
chainTailT = chainBalls.length > 0 ? chainBalls[chainBalls.length - 1].t : chainHeadT;
}
// --- Move shot balls ---
for (var i = shotBalls.length - 1; i >= 0; i--) {
var ball = shotBalls[i];
if (!ball.active) continue;
ball.x += ball.vx;
ball.y += ball.vy;
// Out of bounds
if (ball.x < -100 || ball.x > 2148 || ball.y < -100 || ball.y > 2832) {
ball.destroy();
shotBalls.splice(i, 1);
continue;
}
// --- Collision with chain balls ---
var minDist = 1e9;
var hitIdx = -1;
var hitT = 0;
for (var j = 0; j < chainBalls.length; j++) {
var cb = chainBalls[j];
var dx = ball.x - cb.x;
var dy = ball.y - cb.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < ball.radius * 1.15) {
if (dist < minDist) {
minDist = dist;
hitIdx = j;
// Find t between cb and previous/next
if (j === 0) {
hitT = cb.t - chainSpacing / SPIRAL_RADIUS / (SPIRAL_TURNS * 2 * Math.PI);
} else {
hitT = (cb.t + chainBalls[j - 1].t) / 2;
}
}
}
}
if (hitIdx !== -1) {
// Insert ball into chain at hitIdx
insertBallIntoChain(ball, hitIdx, hitT);
// Animate insertion
tween(ball, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 80,
onFinish: function onFinish() {
tween(ball, {
scaleX: 1,
scaleY: 1
}, {
duration: 80
});
}
});
// After insertion, check for matches
while (checkAndPopMatches()) {}
continue;
}
}
// --- Check for game over (tail reaches end) ---
if (chainBalls.length > 0 && chainBalls[chainBalls.length - 1].t >= 1) {
// Flash screen red
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
// --- Check for win (all balls popped) ---
if (chainBalls.length === 0) {
LK.showYouWin();
return;
}
};
// --- Increase difficulty over time ---
var level = 1;
var levelTimer = LK.setInterval(function () {
level++;
if (level % 2 === 0 && colors.length < 5) {
// Add a new color (for MVP, max 5)
var newColors = ['purple', 'orange', 'cyan', 'pink'];
var colorNames = ['purple', 'orange', 'cyan', 'pink'];
if (colors.length < colorNames.length + 4) {
var c = colorNames[colors.length - 4];
var col = 0x9b59b6; // purple
if (c === 'orange') col = 0xff9500;
if (c === 'cyan') col = 0x5ac8fa;
if (c === 'pink') col = 0xff2d55;
colors.push(c);
}
}
// Increase speed
chainSpeed += 0.00008;
}, 9000); ===================================================================
--- original.js
+++ change.js
@@ -98,9 +98,9 @@
For MVP, use a simple Archimedean spiral centered in the screen.
We'll precompute N points along the spiral and interpolate between them.
*/
var PATH_POINTS = [];
-var PATH_LENGTH = 1200; // Number of points along the path
+var PATH_LENGTH = 800; // Number of points along the path (reduced for greater spacing)
var SPIRAL_TURNS = 2.2; // How many turns
var SPIRAL_RADIUS = 900; // Max radius
var PATH_CENTER_X = 2048 / 2;
var PATH_CENTER_Y = 1200; // Slightly above center
Mistik mısır ve aztek resmi çöl gibi bir spiral ve sarı renk agırlıklı low poly nesneler ve su akıyor efekti. In-Game asset. 2d. High contrast. No shadows
thick and yellow color object edges
make the ball appear on the screen much more smoothly like bubble
make the ball appear on the screen much more smoothly like bubble
make the ball appear on the screen much more smoothly like bubble
yellow