Tint a collider's area only while the collider itself is selected
A collider filled its area with a faint green in the scene view all the time. It lay over every sprite a collider sits on and made the art look washed out. The outline alone shows where a collider is. The area only matters while one works on that collider. - drawItem takes a selected flag. Only a selected collider is filled, every other one is drawn as its outline. - The fill is stronger than before, alpha 0.3 instead of 0.12, so the selected area is easy to see. The value is COLLIDER_FILL_ALPHA in src/viewport/symbols.ts. - The scene view says what is selected through isSelected. The game view keeps the default and never fills. - Only the collider object itself counts. Selecting its body leaves the area unfilled. Tests: - 2 new unit tests in tests/viewport/collider-color.test.ts read the Pixi instructions: outline only when not selected, fill at 0.3 and outline when selected. - Counter check: with the fill drawn regardless of selection the outline-only test fails. The original is back per cmp. - ./check.sh: 158 files, 2334 tests, CHECK EXIT 0. - e2e 1-17,28-30: no FAIL. e2e 39-43: ALL PASSED, 145 checks. A first run left out 39, which 40 builds on, and failed 8 checks in 40 for the missing tile only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4b7e8614a8
commit
495d87847d
4 files changed
+58
-4
No files matched your search
@@ -363,6 +363,11 @@ export class SceneViewport extends FreeViewport {
|
||||
return this.corners.info();
|
||||
}
|
||||
|
||||
protected override isSelected(objectId: string): boolean {
|
||||
const s = this.state.selection;
|
||||
return s?.kind === 'object' && s.objectIds.includes(objectId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pixi only says which hit area was pressed; what is really under the pointer is worked out
|
||||
* here, in the order it is drawn (#203): a tilemap's grid is empty where it has no tile, and a
|
||||
|
||||
@@ -137,6 +137,11 @@ export abstract class WorldViewport extends Widget {
|
||||
|
||||
protected onItemPointerDown(_objectId: string, _e: FederatedPointerEvent): void {}
|
||||
|
||||
/** Whether the object itself is selected – a selected collider shows its area filled. */
|
||||
protected isSelected(_objectId: string): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Screen-space drawing that follows the camera, redrawn after a full redraw and after a pan. */
|
||||
protected drawOnScreen(_items: DrawItem[], _view: View): void {}
|
||||
|
||||
@@ -317,7 +322,13 @@ export abstract class WorldViewport extends Widget {
|
||||
this.syncTilemap(item, onScreen);
|
||||
if (!onScreen) continue;
|
||||
this.visibleCount++;
|
||||
drawItem(node, item, size, view.scale * scaleOf(placementOf(item.type, item.m)));
|
||||
drawItem(
|
||||
node,
|
||||
item,
|
||||
size,
|
||||
view.scale * scaleOf(placementOf(item.type, item.m)),
|
||||
this.isSelected(item.id),
|
||||
);
|
||||
}
|
||||
this.afterItems(items, view);
|
||||
this.drawOnScreen(items, view);
|
||||
|
||||
@@ -23,6 +23,9 @@ import type { TextureLibrary } from './TextureLibrary';
|
||||
/** Screen-pixel half-width of the grab band along a collider's outline. */
|
||||
const COLLIDER_GRAB_BAND = 6;
|
||||
|
||||
/** How strongly a selected collider tints its area. */
|
||||
export const COLLIDER_FILL_ALPHA = 0.3;
|
||||
|
||||
/**
|
||||
* Gives a Pixi node the transform `m`, its scale multiplied by `w`×`h` (a unit quad becomes the
|
||||
* sprite's box). A similarity is a rotation and two scales whose signs carry the mirror, which is
|
||||
@@ -107,13 +110,15 @@ export function disposeSpriteMesh(mesh: Mesh): void {
|
||||
|
||||
/**
|
||||
* Draws in the node's own units after `placeNode` put it where the item is. `scale` is the
|
||||
* on-screen pixels per unit of the node, so strokes stay constant on screen.
|
||||
* on-screen pixels per unit of the node, so strokes stay constant on screen. Only a `selected`
|
||||
* collider fills its area; otherwise the tint would lie over every sprite a collider sits on.
|
||||
*/
|
||||
export function drawItem(
|
||||
node: Graphics,
|
||||
item: DrawItem,
|
||||
size: { w: number; h: number },
|
||||
scale: number,
|
||||
selected = false,
|
||||
): void {
|
||||
placeNode(node, placementOf(item.type, item.m));
|
||||
node.clear();
|
||||
@@ -153,7 +158,8 @@ export function drawItem(
|
||||
const shape = colliderShape(item.props, IDENTITY, item.frame);
|
||||
traceShape(node, shape);
|
||||
const color = colliderColor(shape, item.props);
|
||||
node.fill({ color, alpha: 0.12 }).stroke({ width: 2 / scale, color });
|
||||
if (selected) node.fill({ color, alpha: COLLIDER_FILL_ALPHA });
|
||||
node.stroke({ width: 2 / scale, color });
|
||||
const facing = oneWayFacing(item.props, IDENTITY);
|
||||
if (facing)
|
||||
markBlockingSide(node, shapeBounds(shape), facing, 5 / scale, color, {
|
||||
|
||||
@@ -3,7 +3,9 @@ import { IDENTITY } from '../../src/core/model/matrix';
|
||||
import type { PropertyBag } from '../../src/core/model/Properties';
|
||||
import { colliderShape } from '../../src/core/properties/collider';
|
||||
import { Theme } from '../../src/ui';
|
||||
import { colliderColor } from '../../src/viewport/symbols';
|
||||
import { Graphics } from 'pixi.js';
|
||||
import type { DrawItem } from '../../src/viewport/items';
|
||||
import { COLLIDER_FILL_ALPHA, colliderColor, drawItem } from '../../src/viewport/symbols';
|
||||
|
||||
const colorOf = (props: PropertyBag) => colliderColor(colliderShape(props, IDENTITY), props);
|
||||
|
||||
@@ -21,3 +23,33 @@ describe('the colour of a collider in the scene view, outline and icon alike', (
|
||||
expect(colorOf({ shape: 'polygon', points: bowtie, isTrigger: true })).toBe(Theme.colors.danger);
|
||||
});
|
||||
});
|
||||
|
||||
describe('the area of a collider in the scene view', () => {
|
||||
const item = {
|
||||
id: 'c',
|
||||
type: 'collider',
|
||||
m: IDENTITY,
|
||||
props: { shape: 'box', width: 40, height: 20 },
|
||||
frame: null,
|
||||
} as unknown as DrawItem;
|
||||
const actions = (selected: boolean) => {
|
||||
const node = new Graphics();
|
||||
drawItem(node, item, { w: 40, h: 20 }, 1, selected);
|
||||
return node.context.instructions.map((i) => ({
|
||||
action: i.action,
|
||||
alpha: (i.data.style as { alpha: number }).alpha,
|
||||
}));
|
||||
};
|
||||
|
||||
it('is only an outline while the collider is not selected', () => {
|
||||
expect(actions(false)).toEqual([{ action: 'stroke', alpha: 1 }]);
|
||||
});
|
||||
|
||||
it('is filled, more strongly than before, once the collider itself is selected', () => {
|
||||
expect(COLLIDER_FILL_ALPHA).toBeGreaterThan(0.12);
|
||||
expect(actions(true)).toEqual([
|
||||
{ action: 'fill', alpha: COLLIDER_FILL_ALPHA },
|
||||
{ action: 'stroke', alpha: 1 },
|
||||
]);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user