feat: implement Suika Game (Watermelon Game)

Browser-based physics puzzle game where players drop fruits that merge
into larger fruits on collision, using Matter.js for 2D physics and
Canvas2D for rendering. Includes 11-fruit progression chain, scoring,
game-over detection, mouse/touch input, and Vitest test suite.
This commit is contained in:
2026-04-12 10:26:38 +07:00
parent 00d6bb117b
commit fbec9c89fd
19 changed files with 2385 additions and 0 deletions

44
src/input.js Normal file
View File

@@ -0,0 +1,44 @@
import { FRUITS } from './fruits.js';
import { CONTAINER_X, CONTAINER_WIDTH } from './constants.js';
export function clampX(x, fruitTier) {
const radius = FRUITS[fruitTier].radius;
const minX = CONTAINER_X + radius;
const maxX = CONTAINER_X + CONTAINER_WIDTH - radius;
return Math.max(minX, Math.min(maxX, x));
}
export function setupInput(canvas, game) {
function getCanvasX(clientX) {
const rect = canvas.getBoundingClientRect();
const scaleX = canvas.width / rect.width;
return (clientX - rect.left) * scaleX;
}
canvas.addEventListener('mousemove', (e) => {
game.setCursorX(getCanvasX(e.clientX));
});
canvas.addEventListener('click', (e) => {
if (game.state.isGameOver) {
game.restart();
} else {
game.drop();
}
});
canvas.addEventListener('touchmove', (e) => {
e.preventDefault();
const touch = e.touches[0];
game.setCursorX(getCanvasX(touch.clientX));
}, { passive: false });
canvas.addEventListener('touchend', (e) => {
e.preventDefault();
if (game.state.isGameOver) {
game.restart();
} else {
game.drop();
}
}, { passive: false });
}