miaoda-game-grid-phaser
v0.3.0
Published
Phaser 4 grid adapter with rectangular layout/input/movement and shared Y-down pointy/flat hex positioning and picking.
Downloads
517
Maintainers
Readme
miaoda-game-grid-phaser
Use this Phaser 4 adapter to place Game Objects on a centered tile grid, convert world-space pointer events to tiles, and move characters along paths produced by miaoda-game-grid-core.
Use GridView for layout and one-off movement. Add GridInput for pointer handling, and use GridMover when a character needs committed tile state, route validation, cancellation, or overlapping movement requests.
Install
pnpm add miaoda-game-grid-core miaoda-game-grid-phaser phaserThis package supports Phaser >=4 <5.
Place objects and receive pointer input
import { GridInput, GridView } from 'miaoda-game-grid-phaser';
const layout = { cols: 8, rows: 8, cell: 64 };
const origin = { x: 400, y: 300 };
const view = new GridView(layout, scene.tweens, origin);
view.place(sprite, 2, 3);
await view.moveTo(sprite, 3, 3, 150);
const input = new GridInput(scene.input, layout, origin).onTile((tile) => {
selectTile(tile);
});
scene.events.once(Phaser.Scenes.Events.SHUTDOWN, () => input.destroy());GridView.moveTo measures duration in milliseconds. It places the object immediately when the duration is non-positive or non-finite, or when no tween manager was passed.
The grid is centered on origin. Tile (0, 0) is the top-left; X increases right and Y increases down. GridInput uses pointer.worldX and pointer.worldY, so the origin must also be expressed in world coordinates. Set input.locked = true while board input should be ignored.
GridInput.destroy() is idempotent and terminal. It removes the Phaser listener, releases the tile callback, and ignores captured pointer callbacks or later onTile() calls.
Hex layout
import {
createPhaserHexLayout,
localToHexTile,
setHexGameObjectPosition,
} from 'miaoda-game-grid-phaser';
const hexLayout = createPhaserHexLayout({
orientation: 'pointy',
size: { x: 32, y: 32 },
origin: { x: 400, y: 120 },
});
setHexGameObjectPosition(unit, hexLayout, { x: 3, y: 4 }, 'odd-r');
const tile = localToHexTile(hexLayout, pointerLocal, 'odd-r');The shared HexLayout in grid-core owns all projection and cube-rounding math; this adapter fixes Phaser's Y-down convention and writes positions. Convert camera/world pointer coordinates into the same local plane before calling localToHexTile. Physics and navigation queries remain explicit Phaser host operations.
Move a character along a path
GridMover gives one object a single authoritative movement channel. This prevents a route tween, hit recoil, and idle motion from all writing to the same X/Y position.
import { findPath } from 'miaoda-game-grid-core';
import { GridMover } from 'miaoda-game-grid-phaser';
const motionRoot = scene.add.container(0, 0);
const visualRoot = scene.add.container(0, 0);
motionRoot.add(visualRoot);
const mover = new GridMover(view, motionRoot, { x: 2, y: 3 });
view.place(motionRoot, 2, 3);
const path = findPath(
{ width: layout.cols, height: layout.rows },
mover.currentTile,
targetTile,
{ isBlocked },
);
if (path) {
const result = await mover.followPath(path, {
durationMs: 150,
conflict: 'replace',
});
if (result.status === 'completed') {
commitArrival(result.tile);
}
}
// Visual-only motion belongs on a child of the movement root.
scene.tweens.add({ targets: visualRoot, y: -5, yoyo: true, repeat: -1 });The path must start at mover.currentTile, remain inside the layout, and move one orthogonal tile per step. currentTile changes only after a step reaches its destination; targetTile exposes the current in-flight destination.
Handle competing movement requests
Choose a conflict policy for every request:
| Policy | Behavior |
| --- | --- |
| replace | Cancel the active route and start the new request; this is the default |
| queue | Run the new route after active and already queued routes |
| reject | Return { status: 'rejected', reason: 'busy' } immediately |
cancel() settles the active and queued requests as cancelled. The last fully reached tile remains committed. Call snapToCurrent() when a hard interruption should also return the visual to that tile.
Call destroy() during Scene shutdown or register the mover with a lifecycle owner. Destruction cancels active and queued routes and is terminal: later movement requests settle as cancelled, and snapToCurrent() no longer writes the released Game Object. Tween creation failures reject only the affected request and do not strand the queue.
If Phaser already has an active tween on the movement root, a route is rejected with reason: 'external-tween'. Keep bobbing, recoil, squash, and other effects on a child object so GridMover remains the only writer of the root position.
Public API guide
| API | Use it for |
| --- | --- |
| GridView.tilePosition | Convert a tile to its world-space center |
| GridView.place | Place or teleport an object immediately |
| GridView.moveTo | Run a simple one-off tween |
| GridView.startMove | Start a cancellable one-off tween |
| GridInput.pointerToTile | Convert a pointer without waiting for an event |
| GridInput.onTile | Receive in-range pointerup tiles |
| createPhaserHexLayout | Create a Y-down shared pointy/flat hex transform |
| localToHexTile / setHexGameObjectPosition | Pick a local hex or position an object without owning movement |
| GridMover.moveTo | Move exactly one orthogonal tile |
| GridMover.followPath | Validate and follow a complete core path |
