Show each camera only its layers and lay overlay cameras on top (#302)
A camera could only show the whole scene, so a HUD drawn in the world would run through a camera shader (#299) with everything else. Render layers say which cameras see an object, and overlay cameras draw their own picture over the main one. - The project has a list of up to 16 render layers, edited in Project settings next to the collision layers. It is a list of its own, read by the same rules (`layerNamesOf`). - Sprites, texts, tilemaps, particle sources and containers carry a row `Render layer` under their switches. Its default `Inherit` takes the parent's layer, at the top the first one. - A camera has `Sees` (all layers by default) and `Overlay`. The main camera is the first one switched on without Overlay, so two plain cameras still draw through the first one only. - Every view through a camera keeps only what that camera sees and lays one see-through view per overlay camera over itself. That view is a `WorldViewport` of its own, handed its view and frame from below. - An overlay is fitted into the main camera's whole picture without distortion. One of the same size and zoom covers it exactly. - `z` sorts within one camera only. The pictures lie on top of each other as a whole, which is what a shader per camera (#299) needs. - The Game tab's preview now collects the scene once per redraw. Before it collected it twice, with the cameras it would have been five times. - `SetCollisionLayersCommand` became `SetLayerNamesCommand`, which takes the list it sets. Follow-ups: #304 (the mouse through a chosen camera) and #305 (change the render layer at run time). Tests: - Gate: 226 files, 3487 tests (3480 + 7) - e2e 63 (new): 4 checks. A green overlay sprite at z -10 covers a red main sprite at z 1000, in the preview and in the running game. A `UI` sprite in front of the main camera and a `Default` one in front of the overlay stay away. Without Overlay the second camera draws nothing. The settings dialog adds a render layer at the end. - Counter-checks: inheriting from parents switched off fails 1 unit test. Overlays never drawn fails the two overlay checks in e2e 63. - e2e total 1720 (1716 + 4), computed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f03ca0d382
commit
b2326085ed
33 files changed
+802
-115
No files matched your search
@@ -29,6 +29,7 @@ import {
|
||||
type VariableOwnerAsset,
|
||||
type VariablesAsset,
|
||||
} from '../../core/model/Asset';
|
||||
import { RENDER_LAYER_KEY } from '../../core/model/renderLayers';
|
||||
import {
|
||||
MakeListItemTypeCommand,
|
||||
MakeStructTypeCommand,
|
||||
@@ -181,7 +182,9 @@ export function refreshAssetSection(host: InspectorHost, assetId: string): boole
|
||||
return true;
|
||||
}
|
||||
|
||||
const isSwitch = (key: string): boolean => COMMON_PROPS.some((def) => def.key === key);
|
||||
/** The switches, and the render layer (#302): both say whether the object is seen at all. */
|
||||
const isSwitch = (key: string): boolean =>
|
||||
key === RENDER_LAYER_KEY || COMMON_PROPS.some((def) => def.key === key);
|
||||
|
||||
/** The rows of `schema` that are shown, under `title` – which is left out with them. */
|
||||
function buildProps(host: InspectorHost, asset: Asset, schema: readonly PropertyDef[], title: string): void {
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
maskLabel,
|
||||
maskWith,
|
||||
} from '../../core/model/collisionLayers';
|
||||
import { INHERIT_LABEL, INHERIT_LAYER, ownRenderLayer } from '../../core/model/renderLayers';
|
||||
import {
|
||||
assetChoiceLabel,
|
||||
assetRefChoices,
|
||||
@@ -634,18 +635,23 @@ export function createField(
|
||||
}
|
||||
case 'layer': {
|
||||
// The names live in the project, not in the row: renaming a layer renames it everywhere.
|
||||
const names = () => ctx.project().collisionLayers;
|
||||
const labelOf = (v: PropertyValue) => layerName(names(), layerIndexOf(v));
|
||||
const render = def.renderLayers === true;
|
||||
const names = () => (render ? ctx.project().renderLayers : ctx.project().collisionLayers);
|
||||
const labelOf = (v: PropertyValue) => {
|
||||
if (!render) return layerName(names(), layerIndexOf(v));
|
||||
const own = ownRenderLayer(v);
|
||||
return own === INHERIT_LAYER ? INHERIT_LABEL : layerName(names(), own);
|
||||
};
|
||||
const button = new Dropdown(labelOf(value));
|
||||
button.onClick = () => {
|
||||
new Menu(names().map((name, i) => ({ label: name, onSelect: () => handlers.onChange(i) }))).openBelow(
|
||||
button,
|
||||
);
|
||||
const layers = names().map((name, i) => ({ label: name, onSelect: () => handlers.onChange(i) }));
|
||||
const inherit = { label: INHERIT_LABEL, onSelect: () => handlers.onChange(INHERIT_LAYER) };
|
||||
new Menu(render ? [inherit, ...layers] : layers).openBelow(button);
|
||||
};
|
||||
return { widget: button, grow: true, setValue: (v) => button.setText(labelOf(v)) };
|
||||
}
|
||||
case 'layers': {
|
||||
const names = () => ctx.project().collisionLayers;
|
||||
const names = () => (def.renderLayers ? ctx.project().renderLayers : ctx.project().collisionLayers);
|
||||
const labelOf = (v: PropertyValue) => maskLabel(layerMaskOf(v), names());
|
||||
let mask = layerMaskOf(value);
|
||||
const button = new Dropdown(labelOf(value));
|
||||
|
||||
@@ -9,6 +9,7 @@ import type { ValuePath } from '../../core/properties/valuePath';
|
||||
import { ResizeTilemapEdgeCommand } from '../../core/commands/tilemapCommands';
|
||||
import { TILEMAP_COLUMNS_KEY, TILEMAP_EDGES, TILEMAP_ROWS_KEY } from '../../core/model/tilemap';
|
||||
import { findUsages } from '../../core/model/usages';
|
||||
import { RENDER_LAYER_KEY } from '../../core/model/renderLayers';
|
||||
import {
|
||||
ClearOverrideCommand,
|
||||
MoveSceneObjectCommand,
|
||||
@@ -177,6 +178,8 @@ export function buildObjectSection(host: InspectorHost, holderId: string, object
|
||||
if (asset.type === 'behavior')
|
||||
for (const def of schemaFor('behavior').filter((d) => !isCommonProp(d.key)))
|
||||
propertyRow(host, holderId, objectId, asset, def, resolved);
|
||||
for (const def of schemaForObject(project, asset, object).filter((d) => d.key === RENDER_LAYER_KEY))
|
||||
propertyRow(host, holderId, objectId, asset, def, resolved);
|
||||
|
||||
addTitle(host, object.parentId ? 'POSITION (LOCAL TO PARENT)' : 'POSITION');
|
||||
for (const axis of POSITION_AXES) {
|
||||
|
||||
Reference in new issue
Block a user