refactor: implement cell-based hit area for inventory items

Update `InventoryItemContainer` to use a custom hit area callback
based on individual cell rectangles instead of a single bounding
box. This allows for more precise interaction with non-rectangular
item shapes.
This commit is contained in:
hypercross 2026-04-20 19:45:01 +08:00
parent b2862e34d9
commit 19dfafeb30
1 changed files with 27 additions and 14 deletions

View File

@ -7,8 +7,6 @@ import {
Transform2D,
transformShape,
type GameItem,
type GameItemMeta,
type InventoryItem,
} from "boardgame-core/samples/slay-the-spire-like";
import { computed, signal, Signal } from "@preact/signals-core";
import { DisposableBag } from "../../../framework/dist";
@ -56,15 +54,12 @@ export class InventoryItemContainer extends Phaser.GameObjects.Container {
const color = computed(() => {
return this.getItemColor(this.item.value?.id ?? "");
});
let hasSize = false;
disposables.addEffect(() => {
graphics.clear();
if (!shape.value) return;
this.renderGraphics(graphics, shape.value, color.value);
const cellRects = this.renderGraphics(graphics, shape.value, color.value);
if (hasSize) return;
hasSize = true;
this.setupInteractive();
this.setupInteractive(cellRects);
});
const transform = computed(() => {
@ -84,8 +79,10 @@ export class InventoryItemContainer extends Phaser.GameObjects.Container {
graphics: Phaser.GameObjects.Graphics,
shape: ParsedShape,
itemColor: number,
): void {
): { x: number; y: number }[] {
const cells = transformShape(shape, IDENTITY_TRANSFORM);
const cellRects: { x: number; y: number }[] = [];
for (const cell of cells) {
const localX = (cell.x - cells[0].x) * GRID_CONFIG.VIEWER_CELL_SIZE;
const localY = (cell.y - cells[0].y) * GRID_CONFIG.VIEWER_CELL_SIZE;
@ -97,12 +94,11 @@ export class InventoryItemContainer extends Phaser.GameObjects.Container {
GRID_CONFIG.VIEWER_CELL_SIZE - 4,
GRID_CONFIG.VIEWER_CELL_SIZE - 4,
);
cellRects.push({ x: localX, y: localY });
}
this.setSize(
GRID_CONFIG.VIEWER_CELL_SIZE * shape.width,
GRID_CONFIG.VIEWER_CELL_SIZE * shape.height,
);
return cellRects;
}
private getItemColor(itemId: string): number {
@ -110,9 +106,26 @@ export class InventoryItemContainer extends Phaser.GameObjects.Container {
return ITEM_COLORS[hash % ITEM_COLORS.length];
}
private setupInteractive(): void {
private setupInteractive(cellRects: { x: number; y: number }[]): void {
this.setScrollFactor(0);
this.setInteractive({ useHandCursor: true });
const cellSize = GRID_CONFIG.VIEWER_CELL_SIZE;
this.setInteractive({
hitArea: cellRects,
hitAreaCallback: (
hitArea: { x: number; y: number }[],
x: number,
y: number,
) => {
return hitArea.some(
(cell) =>
x >= cell.x &&
x < cell.x + cellSize &&
y >= cell.y &&
y < cell.y + cellSize,
);
},
useHandCursor: true,
} as Phaser.Types.Input.InputConfiguration);
let startX = 0;
let startY = 0;