User prompt
remove the comment line shake button script. We need to see this button It need to work shown on the screenuΜ βͺπ‘ 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.2; // gravity
// Clamp vy
if (self.vy > 40) self.vy = 40;
// Move
self.x += self.vx;
self.y += self.vy;
// 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
var nextFruitType = 0;
var nextFruitAsset = LK.getAsset(FRUIT_LIST[nextFruitType].id, {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: containerTopY - 120
});
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 (first 5 types for balance)
function getRandomFruitType() {
return Math.floor(Math.random() * 5);
}
// 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 / 2,
y: containerTopY - 120
});
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) {
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
f1.x += nx * overlap / 2;
f1.y += ny * overlap / 2;
f2.x -= nx * overlap / 2;
f2.y -= ny * overlap / 2;
// Exchange velocity (simple elastic)
var k = 0.7;
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;
}
}
}
// 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();
// 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
var gameOverCounter = 0;
var gameOverCountdownActive = false;
var countdownText = 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) {
// Only increment if already in countdown, else start countdown at 1
if (!gameOverCountdownActive) {
gameOverCounter = 1;
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.visible = true;
} else {
gameOverCounter++;
}
// Update countdown text
if (countdownText) {
if (gameOverCounter === 1) countdownText.setText("3");else if (gameOverCounter === 2) countdownText.setText("2");else if (gameOverCounter === 3) countdownText.setText("1");else countdownText.setText(String(4 - gameOverCounter));
}
if (gameOverCounter >= 3) {
if (countdownText) {
countdownText.setText("0");
}
LK.effects.flashScreen(0xff0000, 1000);
LK.setTimeout(function () {
if (countdownText) {
countdownText.visible = false;
}
LK.showGameOver();
}, 400);
return true;
}
} else {
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
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);
};
// 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) {
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;
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
var merged = tryMergeFruits(f1, f2, tick);
if (merged) break; // f1 is gone, break inner loop
// Else, resolve collision
resolveFruitCollision(f1, f2);
}
}
}
// Check for game over
checkGameOver();
};
// 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 / 2,
y: containerTopY - 120
});
game.addChild(nextFruitAsset);
spawnFruit(); ===================================================================
--- original.js
+++ change.js
@@ -332,10 +332,12 @@
break;
}
}
if (anyAbove) {
- gameOverCounter++;
+ // Only increment if already in countdown, else start countdown at 1
if (!gameOverCountdownActive) {
+ gameOverCounter = 1;
+ gameOverCountdownActive = true;
// Show countdown text
if (!countdownText) {
countdownText = new Text2('3', {
size: 220,
@@ -344,22 +346,15 @@
countdownText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(countdownText);
}
countdownText.visible = true;
- countdownText.setText(String(4 - gameOverCounter));
- gameOverCountdownActive = true;
- } else if (countdownText) {
- countdownText.setText(String(4 - gameOverCounter));
+ } else {
+ gameOverCounter++;
}
- if (gameOverCounter === 1 && countdownText) {
- countdownText.setText("3");
+ // Update countdown text
+ if (countdownText) {
+ if (gameOverCounter === 1) countdownText.setText("3");else if (gameOverCounter === 2) countdownText.setText("2");else if (gameOverCounter === 3) countdownText.setText("1");else countdownText.setText(String(4 - gameOverCounter));
}
- if (gameOverCounter === 2 && countdownText) {
- countdownText.setText("2");
- }
- if (gameOverCounter === 3 && countdownText) {
- countdownText.setText("1");
- }
if (gameOverCounter >= 3) {
if (countdownText) {
countdownText.setText("0");
}