Collider asset type, CollisionSystem and collision nodes
- Asset type 'collider' (box/circle) placed as child of a body object - 'Add collider' shortcut (hierarchy context menu + inspector) - CollisionSystem with enter/stay/exit pairs, sibling colliders excluded - Nodes: On collision enter/stay/exit, Is touching - Green outline in edit mode, hidden in play Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
26d5edc496
commit
0a59513b74
21 files changed
+525
-16
No files matched your search
@@ -5,6 +5,7 @@ import { type Position, type PropertyBag, sortByZ, type Vec2 } from '../core/mod
|
||||
import { treeOrder, worldPosition } from '../core/model/hierarchy';
|
||||
import { findAsset } from '../core/model/Project';
|
||||
import { cameraFrame, rectAround, rectsIntersect, type WorldRect } from '../core/properties/camera';
|
||||
import { colliderShape, colliderSize } from '../core/properties/collider';
|
||||
import { resolveSceneObjectProps } from '../core/properties/resolve';
|
||||
import type { World } from '../runtime/World';
|
||||
import { Theme, textStyle, Widget, type Size } from '../ui';
|
||||
@@ -238,6 +239,7 @@ export class Viewport extends Widget {
|
||||
private itemSize(item: DrawItem): { w: number; h: number } {
|
||||
if (item.type === 'sprite') return { w: Number(item.props.width ?? 1), h: Number(item.props.height ?? 1) };
|
||||
if (item.type === 'behavior') return { w: BEHAVIOR_SYMBOL, h: BEHAVIOR_SYMBOL };
|
||||
if (item.type === 'collider') return colliderSize(item.props);
|
||||
return cameraFrame(item.props);
|
||||
}
|
||||
|
||||
@@ -298,6 +300,12 @@ export class Viewport extends Widget {
|
||||
node.moveTo(-6, -3).lineTo(-1, -3).lineTo(-1, 3).lineTo(6, 3).stroke({ width: 2, color: Theme.colors.accentText });
|
||||
node.circle(-6, -3, 2).fill(Theme.colors.accentText);
|
||||
node.circle(6, 3, 2).fill(Theme.colors.accentText);
|
||||
} else if (item.type === 'collider') {
|
||||
// Outline plus a faint fill: the fill keeps the shape hit-testable and reads as "area".
|
||||
const shape = colliderShape(item.props, { x: 0, y: 0 });
|
||||
if (shape.kind === 'circle') node.circle(0, 0, shape.r);
|
||||
else node.rect(-shape.w / 2, -shape.h / 2, shape.w, shape.h);
|
||||
node.fill({ color: Theme.colors.collider, alpha: 0.12 }).stroke({ width: 2 / view.scale, color: Theme.colors.collider });
|
||||
}
|
||||
}
|
||||
this.drawParentLinks(items, editing, view.scale);
|
||||
@@ -335,14 +343,15 @@ export class Viewport extends Widget {
|
||||
|
||||
/**
|
||||
* Dashed line from every parented symbol (behavior, camera) to its parent (edit mode only).
|
||||
* Sprites show their nesting in the hierarchy panel instead, to keep the scene uncluttered.
|
||||
* Sprites show their nesting in the hierarchy panel instead, to keep the scene uncluttered;
|
||||
* colliders sit on their body anyway, so they get no line either.
|
||||
*/
|
||||
private drawParentLinks(items: DrawItem[], editing: boolean, scale: number): void {
|
||||
this.links.clear();
|
||||
if (!editing) return;
|
||||
const byId = new Map(items.map((i) => [i.id, i]));
|
||||
for (const item of items) {
|
||||
if (!this.isSymbol(item) || !item.parentId) continue;
|
||||
if (!this.isSymbol(item) || !item.parentId || item.type === 'collider') continue;
|
||||
const parent = byId.get(item.parentId);
|
||||
if (!parent) continue;
|
||||
const color = item.type === 'behavior' ? Theme.colors.behavior : Theme.colors.textMuted;
|
||||
@@ -368,7 +377,7 @@ export class Viewport extends Widget {
|
||||
label.visible = true;
|
||||
label.text = item.name;
|
||||
label.scale.set(1 / scale);
|
||||
if (item.type === 'camera') label.position.set(item.x - size.w / 2, item.y - size.h / 2 - 16 / scale);
|
||||
if (item.type === 'camera' || item.type === 'collider') label.position.set(item.x - size.w / 2, item.y - size.h / 2 - 16 / scale);
|
||||
else label.position.set(item.x + size.w / 2 + 4 / scale, item.y - 8 / scale);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user