User prompt
Birleşme animasyonunu geliştir ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Puanlama yazı rengi beyaz olsun
User prompt
Sonraki top tam olarak ekranda gözüksün
User prompt
Sonraki top önizleme gözükmüyor düzelt
User prompt
Oyun alanı yanlardan ve aşagı uzatarak ekranı kapla
User prompt
Puanlama yazısı siyah renk
User prompt
Tapto drop bal yazısını sil
User prompt
Sonrakitop önizleme top konumunu biraz yukarı taşı
User prompt
Sonraki top konumunu oyın alanının üst noktasına taşı
User prompt
Sonraki top önizleme büyült
User prompt
Eger top2048 birleşirse patlayıp ekrandan kaybolsun ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Oyın alanını ve diger varlıkları ekranı kaplayacak şekilde geliştir
User prompt
Arkaplanı kaldır
User prompt
Arkalpanı bej rengi yap
User prompt
Arkaplan için resim varlıgı ekle
User prompt
Tehlikeli bölge görsel uyarısını kaldır
User prompt
Topları daha büyük yap
User prompt
Top boyutlarını büyült
User prompt
Topların resim varlıkları boyutları birbirinden farklı olsun numara ne kadar büyükse topta digerlerinden büyğk olsun
User prompt
Puanlama taplosunu sag üste taşi
User prompt
Nişangahın ucunu top fırlatma alanına sabitle diger tarafı oyuncu hareket ettirebilir
User prompt
Üstten gelen topa nişangah ekle
User prompt
Başlangıcta tehlike alanı 5 saniye iptal ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Tehlike alanı iptal
User prompt
Konteyner başlangıçta agzına kadar dolu olsun
/**** * 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; // Set radius based on ball value for proper physics var radiusMap = { 2: 120, 4: 125, 8: 135, 16: 145, 32: 155, 64: 165, 128: 175, 256: 185, 512: 195, 1024: 200, 2048: 210 }; self.radius = radiusMap[self.value] || 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: 56, 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; } // Apply physics only if not static if (!self.isStatic) { // Store previous position for continuous collision detection var prevX = self.x; var prevY = self.y; // Apply gravity self.velocityY += self.gravity; // Calculate intended new position var newX = self.x + self.velocityX; var newY = self.y + self.velocityY; // Continuous collision detection - check path from current to intended position var stepCount = Math.max(1, Math.ceil(Math.abs(self.velocityX) + Math.abs(self.velocityY)) / (self.radius * 0.5)); var stepX = (newX - self.x) / stepCount; var stepY = (newY - self.y) / stepCount; var collisionOccurred = false; // Check each step along the movement path for (var step = 1; step <= stepCount && !collisionOccurred; step++) { var testX = self.x + stepX * step; var testY = self.y + stepY * step; // Test collision with other balls for (var i = 0; i < balls.length; i++) { var otherBall = balls[i]; if (otherBall === self || otherBall.hasBeenMerged || otherBall.mergeTimer > 0) continue; var dx = otherBall.x - testX; var dy = otherBall.y - testY; var distance = Math.sqrt(dx * dx + dy * dy); var minDistance = self.radius + otherBall.radius; if (distance < minDistance) { // Collision detected - stop at safe position var safeDistance = minDistance + 2; // Add small buffer if (distance > 0) { var normalX = dx / distance; var normalY = dy / distance; self.x = otherBall.x - normalX * safeDistance; self.y = otherBall.y - normalY * safeDistance; // Check for merge first - increased sensitivity for same-numbered balls var mergeDistance = self.value === otherBall.value ? minDistance * 1.4 : minDistance; if (self.value === otherBall.value && self.mergeTimer === 0 && otherBall.mergeTimer === 0 && distance < mergeDistance) { 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; // Add merge animation - scale and pulse effect newBall.scaleX = 0.2; // Start very small newBall.scaleY = 0.2; // Start very small newBall.alpha = 0.7; // Start semi-transparent tween(newBall, { scaleX: 1.3, scaleY: 1.3, alpha: 1 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { // Pulse back to normal size tween(newBall, { scaleX: 1, scaleY: 1 }, { duration: 150, easing: tween.easeInOut }); } }); // Animate merging balls with shrink and fade tween(self, { scaleX: 0, scaleY: 0, alpha: 0 }, { duration: 250, easing: tween.easeIn }); tween(otherBall, { scaleX: 0, scaleY: 0, alpha: 0 }, { duration: 250, easing: tween.easeIn }); 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) { // Make 2048 ball explode and disappear tween(newBall, { scaleX: 2.5, scaleY: 2.5, alpha: 0 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { newBall.hasBeenMerged = true; // Mark for removal } }); LK.showYouWin(); } return; } } // Enhanced collision response for better stacking var relativeVelX = self.velocityX - otherBall.velocityX; var relativeVelY = self.velocityY - otherBall.velocityY; var relativeSpeed = relativeVelX * normalX + relativeVelY * normalY; if (relativeSpeed > 0) { // Calculate mass-based collision response (assume equal mass) var restitution = 0.3; // Lower restitution for more stable stacking var impulse = (1 + restitution) * relativeSpeed * 0.5; self.velocityX -= impulse * normalX; self.velocityY -= impulse * normalY; otherBall.velocityX += impulse * normalX; otherBall.velocityY += impulse * normalY; // Apply different friction based on collision angle var collisionAngle = Math.atan2(normalY, normalX); var frictionFactor = Math.abs(Math.sin(collisionAngle)) * 0.8 + 0.2; self.velocityX *= self.friction * frictionFactor; self.velocityY *= self.friction * frictionFactor; otherBall.velocityX *= otherBall.friction * frictionFactor; otherBall.velocityY *= otherBall.friction * frictionFactor; // Enhanced static detection for stacking if (Math.abs(self.velocityY) < 0.5 && Math.abs(self.velocityX) < 0.5) { // Check if ball is supported (has another ball below it or touching ground) var supportFound = false; var localBottom = gameAreaHeight - 20; // Check ground support if (self.y + self.radius >= localBottom - 5) { supportFound = true; } else { // Check support from other balls for (var j = 0; j < balls.length; j++) { var supportBall = balls[j]; if (supportBall === self || supportBall.hasBeenMerged) continue; var supportDx = supportBall.x - self.x; var supportDy = supportBall.y - self.y; var supportDistance = Math.sqrt(supportDx * supportDx + supportDy * supportDy); // Check if this ball is resting on another ball (other ball is below) if (supportDistance < (self.radius + supportBall.radius) * 1.1 && supportDy > 0) { supportFound = true; break; } } } if (supportFound) { self.isStatic = true; self.velocityX = 0; self.velocityY = 0; } } } } collisionOccurred = true; break; } } } // If no collision occurred, move to intended position if (!collisionOccurred) { self.x = newX; self.y = newY; } // Boundary collisions after position update var localWidth = gameAreaRight - gameAreaLeft; var localBottom = gameAreaHeight - 20; // gameAreaHeight minus bottom wall height // Ground collision 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 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; } } // Enhanced merge detection pass - check for nearby same-numbered balls for (var i = 0; i < balls.length; i++) { var otherBall = balls[i]; if (otherBall === self || otherBall.hasBeenMerged || otherBall.mergeTimer > 0 || self.mergeTimer > 0) continue; var dx = otherBall.x - self.x; var dy = otherBall.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); var minDistance = self.radius + otherBall.radius; // Enhanced merge sensitivity for same-numbered balls if (self.value === otherBall.value) { var mergeTriggerDistance = minDistance * 1.3; // 30% more sensitive if (distance < mergeTriggerDistance) { 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; // Add merge animation - scale and pulse effect newBall.scaleX = 0.2; // Start very small newBall.scaleY = 0.2; // Start very small newBall.alpha = 0.7; // Start semi-transparent tween(newBall, { scaleX: 1.3, scaleY: 1.3, alpha: 1 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { // Pulse back to normal size tween(newBall, { scaleX: 1, scaleY: 1 }, { duration: 150, easing: tween.easeInOut }); } }); // Animate merging balls with shrink and fade tween(self, { scaleX: 0, scaleY: 0, alpha: 0 }, { duration: 250, easing: tween.easeIn }); tween(otherBall, { scaleX: 0, scaleY: 0, alpha: 0 }, { duration: 250, easing: tween.easeIn }); 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) { // Make 2048 ball explode and disappear tween(newBall, { scaleX: 2.5, scaleY: 2.5, alpha: 0 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { newBall.hasBeenMerged = true; // Mark for removal } }); LK.showYouWin(); } return; } } } // Enhanced separation for stable stacking if (distance < minDistance && distance > 0) { var overlap = minDistance - distance; var normalX = dx / distance; var normalY = dy / distance; // Different separation strategies based on ball positions var separationForce = overlap * 0.5; // If one ball is static and the other is moving, adjust separation if (self.isStatic && !otherBall.isStatic) { // Static ball moves less self.x -= normalX * separationForce * 0.2; self.y -= normalY * separationForce * 0.2; otherBall.x += normalX * separationForce * 0.8; otherBall.y += normalY * separationForce * 0.8; } else if (!self.isStatic && otherBall.isStatic) { // Other ball is static self.x -= normalX * separationForce * 0.8; self.y -= normalY * separationForce * 0.8; otherBall.x += normalX * separationForce * 0.2; otherBall.y += normalY * separationForce * 0.2; } else { // Both moving or both static - equal separation self.x -= normalX * separationForce; self.y -= normalY * separationForce; otherBall.x += normalX * separationForce; otherBall.y += normalY * separationForce; } // 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)); // Re-check static state after separation if (self.isStatic && (Math.abs(self.x - (self.x - normalX * separationForce)) > 1 || Math.abs(self.y - (self.y - normalY * separationForce)) > 1)) { self.isStatic = false; // Ball was moved significantly, make it dynamic again } } } // Periodic re-evaluation of static state for better stacking if (LK.ticks % 10 === 0 && !self.hasBeenMerged) { // Check every 10 frames if (!self.isStatic && Math.abs(self.velocityX) < 0.3 && Math.abs(self.velocityY) < 0.3) { // Check if ball should become static var supportFound = false; var localBottom = gameAreaHeight - 20; // Check ground support if (self.y + self.radius >= localBottom - 2) { supportFound = true; } else { // Check support from other static balls for (var k = 0; k < balls.length; k++) { var checkBall = balls[k]; if (checkBall === self || checkBall.hasBeenMerged || !checkBall.isStatic) continue; var checkDx = checkBall.x - self.x; var checkDy = checkBall.y - self.y; var checkDistance = Math.sqrt(checkDx * checkDx + checkDy * checkDy); // Ball is resting on another static ball if (checkDistance < (self.radius + checkBall.radius) * 1.05 && checkDy > 0) { supportFound = true; break; } } } if (supportFound) { self.isStatic = true; self.velocityX = 0; self.velocityY = 0; } } else if (self.isStatic) { // Check if static ball should become dynamic (lost support) var stillSupported = false; var localBottom = gameAreaHeight - 20; // Check ground support if (self.y + self.radius >= localBottom - 2) { stillSupported = true; } else { // Check support from other balls for (var k = 0; k < balls.length; k++) { var supportBall = balls[k]; if (supportBall === self || supportBall.hasBeenMerged) continue; var supportDx = supportBall.x - self.x; var supportDy = supportBall.y - self.y; var supportDistance = Math.sqrt(supportDx * supportDx + supportDy * supportDy); if (supportDistance < (self.radius + supportBall.radius) * 1.05 && supportDy > 0) { stillSupported = true; break; } } } if (!stillSupported) { self.isStatic = false; // Ball lost support, make it fall } } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0xf5f5dc }); /**** * Game Code ****/ var balls = []; var nextBallValue = 2; var gameAreaLeft = 50; var gameAreaRight = 1998; var gameAreaTop = 350; var gameAreaBottom = 2650; var dropCooldown = 0; var gameAreaHeight = gameAreaBottom - gameAreaTop; var dangerZoneImmunity = 300; // 5 seconds at 60fps // 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 game over line (invisible) var gameOverLine = game.addChild(LK.getAsset('gameOverLine', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: gameAreaTop + 100, alpha: 0, visible: false })); // Create score display var scoreText = new Text2('0', { size: 100, fill: 0xFFFFFF }); scoreText.anchor.set(1, 0); LK.gui.topRight.addChild(scoreText); scoreText.x = -50; // Small offset from right edge scoreText.y = 100; // Create next ball preview var nextBallPreview = LK.getAsset('ball' + nextBallValue, { anchorX: 0.5, anchorY: 0.5, x: 1024, y: gameAreaTop - 50, scaleX: 1.2, scaleY: 1.2 }); game.addChild(nextBallPreview); // Create next ball value text var nextBallText = new Text2(nextBallValue.toString(), { size: 42, fill: 0xFFFFFF }); nextBallText.anchor.set(0.5, 0.5); nextBallText.x = 1024; nextBallText.y = gameAreaTop - 50; game.addChild(nextBallText); // Create instruction text var instructionText = new Text2('Tap to drop ball', { size: 55, 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: gameAreaTop - 50, scaleX: 1.2, scaleY: 1.2 }); game.addChild(nextBallPreview); nextBallText = new Text2(nextBallValue.toString(), { size: 42, fill: 0xFFFFFF }); nextBallText.anchor.set(0.5, 0.5); nextBallText.x = 1024; nextBallText.y = gameAreaTop - 50; game.addChild(nextBallText); } function checkGameOver() { // Skip game over check during immunity period if (dangerZoneImmunity > 0) return; 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); } } } // Create initial balls to fill container to the top function createInitialBalls() { var localWidth = gameAreaRight - gameAreaLeft; var ballsPerRow = Math.floor(localWidth / 140); // Space balls 140px apart for larger balls var startX = (localWidth - (ballsPerRow - 1) * 140) / 2; // Center the row for (var row = 0; row < 18; row++) { for (var col = 0; col < ballsPerRow; col++) { var initialBall = new Ball(getRandomNextBallValue()); initialBall.x = startX + col * 140; initialBall.y = gameAreaHeight - 100 - row * 140; // Start from bottom, 140px spacing initialBall.velocityX = 0; initialBall.velocityY = 0; initialBall.isStatic = true; // Start as static for stable stacking balls.push(initialBall); gameArea.addChild(initialBall); } } } // Initialize the starting balls createInitialBalls(); 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(80, Math.min(localWidth - 80, 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--; } // Handle danger zone immunity countdown if (dangerZoneImmunity > 0) { dangerZoneImmunity--; } cleanupMergedBalls(); checkGameOver(); // Update score display scoreText.setText(LK.getScore()); };
===================================================================
--- original.js
+++ change.js
@@ -500,9 +500,9 @@
var nextBallPreview = LK.getAsset('ball' + nextBallValue, {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
- y: gameAreaTop,
+ y: gameAreaTop - 50,
scaleX: 1.2,
scaleY: 1.2
});
game.addChild(nextBallPreview);
@@ -512,9 +512,9 @@
fill: 0xFFFFFF
});
nextBallText.anchor.set(0.5, 0.5);
nextBallText.x = 1024;
-nextBallText.y = gameAreaTop;
+nextBallText.y = gameAreaTop - 50;
game.addChild(nextBallText);
// Create instruction text
var instructionText = new Text2('Tap to drop ball', {
size: 55,
@@ -534,9 +534,9 @@
nextBallPreview = LK.getAsset('ball' + nextBallValue, {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
- y: gameAreaTop,
+ y: gameAreaTop - 50,
scaleX: 1.2,
scaleY: 1.2
});
game.addChild(nextBallPreview);
@@ -545,9 +545,9 @@
fill: 0xFFFFFF
});
nextBallText.anchor.set(0.5, 0.5);
nextBallText.x = 1024;
- nextBallText.y = gameAreaTop;
+ nextBallText.y = gameAreaTop - 50;
game.addChild(nextBallText);
}
function checkGameOver() {
// Skip game over check during immunity period
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