Tilemaps can block movement: a tile gains a Solid switch, the solid cells of all layers are merged into rectangles and the placed tilemap takes part in the collision system as one body, reported as other in collision events (#132)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
10b49fac46
commit
4c411c1ff3
10 files changed
+715
-43
No files matched your search
@@ -552,6 +552,8 @@ export function installDebugHook(app: Application, state: EditorState, editor: E
|
||||
/** What the scene view drew: meshes (one draw call each), cells and visible layers. */
|
||||
info: (objectId: string) => viewport.tilemapInfo(objectId),
|
||||
gameInfo: (objectId: string) => gameView.tilemapInfo(objectId),
|
||||
/** Where the scene view outlines the map as blocking: the merged rectangles, in cells (#132). */
|
||||
solids: (objectId: string) => viewport.solidInfo(objectId),
|
||||
/** Where the widgets of the inspector section sit: their texts are on screen elsewhere too. */
|
||||
toolAt: (tool: string) => {
|
||||
const button = currentTilemapSection()?.tools.get(tool as TilemapTool);
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
invert,
|
||||
linearOf,
|
||||
type Mat,
|
||||
scaleOf,
|
||||
} from '../../core/model/matrix';
|
||||
import { placementRejection } from '../../core/model/placement';
|
||||
import type { Position, Vec2 } from '../../core/model/Properties';
|
||||
@@ -34,13 +35,14 @@ import {
|
||||
tilemapRows,
|
||||
tilemapTileSize,
|
||||
} from '../../core/model/tilemap';
|
||||
import type { CellRect } from '../../core/model/tilemapCollision';
|
||||
import { footprintCorners } from '../../core/properties/bounds';
|
||||
import { localMatrixOf, parentTransform, transformOf } from '../../core/properties/transform';
|
||||
import { type EditorState, objectSelection, type TilemapTool } from '../state';
|
||||
import { paintedLayerId } from '../inspector/tilemapSection';
|
||||
import { activeDrag, drawBand, drawGrid, endDrag, onDragChange, Theme, textStyle } from '../../ui';
|
||||
import { collectHolderItems, type DrawItem } from '../../viewport/items';
|
||||
import { dashedLine, drawSourceIcon } from '../../viewport/symbols';
|
||||
import { dashedLine, drawSourceIcon, placeNode } from '../../viewport/symbols';
|
||||
import { screenToWorld, type View, worldToScreen } from '../../viewport/view';
|
||||
import { FreeViewport } from './FreeViewport';
|
||||
import type { Frame } from './WorldViewport';
|
||||
@@ -115,6 +117,8 @@ export class SceneViewport extends FreeViewport {
|
||||
private readonly gizmos = new Graphics();
|
||||
private readonly labels = new Map<string, Text>();
|
||||
private readonly icons = new Map<string, Graphics>();
|
||||
/** Where each placed tilemap blocks, outlined like a collider (#132). */
|
||||
private readonly solids = new Map<string, Graphics>();
|
||||
private dragging: ObjectDrag | null = null;
|
||||
private stroke: PaintStroke | null = null;
|
||||
private marquee: Marquee | null = null;
|
||||
@@ -500,6 +504,7 @@ export class SceneViewport extends FreeViewport {
|
||||
|
||||
protected override syncDecorations(item: DrawItem, onScreen: boolean, scale: number, extent: Extent): void {
|
||||
this.syncIcon(item, onScreen);
|
||||
this.syncSolids(item, onScreen, scale);
|
||||
this.syncLabel(item, onScreen, scale, extent);
|
||||
}
|
||||
|
||||
@@ -508,6 +513,46 @@ export class SceneViewport extends FreeViewport {
|
||||
this.labels.delete(id);
|
||||
this.icons.get(id)?.destroy();
|
||||
this.icons.delete(id);
|
||||
this.solids.get(id)?.destroy();
|
||||
this.solids.delete(id);
|
||||
}
|
||||
|
||||
/** The blocking rectangles of a placed tilemap as outlined, in cells (test hook). */
|
||||
solidInfo(id: string): { visible: boolean; rects: CellRect[] } | null {
|
||||
const node = this.solids.get(id);
|
||||
const item = this.lastItems.find((i) => i.id === id);
|
||||
if (!node || !item?.tilemap) return null;
|
||||
return { visible: node.visible, rects: item.tilemap.solids.map((rect) => ({ ...rect })) };
|
||||
}
|
||||
|
||||
/**
|
||||
* The solid cells of a tilemap, drawn the way a collider is: tinted and outlined, above the
|
||||
* content so a painted wall cannot hide them. Editor-only for the same reason as the icon: the
|
||||
* game view never syncs decorations.
|
||||
*/
|
||||
private syncSolids(item: DrawItem, show: boolean, scale: number): void {
|
||||
let node = this.solids.get(item.id);
|
||||
const rects = item.tilemap?.solids ?? [];
|
||||
if (!show || rects.length === 0) {
|
||||
if (node) node.visible = false;
|
||||
return;
|
||||
}
|
||||
if (!node) {
|
||||
node = new Graphics();
|
||||
node.eventMode = 'none';
|
||||
this.solids.set(item.id, node);
|
||||
this.overlay.addChild(node);
|
||||
}
|
||||
node.visible = true;
|
||||
placeNode(node, item.m);
|
||||
const tile = tilemapTileSize(item.props);
|
||||
const width = 2 / (scale * scaleOf(item.m));
|
||||
node.clear();
|
||||
for (const rect of rects) {
|
||||
const corner = cellOrigin(item.props, rect.x, rect.y);
|
||||
node.rect(corner.x, corner.y, rect.w * tile, rect.h * tile);
|
||||
}
|
||||
node.fill({ color: Theme.colors.collider, alpha: 0.12 }).stroke({ width, color: Theme.colors.collider });
|
||||
}
|
||||
|
||||
/** The icon of a particle source, the name beside it and whether it hangs off its owner (test hook). */
|
||||
|
||||
Reference in new issue
Block a user