Pick the frame's outline when adding a collider to a sprite (#218)
"Add collider" on a sprite always made a box of the sprite's size, even when the sprite's picture is a frame that has a collision outline. The outline then had to be picked by hand as "Frame collision". - newColliderProps in colliderCommands.ts is the rule as a pure function. A sprite whose picture is a frame with an outline gets a collider with shape "frame" and that frame. Anything else keeps the box of the sprite's size. - The frame is the one the sprite shows when the collider is added, also when only this placement overrides it. - Decided here, the issue says nothing about size: a frame collider is as big as its frame, so a sprite drawn bigger or smaller with the same proportions gets the collider's scale set to match. - Decided here as well: a sprite stretched out of the frame's proportions keeps the box, because an outline cannot be stretched in one direction. Tests: - Gate: 198 files, 2914 tests (2911 + 3), exit 0 - e2e 8,42: 46 checks passed (42: +1 for 42e-ii) - Three new unit tests in tests/core/collider-parts.test.ts: the frame with undo, the scale with an overridden frame, and the cases that keep the box - 42e-ii goes through the context menu: "Frame collision" with scale 2 on the sprite wearing the wedge frame, a box on a sprite without a picture - Five unit counter-probes fail, 1 to 2 tests each. They ran on stein2, not in the container. - The browser counter-probe fails 42e-ii with shape "box" - The e2e sum of 1617 is calculated (1616 + 1). Neither the full suite nor the smokes ran for this state. - Vector art has no test of its own, it takes the same branch as a whole texture. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
defc34bd09
commit
d39bf83ea9
4 files changed
+150
-7
No files matched your search
@@ -2,9 +2,11 @@ import { newId } from '../ids';
|
||||
import { findObject } from '../model/hierarchy';
|
||||
import { isLiveInstance, type ObjectRef, resolvedHolder } from '../model/instance';
|
||||
import type { Asset } from '../model/Asset';
|
||||
import type { PropertyBag } from '../model/Properties';
|
||||
import { frameOutline } from '../model/frameOutline';
|
||||
import { type PropertyBag, textOf } from '../model/Properties';
|
||||
import { findAsset, type ObjectHolder, type Project } from '../model/Project';
|
||||
import { createSceneObject, objectName, type SceneObject } from '../model/SceneObject';
|
||||
import { matchesAspect } from '../properties/aspect';
|
||||
import { resolveSceneObjectProps } from '../properties/resolve';
|
||||
import { transformOfObject } from '../properties/transform';
|
||||
import { removeById } from '../util/list';
|
||||
@@ -74,7 +76,23 @@ export function colliderRefusal(project: Project, holder: ObjectHolder, target:
|
||||
return null;
|
||||
}
|
||||
|
||||
/** A new collider asset is sized like the object (sprites: width × height; anything else: schema defaults). */
|
||||
/**
|
||||
* What a new collider on `target` starts with. A sprite gets a box of its width × height – or, when
|
||||
* its picture is a frame with a collision outline, that outline (#218): a frame collider is the
|
||||
* frame's size, so it is scaled up to the sprite, and a sprite stretched out of the frame's
|
||||
* proportions keeps the box, since an outline cannot be stretched. Anything else: schema defaults.
|
||||
*/
|
||||
export function newColliderProps(project: Project, targetAsset: Asset, target: SceneObject): PropertyBag {
|
||||
if (targetAsset.type !== 'sprite') return {};
|
||||
const size = spriteSize(project, targetAsset, target);
|
||||
const frame = textOf(resolveSceneObjectProps(project, targetAsset, target).texture ?? '');
|
||||
const outline = frameOutline(project, frame);
|
||||
if (!outline || outline.pieces.length === 0 || !matchesAspect(outline, size)) return size;
|
||||
const scale = size.width / outline.w;
|
||||
return { shape: 'frame', frame, width: outline.w, height: outline.h, ...(scale === 1 ? {} : { scale }) };
|
||||
}
|
||||
|
||||
/** A new collider asset starts with `newColliderProps`. */
|
||||
export class AddColliderCommand extends AddPartCommand {
|
||||
readonly label = 'Add collider';
|
||||
protected readonly partType = 'collider';
|
||||
@@ -85,7 +103,7 @@ export class AddColliderCommand extends AddPartCommand {
|
||||
|
||||
protected build(project: Project, target: SceneObject, targetAsset: Asset, owner: string): Asset {
|
||||
const asset = buildAsset('collider', nextAssetName(project, `${owner} collider`), this.assetId);
|
||||
if (targetAsset.type === 'sprite') Object.assign(asset.props, spriteSize(project, targetAsset, target));
|
||||
Object.assign(asset.props, newColliderProps(project, targetAsset, target));
|
||||
return asset;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user