8-Ball Pool: One Drag, Two Modules, and a Full Rules Engine Behind Every Shot
A physics rebuild of 8-ball pool: drag distance folds into 18 power levels, all-stopped settlement keeps the rules trustworthy, and AI aim error keeps it alive.
Análisis
- Standard 8-ball: 16 balls (cue + 1-15), solids 1-7 vs stripes 9-15, the 8 goes last
- The first ball pocketed after the break assigns both players' groups
- Three foul states — cue ball pocketed, no ball contacted, opponent's ball hit first — hand the opponent ball-in-hand
- Clear your group then legally sink the 8 to win; sinking it early or on a foul loses on the spot
- Press near the cue ball → drag to set direction and power (drag distance maps to 0-18) → release to shoot
- Frame-by-frame physics: ball-ball collisions, cushion bounces, pocket detection, settled only after everything stops
- Settlement judges fouls, group assignment, and pocket ownership — keep shooting or hand over; the AI answers in 1-1.5s
- A legal 8-ball finish pops the result screen; PLAY AGAIN rematches instantly
- 8-ball/pool-model carries physics and rules in one module: ball states, collision/pocket detection, foul and winner logic, plus the AI shot
- __OV.Games.EightBallPool.Game is the scene-private renderer: PIXI draws table, balls and cue, translating pointer events into shot.aim/drag/release
- A real race had to be fixed: LogicGraph runs async, so resetting the aiming flag must defer to a macrotask or a real drag can never fire a shot
- common-ui wires pause/help buttons; the boot → menu → gameplay ⇄ paused → result state machine is declared in the blueprint
Código clave
Demo
Hit Play to break. Press near the cue ball, drag to aim, release to shoot — the farther you drag, the harder the hit. Clear your group, then sink the 8; the AI takes its turns automatically.
Artículo
8-Ball Pool: One Drag, Two Modules, and a Full Rules Engine Behind Every Shot
One-Sentence Essence
This rebuild of 8-Ball Pool is a complete physics-archetype duel: you and an AI alternate shots on a standard table, and every shot is compressed into one gesture — press to aim, drag to set power, release to fire — while collisions, pockets, fouls, and the win itself are handed to a physics simulation and a deterministic rule settlement. It splits the two hardest parts of a pool game, feel and rule trustworthiness, into two separate modules.
Behavior Loop
- Opening: hit Play on the menu; 15 object balls rack in a triangle, the cue ball sits behind the head string at 25% of the table width, and you take the break.
- Aiming: press within 4 ball-radii of the cue ball to start aiming; the drag direction is the shot direction, and drag distance maps linearly to power (150 logical pixels reaches the maximum level 18).
- Shooting: releasing with power above 0.5 fires the shot at an initial speed of power × 180, starting the physics simulation.
- Settlement: ball-ball collisions, cushion bounces, and pocket detection run per frame; once every ball stops, a single pass judges fouls, group assignment, pocket ownership, and the turn switch.
- Rotation: before groups are assigned, the first pocketed ball decides them; a legal pocket keeps the turn, otherwise the AI takes over, answering after 1-1.5 seconds of "thinking" with deliberate aim error.
- Endgame: clear your group and legally sink the 8 to win; sinking it early or on a foul loses immediately. The result popup shows 🏆 or 😞, and PLAY AGAIN rematches on the spot.
The demo at the end of this article runs this exact loop.
Gameplay Primitives
AimStart(x, y)– press near the cue ball to begin aiming (shot.aim)AimDrag(dx, dy)– drag to update angle and power (shot.drag)Shoot(angle, power)– release to fire (shot.release → takeShot)SimulatePhysics()– per-frame collision / bounce / pocket until all balls stopSettleTurn()– foul check, group assignment, turn switch (on balls.stopped)PlaceCueBall(x, y)– manually place the cue ball under ball-in-hand (cueball.place)AiShoot()– the AI computes angle and power and shoots backJudgeWinner()– legality ruling on the 8 ball (game.over)
Parameter Cheat Sheet
| Parameter | Description | Default | Unit | |-----------|-------------|---------|------| | Table size | Logical table width × height | 600 × 300 | logical px | | Ball radius | Uniform for all balls | 10 | logical px | | Pockets | 4 corners + 2 side | 6 | – | | Ball count | Cue ball + 1-15 | 16 | balls | | Power range | power | 0-18 | levels | | Full-power drag | maxDrag | 150 | logical px | | Fire threshold | Minimum effective power | 0.5 | levels | | Speed mapping | speed = power × 180 | up to 3240 | logical px/s | | Aim grab radius | Max press distance from cue ball | 40 (4× radius) | logical px | | Head string | Cue ball starting position | 25% of width | – | | AI think time | Pause before answering | 1-1.5 | seconds |
Design Trade-offs
- Drag toward the target, not away: a real cue pulls back and strikes forward; this build simplifies it to "drag where you want the ball to go" — one less mental inversion on touch screens, at the cost of the pull-back feel.
- Power as raw drag distance: no charging meter — a short drag nudges, a long drag smashes, and there is no waiting; anything under 0.5 reads as a cancel, guarding against mis-taps.
- All-stopped settlement over per-ball instant scoring: fouls, ownership, and the win are judged only after every ball stops, so the rules are written once in a fixed order; the cost is that feedback on your shot waits for the table to settle, favoring a "one shot, one tableau" rhythm over rapid-fire play.
- AI with deliberate error: instead of solving shots exactly, the AI aims with added noise to simulate skill, keeping the match competitive; there is a single strength level and no difficulty selector yet.
- Secondary menus: the main HUD keeps only pause and help buttons, tucking the volume slider inside the pause menu so the table area stays clean.
Implementation Notes
- State machine: boot → menu → gameplay ⇄ paused → result, with transitions game.start / game.pause / game.resume / game.over / game.restart declared in the blueprint.
- Event flow:
shot.aim→shot.drag→shot.release(emitted only above power 0.5) → pool-model.takeShot; when all balls stop, turn settlement runs, and withcurrentPlayer = 2the AI fires automatically, closing the rotation. - Data model:
balls(position / velocity / pocketed for all 16), currentPlayer, both groups and pocketed lists, foul / ballInHand / placingCueBall, aiming / power / aimAngle — about twenty keys carry the whole match. - Module split: 8-ball/pool-model holds the physics, the 8-ball rules, and the AI shot in one place;
__OV.Games.EightBallPool.Gameis the scene-private renderer — PIXI draws the table, balls, and cue, and translates pointer events into the aiming gesture; common-ui wires the pause and help buttons. - A real race, really fixed: LogicGraph event handling is asynchronous, so when the renderer reset
aimingsynchronously right after emittingshot.release, the rule'sif (aiming == true)check lost the race and real drags could never fire; deferring the reset to a macrotask fixed it. Event-injection e2e tests cannot catch this class of bug — only real pointer input exposes it. - Runtime: PIXI rendering with the v2-kernel LogicGraph driver, exported as a fully static, offline-playable demo; the iframe below is that exact export.
The value of this build is the clean decoupling of feel from rules: the gesture produces nothing but two parameters — angle and power — while the physics and rules modules read only data keys. Every layer can be swapped or tuned independently, and the causal chain of a single shot stays verifiable end to end.
También te puede gustar

Deconstructing the penalty mechanics and fake difficulty design in pull-the-pin puzzles. The article analyzes how sequence errors trigger immediate restarts, leveraging low forgiveness to create a 'seemingly hard, actually fake' challenge experience. It reveals the underlying logic of building levels via rule-based constraints rather than complex operations, offering high-value design references for puzzle games.
A bubble-shooter teardown: aim angle folds into a column pick, 8-way pops raise density, and a ceiling drop every 10 shots makes pressure countable.
A 5-round penalty duel compressed into an angle-by-power table: three judgement bands — miss zone, risk zone, sweet spot — rule goal, save or miss.