User prompt
remove the comment line shake button script. We need to see this button It need to work shown on the screenü ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
add a button to lower screen. It shake the bottle left and right to better feeling ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
when colliders inside eacouther. Make a better push each other. Better powerful push. Reduce gravity to prevent shaking
User prompt
fix hold and drop system
User prompt
fix gravity move shaking. Fix left mouse click hold system. When hold its need to drop and drop and drop drop drop drop ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
improve collider fruits become inside and inside eachothers. + When I hold left mouse click it drop next and next fuirt like machine gun. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
1) fix colliders. when merge happend it need to push other fruits like explode 2) add particles when merge 3) when I hold left click drop and drop even I release + I can click to drop 4) fix gravity. Its shaking when stacking 2 or 3 fruit up and up ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
1) dropped furit dont reackt the counter. After 3 second countdown start for that new fruit. 2) fix hitbox colliders sometimes fruits going inside eachothers
User prompt
The bigger dropable fruit is fruit_grape. Next fruit is visible on right top corner.
User prompt
the countdown count every second. 3 (1 sec later) 2 .....
User prompt
its insta gameover now. Check the code and fix that
User prompt
fix the game over state. Add 3 2 1 countdown
User prompt
current fruit follow the mose on x axis. when I click the left mouse button it must drop on the glass
User prompt
okay do like that. Add a line to furit. This line indicate the fruits drop spot
Code edit (1 edits merged)
Please save this source code
User prompt
Suika Merge Fruits
Initial prompt
make me a suika game clon
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Fruit class var Fruit = Container.expand(function () { var self = Container.call(this); // Properties self.fruitType = 0; // index in FRUIT_LIST self.radius = 60; // default, will be set below self.vx = 0; self.vy = 0; self.isDropping = false; // true if falling, false if held at top self.isMerging = false; // true if currently merging (to avoid double merges) self.lastMergedTick = -1; // to prevent double merges in one tick // Attach fruit asset self.setType = function (typeIdx) { self.fruitType = typeIdx; if (self.fruitAsset) { self.removeChild(self.fruitAsset); } var fruitId = FRUIT_LIST[typeIdx].id; self.fruitAsset = self.attachAsset(fruitId, { anchorX: 0.5, anchorY: 0.5 }); self.radius = self.fruitAsset.width / 2; }; // Set initial type self.setType(0); // Set position self.setPosition = function (x, y) { self.x = x; self.y = y; }; // Physics update self.update = function () { if (!self.isDropping) return; // Gravity self.vy += 1.1; // slightly less gravity for stability // Clamp vy if (self.vy > 32) self.vy = 32; // Move self.x += self.vx; self.y += self.vy; // Dampen tiny velocities to avoid jitter if (Math.abs(self.vx) < 0.05) self.vx = 0; if (Math.abs(self.vy) < 0.05) self.vy = 0; // Wall collision if (self.x - self.radius < containerLeftX + wallThickness) { self.x = containerLeftX + wallThickness + self.radius; self.vx *= -0.5; } if (self.x + self.radius > containerRightX - wallThickness) { self.x = containerRightX - wallThickness - self.radius; self.vx *= -0.5; } // Floor collision if (self.y + self.radius > containerBottomY) { self.y = containerBottomY - self.radius; if (Math.abs(self.vy) > 2) { self.vy *= -0.45; } else { self.vy = 0; } // Friction self.vx *= 0.98; if (Math.abs(self.vx) < 0.1) self.vx = 0; } }; // Merge animation self.playMergeAnim = function () { tween(self, { scaleX: 1.25, scaleY: 1.25 }, { duration: 120, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1 }, { duration: 120, easing: tween.easeIn }); } }); }; // Destroy self.destroyFruit = function () { if (self.parent) self.parent.removeChild(self); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x222233 }); /**** * Game Code ****/ /**** * Fruit Progression Data ****/ // Container (the bin) // We'll use colored ellipses for each fruit, with increasing size and distinct colors. // Fruit progression: cherry → strawberry → grape → dekopon → orange → apple → pear → peach → pineapple → melon → watermelon // Container dimensions var FRUIT_LIST = [{ id: 'fruit_cherry', name: 'Cherry', score: 1 }, { id: 'fruit_strawberry', name: 'Strawberry', score: 2 }, { id: 'fruit_grape', name: 'Grape', score: 4 }, { id: 'fruit_dekopon', name: 'Dekopon', score: 8 }, { id: 'fruit_orange', name: 'Orange', score: 16 }, { id: 'fruit_apple', name: 'Apple', score: 32 }, { id: 'fruit_pear', name: 'Pear', score: 64 }, { id: 'fruit_peach', name: 'Peach', score: 128 }, { id: 'fruit_pineapple', name: 'Pineapple', score: 256 }, { id: 'fruit_melon', name: 'Melon', score: 512 }, { id: 'fruit_watermelon', name: 'Watermelon', score: 1024 }]; var containerWidth = 1200; var containerHeight = 1600; var wallThickness = 40; var containerLeftX = (2048 - containerWidth) / 2; var containerRightX = containerLeftX + containerWidth; var containerTopY = 400; var containerBottomY = containerTopY + containerHeight; // Draw container var containerBody = LK.getAsset('container_body', { anchorX: 0, anchorY: 0, x: containerLeftX, y: containerTopY }); game.addChild(containerBody); var leftWall = LK.getAsset('container_wall', { anchorX: 0, anchorY: 0, x: containerLeftX, y: containerTopY }); game.addChild(leftWall); var rightWall = LK.getAsset('container_wall', { anchorX: 0, anchorY: 0, x: containerRightX - wallThickness, y: containerTopY }); game.addChild(rightWall); // Fruits array var fruits = []; // Score var score = 0; var scoreTxt = new Text2('0', { size: 120, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Next fruit preview // The biggest dropable fruit is fruit_grape (index 2) var MAX_DROP_FRUIT_INDEX = 2; var nextFruitType = 0; var nextFruitAsset = LK.getAsset(FRUIT_LIST[nextFruitType].id, { anchorX: 0.5, anchorY: 0.5, // Place in right top corner, but not in the top left 100x100 area x: 2048 - 200, y: 180 }); game.addChild(nextFruitAsset); // Drop spot indicator line var dropLine = LK.getAsset('container_wall', { anchorX: 0.5, anchorY: 0, width: 8, height: containerHeight + 60, color: 0xffffff, x: 2048 / 2, y: containerTopY - 30 }); dropLine.alpha = 0.5; game.addChild(dropLine); // Current dropping fruit var currentFruit = null; // Helper: get random fruit type (up to fruit_grape) function getRandomFruitType() { // Only allow cherry, strawberry, grape (index 0,1,2) return Math.floor(Math.random() * (MAX_DROP_FRUIT_INDEX + 1)); } // Helper: spawn new fruit at top function spawnFruit() { var typeIdx = nextFruitType; currentFruit = new Fruit(); currentFruit.setType(typeIdx); currentFruit.setPosition(2048 / 2, containerTopY - 80); currentFruit.isDropping = false; currentFruit.vx = 0; currentFruit.vy = 0; currentFruit.scaleX = 1; currentFruit.scaleY = 1; game.addChild(currentFruit); // Move dropLine to center for new fruit if (dropLine) dropLine.x = 2048 / 2; // Next fruit nextFruitType = getRandomFruitType(); if (nextFruitAsset && nextFruitAsset.parent) nextFruitAsset.parent.removeChild(nextFruitAsset); nextFruitAsset = LK.getAsset(FRUIT_LIST[nextFruitType].id, { anchorX: 0.5, anchorY: 0.5, x: 2048 - 200, y: 180 }); game.addChild(nextFruitAsset); } // Helper: check collision between two fruits function fruitsCollide(f1, f2) { var dx = f1.x - f2.x; var dy = f1.y - f2.y; var dist = Math.sqrt(dx * dx + dy * dy); return dist < (f1.radius + f2.radius) * 0.98; } // Helper: resolve collision between two fruits (physics bounce) function resolveFruitCollision(f1, f2, pushStrength) { var dx = f1.x - f2.x; var dy = f1.y - f2.y; var dist = Math.sqrt(dx * dx + dy * dy); var minDist = f1.radius + f2.radius; if (dist < minDist && dist > 0.1) { var overlap = minDist - dist; var nx = dx / dist; var ny = dy / dist; // Push fruits apart (stronger separation for stability) var push = overlap / (pushStrength || 1.1); f1.x += nx * push / 2; f1.y += ny * push / 2; f2.x -= nx * push / 2; f2.y -= ny * push / 2; // Exchange velocity (dampen for stacking) var k = 0.45; var tx = nx; var ty = ny; var dp = (f1.vx - f2.vx) * tx + (f1.vy - f2.vy) * ty; if (dp < 0) { f1.vx -= dp * tx * k; f1.vy -= dp * ty * k; f2.vx += dp * tx * k; f2.vy += dp * ty * k; // Dampen vertical velocity for stacking f1.vy *= 0.82; f2.vy *= 0.82; } } } // Helper: merge two fruits (returns new fruit or null) function tryMergeFruits(f1, f2, tick) { if (f1.isMerging || f2.isMerging) return null; if (f1.fruitType !== f2.fruitType) return null; if (f1.fruitType >= FRUIT_LIST.length - 1) return null; // Prevent double merge in one tick if (f1.lastMergedTick === tick || f2.lastMergedTick === tick) return null; // Mark as merging f1.isMerging = true; f2.isMerging = true; f1.lastMergedTick = tick; f2.lastMergedTick = tick; // New fruit at average position var newFruit = new Fruit(); newFruit.setType(f1.fruitType + 1); newFruit.setPosition((f1.x + f2.x) / 2, (f1.y + f2.y) / 2); newFruit.vx = (f1.vx + f2.vx) / 2; newFruit.vy = (f1.vy + f2.vy) / 2 - 8; // bounce up a bit newFruit.isDropping = true; newFruit.scaleX = 0.7; newFruit.scaleY = 0.7; game.addChild(newFruit); // Merge animation newFruit.playMergeAnim(); // Particle effect for (var p = 0; p < 18; p++) { var particle = LK.getAsset(FRUIT_LIST[newFruit.fruitType].id, { anchorX: 0.5, anchorY: 0.5, x: newFruit.x, y: newFruit.y, scaleX: 0.18 + Math.random() * 0.12, scaleY: 0.18 + Math.random() * 0.12, alpha: 0.85 }); game.addChild(particle); var angle = Math.PI * 2 * p / 18 + Math.random() * 0.3; var dist = newFruit.radius * (1.1 + Math.random() * 0.7); var tx = newFruit.x + Math.cos(angle) * dist; var ty = newFruit.y + Math.sin(angle) * dist; tween(particle, { x: tx, y: ty, alpha: 0 }, { duration: 420 + Math.random() * 180, easing: tween.cubicOut, onFinish: function (pt) { return function () { if (pt.parent) pt.parent.removeChild(pt); }; }(particle) }); } // Explode nearby fruits outward for (var i = 0; i < fruits.length; i++) { var other = fruits[i]; if (other !== f1 && other !== f2 && other.isDropping) { var dx = other.x - newFruit.x; var dy = other.y - newFruit.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < newFruit.radius * 2.5 && dist > 0.1) { // Push away with random force var force = 18 + Math.random() * 8; var nx = dx / dist; var ny = dy / dist; other.vx += nx * force; other.vy += ny * force; } } } // Remove old fruits f1.destroyFruit(); f2.destroyFruit(); // Remove from fruits array for (var i = fruits.length - 1; i >= 0; i--) { if (fruits[i] === f1 || fruits[i] === f2) fruits.splice(i, 1); } fruits.push(newFruit); // Add score score += FRUIT_LIST[newFruit.fruitType].score; scoreTxt.setText(score); return newFruit; } // Helper: check for game over (if any fruit is above containerTopY) with 3-2-1 countdown (1 second per count) var gameOverCounter = 0; var gameOverCountdownActive = false; var countdownText = null; var gameOverInterval = null; function clearGameOverInterval() { if (gameOverInterval !== null) { LK.clearInterval(gameOverInterval); gameOverInterval = null; } } function checkGameOver() { var anyAbove = false; for (var i = 0; i < fruits.length; i++) { if (fruits[i].y - fruits[i].radius < containerTopY + 10) { anyAbove = true; break; } } if (anyAbove) { if (!gameOverCountdownActive) { gameOverCounter = 3; gameOverCountdownActive = true; // Show countdown text if (!countdownText) { countdownText = new Text2('3', { size: 220, fill: 0xFF3333 }); countdownText.anchor.set(0.5, 0.5); LK.gui.center.addChild(countdownText); } countdownText.setText("3"); countdownText.visible = true; // Start 1-second interval countdown clearGameOverInterval(); gameOverInterval = LK.setInterval(function () { // Check if still any fruit above var stillAbove = false; for (var i = 0; i < fruits.length; i++) { if (fruits[i].y - fruits[i].radius < containerTopY + 10) { stillAbove = true; break; } } if (!stillAbove) { // Reset if no fruit above clearGameOverInterval(); gameOverCounter = 0; gameOverCountdownActive = false; if (countdownText) countdownText.visible = false; return; } gameOverCounter--; if (countdownText) countdownText.setText(gameOverCounter > 0 ? String(gameOverCounter) : "0"); if (gameOverCounter <= 0) { clearGameOverInterval(); if (countdownText) countdownText.setText("0"); LK.effects.flashScreen(0xff0000, 1000); LK.setTimeout(function () { if (countdownText) countdownText.visible = false; LK.showGameOver(); }, 400); } }, 1000); } // else: already counting down, do nothing (interval handles decrement) } else { // No fruit above, reset everything clearGameOverInterval(); gameOverCounter = 0; gameOverCountdownActive = false; if (countdownText) countdownText.visible = false; } return false; } // Drag and drop for current fruit var isDragging = false; var dragOffsetX = 0; // Only allow drop inside container function clampFruitX(x, fruit) { var minX = containerLeftX + wallThickness + fruit.radius; var maxX = containerRightX - wallThickness - fruit.radius; if (x < minX) x = minX; if (x > maxX) x = maxX; return x; } // Touch/mouse down: start drag if on current fruit // Machine gun drop: hold to drop fruits rapidly var dropInterval = null; game.down = function (x, y, obj) { if (!currentFruit || currentFruit.isDropping) return; function dropAndSpawn() { if (!currentFruit || currentFruit.isDropping) return; currentFruit.isDropping = true; currentFruit.vx = 0; currentFruit.vy = 0; fruits.push(currentFruit); // Hide dropLine while fruit is falling if (dropLine) dropLine.visible = false; currentFruit = null; // Spawn next fruit after short delay LK.setTimeout(function () { if (dropLine) dropLine.visible = true; spawnFruit(); }, 120); } dropAndSpawn(); // Start interval for machine gun drop if (dropInterval) LK.clearInterval(dropInterval); dropInterval = LK.setInterval(function () { if (!currentFruit || currentFruit.isDropping) { LK.clearInterval(dropInterval); dropInterval = null; return; } dropAndSpawn(); }, 140); }; // Touch/mouse move: drag fruit horizontally game.move = function (x, y, obj) { if (!currentFruit || currentFruit.isDropping) return; // If dragging, keep old drag logic if (isDragging) { var nx = clampFruitX(x + dragOffsetX, currentFruit); currentFruit.x = nx; if (dropLine) dropLine.x = nx; } else { // Not dragging: always follow mouse/touch X axis var nx = clampFruitX(x, currentFruit); currentFruit.x = nx; if (dropLine) dropLine.x = nx; } }; // Touch/mouse up: drop fruit game.up = function (x, y, obj) { // Stop machine gun drop on release if (dropInterval) { LK.clearInterval(dropInterval); dropInterval = null; } if (!currentFruit || currentFruit.isDropping) return; if (isDragging) { isDragging = false; currentFruit.isDropping = true; currentFruit.vx = 0; currentFruit.vy = 0; fruits.push(currentFruit); // Hide dropLine while fruit is falling if (dropLine) dropLine.visible = false; currentFruit = null; // Spawn next fruit after short delay LK.setTimeout(function () { if (dropLine) dropLine.visible = true; if (dropLine) dropLine.visible = true; spawnFruit(); }, 300); } }; // Main update loop game.update = function () { // Update all fruits for (var i = 0; i < fruits.length; i++) { fruits[i].update(); } // Fruit-to-fruit collision and merging var tick = LK.ticks; // Run collision resolution more times per frame for better separation for (var repeat = 0; repeat < 8; repeat++) { for (var i = 0; i < fruits.length; i++) { var f1 = fruits[i]; if (!f1.isDropping) continue; for (var j = i + 1; j < fruits.length; j++) { var f2 = fruits[j]; if (!f2.isDropping) continue; if (fruitsCollide(f1, f2)) { // Try merge only on first pass if (repeat === 0) { var merged = tryMergeFruits(f1, f2, tick); if (merged) break; // f1 is gone, break inner loop } // Always resolve collision // Use a stronger push for the first few passes resolveFruitCollision(f1, f2, repeat < 3 ? 1.5 : 1.1); } } } } // Check for game over // Only check for game over if there is no currentFruit (i.e., all fruits are dropped) // And only after a 3 second grace period after a fruit is dropped if (!currentFruit) { if (typeof lastFruitDropTime === "undefined") lastFruitDropTime = 0; if (typeof gameOverGraceActive === "undefined") gameOverGraceActive = false; if (!gameOverGraceActive) { // Start grace period after fruit is dropped lastFruitDropTime = Date.now(); gameOverGraceActive = true; } // Wait 3 seconds after drop before checking game over if (Date.now() - lastFruitDropTime > 3000) { checkGameOver(); } } else { // Reset grace period if a new fruit is being held gameOverGraceActive = false; } }; // Start game score = 0; scoreTxt.setText(score); fruits = []; gameOverCounter = 0; gameOverCountdownActive = false; if (typeof countdownText !== "undefined" && countdownText && countdownText.parent) { countdownText.visible = false; } if (nextFruitAsset && nextFruitAsset.parent) nextFruitAsset.parent.removeChild(nextFruitAsset); nextFruitType = getRandomFruitType(); nextFruitAsset = LK.getAsset(FRUIT_LIST[nextFruitType].id, { anchorX: 0.5, anchorY: 0.5, x: 2048 - 200, y: 180 }); game.addChild(nextFruitAsset); spawnFruit();
===================================================================
--- original.js
+++ change.js
@@ -262,9 +262,9 @@
var dist = Math.sqrt(dx * dx + dy * dy);
return dist < (f1.radius + f2.radius) * 0.98;
}
// Helper: resolve collision between two fruits (physics bounce)
-function resolveFruitCollision(f1, f2) {
+function resolveFruitCollision(f1, f2, pushStrength) {
var dx = f1.x - f2.x;
var dy = f1.y - f2.y;
var dist = Math.sqrt(dx * dx + dy * dy);
var minDist = f1.radius + f2.radius;
@@ -272,9 +272,9 @@
var overlap = minDist - dist;
var nx = dx / dist;
var ny = dy / dist;
// Push fruits apart (stronger separation for stability)
- var push = overlap / 1.1;
+ var push = overlap / (pushStrength || 1.1);
f1.x += nx * push / 2;
f1.y += ny * push / 2;
f2.x -= nx * push / 2;
f2.y -= ny * push / 2;
@@ -465,23 +465,38 @@
if (x > maxX) x = maxX;
return x;
}
// Touch/mouse down: start drag if on current fruit
+// Machine gun drop: hold to drop fruits rapidly
+var dropInterval = null;
game.down = function (x, y, obj) {
if (!currentFruit || currentFruit.isDropping) return;
- // Drop the fruit immediately on any down event
- currentFruit.isDropping = true;
- currentFruit.vx = 0;
- currentFruit.vy = 0;
- fruits.push(currentFruit);
- // Hide dropLine while fruit is falling
- if (dropLine) dropLine.visible = false;
- currentFruit = null;
- // Spawn next fruit after short delay
- LK.setTimeout(function () {
- if (dropLine) dropLine.visible = true;
- spawnFruit();
- }, 300);
+ function dropAndSpawn() {
+ if (!currentFruit || currentFruit.isDropping) return;
+ currentFruit.isDropping = true;
+ currentFruit.vx = 0;
+ currentFruit.vy = 0;
+ fruits.push(currentFruit);
+ // Hide dropLine while fruit is falling
+ if (dropLine) dropLine.visible = false;
+ currentFruit = null;
+ // Spawn next fruit after short delay
+ LK.setTimeout(function () {
+ if (dropLine) dropLine.visible = true;
+ spawnFruit();
+ }, 120);
+ }
+ dropAndSpawn();
+ // Start interval for machine gun drop
+ if (dropInterval) LK.clearInterval(dropInterval);
+ dropInterval = LK.setInterval(function () {
+ if (!currentFruit || currentFruit.isDropping) {
+ LK.clearInterval(dropInterval);
+ dropInterval = null;
+ return;
+ }
+ dropAndSpawn();
+ }, 140);
};
// Touch/mouse move: drag fruit horizontally
game.move = function (x, y, obj) {
if (!currentFruit || currentFruit.isDropping) return;
@@ -498,8 +513,13 @@
}
};
// Touch/mouse up: drop fruit
game.up = function (x, y, obj) {
+ // Stop machine gun drop on release
+ if (dropInterval) {
+ LK.clearInterval(dropInterval);
+ dropInterval = null;
+ }
if (!currentFruit || currentFruit.isDropping) return;
if (isDragging) {
isDragging = false;
currentFruit.isDropping = true;
@@ -524,10 +544,10 @@
fruits[i].update();
}
// Fruit-to-fruit collision and merging
var tick = LK.ticks;
- // Run collision resolution multiple times per frame for better separation
- for (var repeat = 0; repeat < 3; repeat++) {
+ // Run collision resolution more times per frame for better separation
+ for (var repeat = 0; repeat < 8; repeat++) {
for (var i = 0; i < fruits.length; i++) {
var f1 = fruits[i];
if (!f1.isDropping) continue;
for (var j = i + 1; j < fruits.length; j++) {
@@ -539,9 +559,10 @@
var merged = tryMergeFruits(f1, f2, tick);
if (merged) break; // f1 is gone, break inner loop
}
// Always resolve collision
- resolveFruitCollision(f1, f2);
+ // Use a stronger push for the first few passes
+ resolveFruitCollision(f1, f2, repeat < 3 ? 1.5 : 1.1);
}
}
}
}