Files
story-edit-web/test_scene.js
邓雨鹏 021080dd56 feat(timeline): P2 并行编排——scene 多轨编辑器 + 白模重叠预览
剧情 Timeline P2 前端 + 共享内核(与 SGame 源真同步):
- ir_core/IR_SCHEMA/样张:scene v0.3 + scene 校验 + 导出 gate(D3),与 SGame 仓逐字一致
- timeline.js:appendScene 按 authored start 铺多轨 clip(自然重叠预览),move from 同 actor 跨轨续连(D4);
  drawStage 改逐 actor 查对话→多人气泡同时计时;导出 _clipDur 纯函数;show() 加 startId 参;常量加 CAMERA_DUR
- scene_edit.js(新):演出段编辑模态——拖 clip 改 start(吸附 0.1s)、拖右缘改 dur、增删 clip/轨道、
  选中属性条精确编辑、客户端轻量 lint(镜像 validate.py)、▶ 预览此段(复用播放核)
- graph.js:scene 节点(KIND_CN/summary/nodeInner 列轨道)+双击进编辑模态
- form.js:右栏 renderScene 精确数值编辑(轨道/clip 的 start/dur/kind/目标)+打开编辑器按钮
- app.py export:捕获 CompileError 并入 report(scene 被拦时不再 500)
- test_scene.js:离线 10 断言全过(重叠确凿/晚 1.5s 起步/from 续连);gitignore 忽略本地 _localdemo.db

待浏览器目测拖拽编辑落 IR + 白模重叠演出。
2026-06-13 22:34:29 +08:00

63 lines
3.5 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 离线回归scene 多轨数据源 + 重叠演出无浏览器stub window 加载 timeline.js
// 跑法node test_scene.js
const fs = require("fs"), path = require("path");
global.window = {};
require("./web/static/timeline.js"); // 设置 window.Timeline加载期不碰 DOM
const T = global.window.Timeline;
function loadAnchors(psName) {
// 复刻 app.py /api/pointsets把点位集转成 anchors [{name,pos:[x,y,z]}]
const p = path.join("E:/Library/SGame/Assets/StreamingAssets/Story/PointSets", psName + ".points.json");
const ps = JSON.parse(fs.readFileSync(p, "utf-8"));
return (ps.points || []).map(pt => ({ name: pt.name, pos: pt.pos || [0, 0, 0], rot: pt.rot || 0 }));
}
let fails = 0;
function ok(cond, msg) { console.log((cond ? "PASS " : "FAIL ") + msg); if (!cond) fails++; }
function overlap(a, b) { return a.start < b.start + b.dur && b.start < a.start + a.dur; }
const ir = JSON.parse(fs.readFileSync("samples/scene_demo.ir.json", "utf-8"));
const anchors = loadAnchors("QY_TLDEMO");
const M = T._buildModel(ir, anchors);
// 1) scene clips 被铺出来P1/NP1 各有 moveNP2 有 dialogue镜头有 camera
const p1move = M.clips.find(c => c.actor === "P1" && c.kind === "move");
const np1move = M.clips.find(c => c.actor === "NP1" && c.kind === "move");
const np1say = M.clips.find(c => c.actor === "NP1" && c.kind === "dialogue");
const cam = M.clips.find(c => c.kind === "camera");
ok(!!p1move, "P1 走位 clip 存在");
ok(!!np1move, "NP1 走位 clip 存在");
ok(!!np1say, "NP1 对话 clip 存在");
ok(!!cam && cam.focus === "PT_CENTER", "镜头 clip 对焦 PT_CENTER");
// 2) 核心A 走与 1.5s 后 B 走在时间上重叠(两点同时移动)
ok(overlap(p1move, np1move),
`P1/NP1 走位重叠P1[${p1move.start.toFixed(2)},${(p1move.start + p1move.dur).toFixed(2)}] NP1[${np1move.start.toFixed(2)},${(np1move.start + np1move.dur).toFixed(2)}]`);
// 3) NP1 比 P1 晚 1.5s 起步authored start 偏移被尊重scene 整体在前置 narration 之后铺轴)
ok(Math.abs((np1move.start - p1move.start) - 1.5) < 1e-6, `NP1 比 P1 晚起步 ${(np1move.start - p1move.start).toFixed(2)}s 应=1.5`);
// 4) move from 续连P1 从初始锚点(-6) 起步、终点 PT_CENTER(+1.5)
ok(Math.abs(p1move.from.z - (-6)) < 1e-6 && Math.abs(p1move.to.z - 1.5) < 1e-6,
`P1 move from.z=${p1move.from.z}→to.z=${p1move.to.z}`);
// 5) scene 之后能续演到 choiceextendSegment 流过 scene→n_choice
const s = T._prepare(ir, anchors);
let r = T._extend(s, T._firstNode(ir));
ok(r && (r.kind === "choice"), "scene 后停在 choicekind=" + (r && r.kind));
// 6) NP2 对话比 P1 走位晚 4.5sscene-local start 偏移)
const np2say = M.clips.find(c => c.actor === "NP2" && c.kind === "dialogue");
ok(np2say && Math.abs((np2say.start - p1move.start) - 4.5) < 1e-6, "NP2 对话比 P1 晚 " + (np2say && (np2say.start - p1move.start).toFixed(2)) + "s 应=4.5");
// 7) 重叠时刻两人都在走(数据层:取两 move 区间交集中点)
const lo = Math.max(p1move.start, np1move.start), hi = Math.min(p1move.start + p1move.dur, np1move.start + np1move.dur);
const tau = (lo + hi) / 2;
const p1moving = tau >= p1move.start && tau < p1move.start + p1move.dur;
const np1moving = tau >= np1move.start && tau < np1move.start + np1move.dur;
ok(p1moving && np1moving, `tau=${tau.toFixed(2)}(重叠区间中点)时 P1 走=${p1moving} NP1 走=${np1moving}(应同时为真)`);
console.log("\n" + (fails ? (fails + " 个断言失败") : "全部通过"));
process.exit(fails ? 1 : 0);