User prompt
Çarpışma çözüm sistemini öngörücü yap
User prompt
Toplar birnirinin içinden geçiyor
User prompt
Toplar birbirinin içinden geçemez
User prompt
Toplar biribirinin üzerine çarptıgında zeminden dışarı cıkıyor
User prompt
Topladı biraz büyült
User prompt
Topları büyült
User prompt
Başlangıcta konteynırın birazı toplarla dolu olsun
User prompt
Toplar konteynır alt çizgisinden aşagı cıkıyor
User prompt
Toplar zemin çizgisinden aşagı düşüyor
User prompt
Olmuyor top yukardan aşagı dogru düşmeye başladıgında zemine varmadan tehlike bölgesine degiyor ve oyun biyitor
User prompt
Top yukardan düşmeye başladıgında tehlike bölgesine dokunup oyun bitti diyor
User prompt
Çalışmıyor
User prompt
Oyun barlıklarını ekle
User prompt
Herşeyi ayarla
Code edit (1 edits merged)
Please save this source code
User prompt
Ball Merge 2048
Initial prompt
Toplarla oynanan 2048 oyunu
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Ball = Container.expand(function (value) {
var self = Container.call(this);
self.value = value || 2;
self.velocityX = 0;
self.velocityY = 0;
self.gravity = 0.8;
self.bounce = 0.4;
self.friction = 0.98;
self.radius = 60;
self.isStatic = false;
self.mergeTimer = 0;
self.hasBeenMerged = false;
var ballAsset = self.attachAsset('ball' + self.value, {
anchorX: 0.5,
anchorY: 0.5
});
var valueText = new Text2(self.value.toString(), {
size: 42,
fill: 0xFFFFFF
});
valueText.anchor.set(0.5, 0.5);
self.addChild(valueText);
self.update = function () {
if (self.hasBeenMerged) return;
if (self.mergeTimer > 0) {
self.mergeTimer--;
return;
}
if (!self.isStatic) {
self.velocityY += self.gravity;
self.x += self.velocityX;
self.y += self.velocityY;
// Ground collision (local coordinates) - account for bottom wall thickness
var localBottom = gameAreaHeight - 20; // gameAreaHeight minus bottom wall height
if (self.y + self.radius > localBottom) {
self.y = localBottom - self.radius;
self.velocityY *= -self.bounce;
self.velocityX *= self.friction;
if (Math.abs(self.velocityY) < 1) {
self.velocityY = 0;
self.isStatic = true;
}
}
// Side walls collision (local coordinates)
var localWidth = gameAreaRight - gameAreaLeft;
if (self.x - self.radius < 0) {
self.x = self.radius;
self.velocityX *= -self.bounce;
}
if (self.x + self.radius > localWidth) {
self.x = localWidth - self.radius;
self.velocityX *= -self.bounce;
}
// Predictive collision detection - check future positions to prevent overlap
for (var i = 0; i < balls.length; i++) {
var otherBall = balls[i];
if (otherBall === self || otherBall.hasBeenMerged) continue;
// Current positions and distances
var currentDx = otherBall.x - self.x;
var currentDy = otherBall.y - self.y;
var currentDistance = Math.sqrt(currentDx * currentDx + currentDy * currentDy);
var minDistance = self.radius + otherBall.radius;
// Predict future positions after velocity is applied
var futureX = self.x + self.velocityX;
var futureY = self.y + self.velocityY;
var otherFutureX = otherBall.x + otherBall.velocityX;
var otherFutureY = otherBall.y + otherBall.velocityY;
// Calculate future distance
var futureDx = otherFutureX - futureX;
var futureDy = otherFutureY - futureY;
var futureDistance = Math.sqrt(futureDx * futureDx + futureDy * futureDy);
// If balls will collide in the next frame, handle collision now
var willCollide = futureDistance < minDistance;
var isCurrentlyTouching = currentDistance <= minDistance;
if (willCollide || isCurrentlyTouching) {
// Use current position for collision calculations if already touching, future otherwise
var dx = isCurrentlyTouching ? currentDx : futureDx;
var dy = isCurrentlyTouching ? currentDy : futureDy;
var distance = isCurrentlyTouching ? currentDistance : futureDistance;
if (distance > 0) {
// Check for merge first
if (self.value === otherBall.value && self.mergeTimer === 0 && otherBall.mergeTimer === 0) {
// Merge balls
var newValue = self.value * 2;
if (newValue <= 2048) {
var newBall = new Ball(newValue);
newBall.x = (self.x + otherBall.x) / 2;
newBall.y = (self.y + otherBall.y) / 2;
newBall.velocityX = (self.velocityX + otherBall.velocityX) / 2;
newBall.velocityY = (self.velocityY + otherBall.velocityY) / 2;
newBall.mergeTimer = 10;
balls.push(newBall);
gameArea.addChild(newBall);
// Mark for removal
self.hasBeenMerged = true;
otherBall.hasBeenMerged = true;
LK.getSound('merge').play();
LK.setScore(LK.getScore() + newValue);
scoreText.setText(LK.getScore());
// Check win condition
if (newValue === 2048) {
LK.showYouWin();
}
return;
}
}
// Predictive collision response - adjust velocities to prevent overlap
var normalX = dx / distance;
var normalY = dy / distance;
// Calculate relative velocity
var relativeVelX = self.velocityX - otherBall.velocityX;
var relativeVelY = self.velocityY - otherBall.velocityY;
// Calculate collision speed along normal
var speed = relativeVelX * normalX + relativeVelY * normalY;
// Only resolve if objects are moving towards each other
if (speed > 0) {
// Calculate collision impulse
var impulse = 2 * speed / 2; // Assume equal mass
var impulseX = impulse * normalX * 0.8; // Add damping factor
var impulseY = impulse * normalY * 0.8;
// Apply impulse to prevent collision
self.velocityX -= impulseX;
self.velocityY -= impulseY;
otherBall.velocityX += impulseX;
otherBall.velocityY += impulseY;
// Apply friction
self.velocityX *= self.friction;
self.velocityY *= self.friction;
otherBall.velocityX *= otherBall.friction;
otherBall.velocityY *= otherBall.friction;
}
// If balls are already overlapping, separate them immediately
if (isCurrentlyTouching && distance < minDistance) {
var overlap = minDistance - distance;
var separationX = normalX * overlap * 0.5;
var separationY = normalY * overlap * 0.5;
self.x -= separationX;
self.y -= separationY;
otherBall.x += separationX;
otherBall.y += separationY;
// Ensure balls stay within bounds after separation
var localWidth = gameAreaRight - gameAreaLeft;
var localBottom = gameAreaHeight - 20; // gameAreaHeight minus bottom wall height
self.x = Math.max(self.radius, Math.min(localWidth - self.radius, self.x));
self.y = Math.max(self.radius, Math.min(localBottom - self.radius, self.y));
otherBall.x = Math.max(otherBall.radius, Math.min(localWidth - otherBall.radius, otherBall.x));
otherBall.y = Math.max(otherBall.radius, Math.min(localBottom - otherBall.radius, otherBall.y));
}
}
}
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x34495e
});
/****
* Game Code
****/
var balls = [];
var nextBallValue = 2;
var gameAreaLeft = 224;
var gameAreaRight = 1824;
var gameAreaTop = 400;
var gameAreaBottom = 2600;
var dropCooldown = 0;
var gameAreaHeight = gameAreaBottom - gameAreaTop;
// Create game area background
var gameAreaBg = game.addChild(LK.getAsset('gameArea', {
anchorX: 0.5,
anchorY: 0,
x: 1024,
y: gameAreaTop
}));
// Create game area container for balls
var gameArea = new Container();
gameArea.x = gameAreaLeft;
gameArea.y = gameAreaTop;
game.addChild(gameArea);
// Create visible walls
var leftWall = game.addChild(LK.getAsset('leftWall', {
anchorX: 1,
anchorY: 0,
x: gameAreaLeft,
y: gameAreaTop
}));
var rightWall = game.addChild(LK.getAsset('rightWall', {
anchorX: 0,
anchorY: 0,
x: gameAreaRight,
y: gameAreaTop
}));
var bottomWall = game.addChild(LK.getAsset('bottomWall', {
anchorX: 0.5,
anchorY: 0,
x: 1024,
y: gameAreaBottom
}));
// Create danger zone indicator
var dangerZone = game.addChild(LK.getAsset('dangerZone', {
anchorX: 0.5,
anchorY: 0,
x: 1024,
y: gameAreaTop,
alpha: 0.3
}));
// Create game over line
var gameOverLine = game.addChild(LK.getAsset('gameOverLine', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: gameAreaTop + 100
}));
// Create score display
var scoreText = new Text2('0', {
size: 80,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
scoreText.y = 100;
// Create next ball preview
var nextBallPreview = LK.getAsset('ball' + nextBallValue, {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 300,
scaleX: 0.75,
scaleY: 0.75
});
game.addChild(nextBallPreview);
// Create next ball value text
var nextBallText = new Text2(nextBallValue.toString(), {
size: 24,
fill: 0xFFFFFF
});
nextBallText.anchor.set(0.5, 0.5);
nextBallText.x = 1024;
nextBallText.y = 300;
game.addChild(nextBallText);
// Create instruction text
var instructionText = new Text2('Tap to drop ball', {
size: 40,
fill: 0xFFFFFF
});
instructionText.anchor.set(0.5, 0);
instructionText.x = 1024;
instructionText.y = 150;
game.addChild(instructionText);
function getRandomNextBallValue() {
var values = [2, 4, 8];
return values[Math.floor(Math.random() * values.length)];
}
function updateNextBallPreview() {
game.removeChild(nextBallPreview);
game.removeChild(nextBallText);
nextBallPreview = LK.getAsset('ball' + nextBallValue, {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 300,
scaleX: 0.75,
scaleY: 0.75
});
game.addChild(nextBallPreview);
nextBallText = new Text2(nextBallValue.toString(), {
size: 24,
fill: 0xFFFFFF
});
nextBallText.anchor.set(0.5, 0.5);
nextBallText.x = 1024;
nextBallText.y = 300;
game.addChild(nextBallText);
}
function checkGameOver() {
for (var i = 0; i < balls.length; i++) {
// Check if ball touches the danger zone (first 100 pixels of game area)
// Since balls are in gameArea container, we use local coordinates
// Only trigger game over if ball is static (not actively falling) and in danger zone
if (balls[i].y - balls[i].radius <= 100 && balls[i].isStatic) {
LK.showGameOver();
return;
}
}
}
function cleanupMergedBalls() {
for (var i = balls.length - 1; i >= 0; i--) {
if (balls[i].hasBeenMerged) {
gameArea.removeChild(balls[i]);
balls[i].destroy();
balls.splice(i, 1);
}
}
}
game.down = function (x, y, obj) {
if (dropCooldown > 0) return;
// Constrain drop position to game area (convert to local coordinates)
var localWidth = gameAreaRight - gameAreaLeft;
var localX = x - gameAreaLeft;
var dropX = Math.max(60, Math.min(localWidth - 60, localX));
var newBall = new Ball(nextBallValue);
newBall.x = dropX;
newBall.y = 170; // Start below the danger zone (100px) plus ball radius (60px) plus buffer
newBall.velocityX = 0;
newBall.velocityY = 0;
balls.push(newBall);
gameArea.addChild(newBall);
LK.getSound('drop').play();
// Update next ball
nextBallValue = getRandomNextBallValue();
updateNextBallPreview();
dropCooldown = 30; // 0.5 seconds at 60fps
};
game.update = function () {
if (dropCooldown > 0) {
dropCooldown--;
}
cleanupMergedBalls();
checkGameOver();
// Update score display
scoreText.setText(LK.getScore());
}; ===================================================================
--- original.js
+++ change.js
@@ -58,83 +58,104 @@
if (self.x + self.radius > localWidth) {
self.x = localWidth - self.radius;
self.velocityX *= -self.bounce;
}
- // Ball to ball collisions
+ // Predictive collision detection - check future positions to prevent overlap
for (var i = 0; i < balls.length; i++) {
var otherBall = balls[i];
if (otherBall === self || otherBall.hasBeenMerged) continue;
- var dx = otherBall.x - self.x;
- var dy = otherBall.y - self.y;
- var distance = Math.sqrt(dx * dx + dy * dy);
+ // Current positions and distances
+ var currentDx = otherBall.x - self.x;
+ var currentDy = otherBall.y - self.y;
+ var currentDistance = Math.sqrt(currentDx * currentDx + currentDy * currentDy);
var minDistance = self.radius + otherBall.radius;
- if (distance < minDistance && distance > 0) {
- // Check for merge
- if (self.value === otherBall.value && self.mergeTimer === 0 && otherBall.mergeTimer === 0) {
- // Merge balls
- var newValue = self.value * 2;
- if (newValue <= 2048) {
- var newBall = new Ball(newValue);
- newBall.x = (self.x + otherBall.x) / 2;
- newBall.y = (self.y + otherBall.y) / 2;
- newBall.velocityX = (self.velocityX + otherBall.velocityX) / 2;
- newBall.velocityY = (self.velocityY + otherBall.velocityY) / 2;
- newBall.mergeTimer = 10;
- balls.push(newBall);
- gameArea.addChild(newBall);
- // Mark for removal
- self.hasBeenMerged = true;
- otherBall.hasBeenMerged = true;
- LK.getSound('merge').play();
- LK.setScore(LK.getScore() + newValue);
- scoreText.setText(LK.getScore());
- // Check win condition
- if (newValue === 2048) {
- LK.showYouWin();
+ // Predict future positions after velocity is applied
+ var futureX = self.x + self.velocityX;
+ var futureY = self.y + self.velocityY;
+ var otherFutureX = otherBall.x + otherBall.velocityX;
+ var otherFutureY = otherBall.y + otherBall.velocityY;
+ // Calculate future distance
+ var futureDx = otherFutureX - futureX;
+ var futureDy = otherFutureY - futureY;
+ var futureDistance = Math.sqrt(futureDx * futureDx + futureDy * futureDy);
+ // If balls will collide in the next frame, handle collision now
+ var willCollide = futureDistance < minDistance;
+ var isCurrentlyTouching = currentDistance <= minDistance;
+ if (willCollide || isCurrentlyTouching) {
+ // Use current position for collision calculations if already touching, future otherwise
+ var dx = isCurrentlyTouching ? currentDx : futureDx;
+ var dy = isCurrentlyTouching ? currentDy : futureDy;
+ var distance = isCurrentlyTouching ? currentDistance : futureDistance;
+ if (distance > 0) {
+ // Check for merge first
+ if (self.value === otherBall.value && self.mergeTimer === 0 && otherBall.mergeTimer === 0) {
+ // Merge balls
+ var newValue = self.value * 2;
+ if (newValue <= 2048) {
+ var newBall = new Ball(newValue);
+ newBall.x = (self.x + otherBall.x) / 2;
+ newBall.y = (self.y + otherBall.y) / 2;
+ newBall.velocityX = (self.velocityX + otherBall.velocityX) / 2;
+ newBall.velocityY = (self.velocityY + otherBall.velocityY) / 2;
+ newBall.mergeTimer = 10;
+ balls.push(newBall);
+ gameArea.addChild(newBall);
+ // Mark for removal
+ self.hasBeenMerged = true;
+ otherBall.hasBeenMerged = true;
+ LK.getSound('merge').play();
+ LK.setScore(LK.getScore() + newValue);
+ scoreText.setText(LK.getScore());
+ // Check win condition
+ if (newValue === 2048) {
+ LK.showYouWin();
+ }
+ return;
}
- return;
}
+ // Predictive collision response - adjust velocities to prevent overlap
+ var normalX = dx / distance;
+ var normalY = dy / distance;
+ // Calculate relative velocity
+ var relativeVelX = self.velocityX - otherBall.velocityX;
+ var relativeVelY = self.velocityY - otherBall.velocityY;
+ // Calculate collision speed along normal
+ var speed = relativeVelX * normalX + relativeVelY * normalY;
+ // Only resolve if objects are moving towards each other
+ if (speed > 0) {
+ // Calculate collision impulse
+ var impulse = 2 * speed / 2; // Assume equal mass
+ var impulseX = impulse * normalX * 0.8; // Add damping factor
+ var impulseY = impulse * normalY * 0.8;
+ // Apply impulse to prevent collision
+ self.velocityX -= impulseX;
+ self.velocityY -= impulseY;
+ otherBall.velocityX += impulseX;
+ otherBall.velocityY += impulseY;
+ // Apply friction
+ self.velocityX *= self.friction;
+ self.velocityY *= self.friction;
+ otherBall.velocityX *= otherBall.friction;
+ otherBall.velocityY *= otherBall.friction;
+ }
+ // If balls are already overlapping, separate them immediately
+ if (isCurrentlyTouching && distance < minDistance) {
+ var overlap = minDistance - distance;
+ var separationX = normalX * overlap * 0.5;
+ var separationY = normalY * overlap * 0.5;
+ self.x -= separationX;
+ self.y -= separationY;
+ otherBall.x += separationX;
+ otherBall.y += separationY;
+ // Ensure balls stay within bounds after separation
+ var localWidth = gameAreaRight - gameAreaLeft;
+ var localBottom = gameAreaHeight - 20; // gameAreaHeight minus bottom wall height
+ self.x = Math.max(self.radius, Math.min(localWidth - self.radius, self.x));
+ self.y = Math.max(self.radius, Math.min(localBottom - self.radius, self.y));
+ otherBall.x = Math.max(otherBall.radius, Math.min(localWidth - otherBall.radius, otherBall.x));
+ otherBall.y = Math.max(otherBall.radius, Math.min(localBottom - otherBall.radius, otherBall.y));
+ }
}
- // Iterative collision separation to prevent balls from passing through each other
- var localWidth = gameAreaRight - gameAreaLeft;
- var localBottom = gameAreaHeight - 20; // gameAreaHeight minus bottom wall height
- var maxIterations = 5;
- var iteration = 0;
- while (distance < minDistance && distance > 0 && iteration < maxIterations) {
- var overlap = minDistance - distance;
- var separationMultiplier = 1.2; // Stronger separation
- var separationX = dx / distance * overlap * separationMultiplier * 0.5;
- var separationY = dy / distance * overlap * separationMultiplier * 0.5;
- self.x -= separationX;
- self.y -= separationY;
- otherBall.x += separationX;
- otherBall.y += separationY;
- // Constrain positions within bounds
- self.x = Math.max(self.radius, Math.min(localWidth - self.radius, self.x));
- self.y = Math.max(self.radius, Math.min(localBottom - self.radius, self.y));
- otherBall.x = Math.max(otherBall.radius, Math.min(localWidth - otherBall.radius, otherBall.x));
- otherBall.y = Math.max(otherBall.radius, Math.min(localBottom - otherBall.radius, otherBall.y));
- // Recalculate distance for next iteration
- dx = otherBall.x - self.x;
- dy = otherBall.y - self.y;
- distance = Math.sqrt(dx * dx + dy * dy);
- iteration++;
- }
- // Apply velocity exchange only if collision is resolved
- if (distance >= minDistance * 0.95) {
- // Small tolerance to avoid jitter
- var relativeVelocityX = self.velocityX - otherBall.velocityX;
- var relativeVelocityY = self.velocityY - otherBall.velocityY;
- var collisionForce = (relativeVelocityX * dx + relativeVelocityY * dy) / (distance * distance);
- self.velocityX -= collisionForce * dx * 0.8;
- self.velocityY -= collisionForce * dy * 0.8;
- otherBall.velocityX += collisionForce * dx * 0.8;
- otherBall.velocityY += collisionForce * dy * 0.8;
- self.velocityX *= self.friction;
- self.velocityY *= self.friction;
- otherBall.velocityX *= otherBall.friction;
- otherBall.velocityY *= otherBall.friction;
- }
}
}
}
};
Koyu yeşil bilye. In-Game asset. 2d. High contrast. No shadows
Kahverengi bilye. In-Game asset. 2d. High contrast. No shadows
Bprdo renk bilye. In-Game asset. 2d. High contrast. No shadows
Açık kahve bilye. In-Game asset. 2d. High contrast. No shadows
Gri bilye. In-Game asset. 2d. High contrast. No shadows