Edit the graph during play and watch values and flow run through it
The graph was only locked by five edit-mode guards in GraphCanvas. The running game reads the graph by reference each frame, so a new node, edge, changed value, disconnect or undo takes effect from the next frame. Stop discards these edits like everything else changed in play. A new On start node does not fire for a behavior that has already started. During play every data output shows the value it last produced, in the highlight colour and refreshed ten times a second. Values are kept per graph, because node ids are only unique within one; with several instances the most recent value wins. The Variables node reads the live variable bag, so only its own outputs are kept, as a snapshot. Each time an exec edge fires, a bead runs along it in 0.3 s, at most one every 0.15 s per edge, following the same curve the edge is drawn with. Beads run on game time and freeze while paused; the game logic is unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
495756b500
commit
656d61f187
17 files changed
+542
-37
No files matched your search
@@ -1,11 +1,13 @@
|
||||
import {
|
||||
asNumber,
|
||||
type EntityHandle,
|
||||
type Graph,
|
||||
type GraphNode,
|
||||
GraphInterpreter,
|
||||
isHandleEnabled,
|
||||
type NodeContext,
|
||||
type TraceListener,
|
||||
type Value,
|
||||
} from '../../core/graph';
|
||||
import { stepKeyRepeat } from '../../core/graph/keyRepeat';
|
||||
import { behaviorLookup } from '../../core/model/behaviorPrograms';
|
||||
@@ -24,12 +26,19 @@ export const TRACE_FADE = 0.35;
|
||||
export interface Trace {
|
||||
edges: Map<string, number>;
|
||||
nodes: Map<string, number>;
|
||||
values: Map<string, Record<string, Value>>;
|
||||
}
|
||||
|
||||
interface Produced {
|
||||
time: number;
|
||||
values: Record<string, Value>;
|
||||
}
|
||||
|
||||
interface Instance {
|
||||
entity: Entity;
|
||||
interpreter: GraphInterpreter;
|
||||
trace: Trace;
|
||||
trace: Omit<Trace, 'values'>;
|
||||
values: Map<Graph, Map<string, Produced>>;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,13 +109,19 @@ export class BehaviorSystem implements System {
|
||||
continue;
|
||||
const asset = findAssetOf(this.project, entity.assetId, 'behavior');
|
||||
if (!asset) continue;
|
||||
const trace: Trace = { edges: new Map(), nodes: new Map() };
|
||||
const trace = { edges: new Map<string, number>(), nodes: new Map<string, number>() };
|
||||
const values = new Map<Graph, Map<string, Produced>>();
|
||||
const interpreter = new GraphInterpreter(asset.graph, behaviorLookup(this.project));
|
||||
interpreter.trace = {
|
||||
onEdge: (id) => trace.edges.set(id, world.time),
|
||||
onNode: (id) => trace.nodes.set(id, world.time),
|
||||
onValues: (graph, id, produced) => {
|
||||
let byNode = values.get(graph);
|
||||
if (!byNode) values.set(graph, (byNode = new Map<string, Produced>()));
|
||||
byNode.set(id, { time: world.time, values: produced });
|
||||
},
|
||||
} satisfies TraceListener;
|
||||
const instance = { entity, interpreter, trace };
|
||||
const instance = { entity, interpreter, trace, values };
|
||||
this.instances.push(instance);
|
||||
this.run(instance, (i, ctx) => i.fire('OnStart', ctx), 0);
|
||||
}
|
||||
@@ -203,15 +218,22 @@ export class BehaviorSystem implements System {
|
||||
* those that ran it as a node, which is the only way a nested behavior's graph ever lights up.
|
||||
*/
|
||||
traceFor(assetId: string): Trace {
|
||||
const merged: Trace = { edges: new Map(), nodes: new Map() };
|
||||
const merged: Trace = { edges: new Map(), nodes: new Map(), values: new Map() };
|
||||
const latest = (into: Map<string, number>, from: Map<string, number>) => {
|
||||
for (const [id, t] of from) into.set(id, Math.max(t, into.get(id) ?? -Infinity));
|
||||
};
|
||||
const graph = findAssetOf(this.project, assetId, 'behavior')?.graph;
|
||||
const newest = new Map<string, Produced>();
|
||||
for (const instance of this.instances) {
|
||||
if (instance.entity.assetId !== assetId && !instance.interpreter.calledBehaviors.has(assetId)) continue;
|
||||
latest(merged.edges, instance.trace.edges);
|
||||
latest(merged.nodes, instance.trace.nodes);
|
||||
for (const [id, produced] of (graph && instance.values.get(graph)) ?? []) {
|
||||
const known = newest.get(id);
|
||||
if (!known || produced.time >= known.time) newest.set(id, produced);
|
||||
}
|
||||
}
|
||||
for (const [id, produced] of newest) merged.values.set(id, produced.values);
|
||||
return merged;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user