feat(timeline): 无点位集时合成圆周布局兜底 + 演示事件
- 线上 NAS 多半没挂点位集卷(/api/pointsets 空),白模会无坐标。改为:真实锚点优先, 缺坐标时把引用到的点位(演员位/走位目标/镜头点)在圆周自动铺开,走位预览不空。 真实坐标存在时一律覆盖合成。mapinfo 标注当前是真实坐标还是示意布局。 - samples/timeline_demo.ir.json:QY_TLDEMO 演示事件,覆盖 P1 全部功能 (旁白/对话打字机/3角色走位/动画标记/镜头对焦/选择分支/双结局),离线测试合成布局走位全有位移
This commit is contained in:
@ -69,7 +69,31 @@
|
||||
const seq = linearize(IR);
|
||||
const roleName = {}; (IR.roles || []).forEach(r => roleName[r.slot] = r.name);
|
||||
const nm = s => s === "P1" ? "玩家" : (roleName[s] || s);
|
||||
const initPos = {}; (anchors || []).forEach(a => initPos[a.name] = { x: a.pos[0], z: a.pos[2] });
|
||||
|
||||
// 位置来源:真实锚点优先;点位集没挂/缺坐标时,把引用到的点位在圆周上**合成布局**,
|
||||
// 保证走位预览不空(线上未挂点位集卷的常见情形 + 尚未取点的事件)。
|
||||
const refs = [];
|
||||
const addRef = n => { if (n && !refs.includes(n)) refs.push(n); };
|
||||
addRef("P1"); (IR.roles || []).forEach(r => addRef(r.slot));
|
||||
seq.forEach(item => {
|
||||
const n = item.node; if (!n) return;
|
||||
addRef(n.speaker); addRef(n.actor);
|
||||
if (n.kind === "move") addRef(n.to);
|
||||
if (n.camera) addRef(n.camera);
|
||||
});
|
||||
const realPos = {}; (anchors || []).forEach(a => realPos[a.name] = { x: a.pos[0], z: a.pos[2] });
|
||||
const hasReal = Object.keys(realPos).length > 0;
|
||||
const posMap = {};
|
||||
const missing = refs.filter(r => !realPos[r]);
|
||||
const R = 6;
|
||||
missing.forEach((name, i) => {
|
||||
const ang = (i / Math.max(1, missing.length)) * Math.PI * 2 - Math.PI / 2;
|
||||
posMap[name] = { x: +(Math.cos(ang) * R).toFixed(2), z: +(Math.sin(ang) * R).toFixed(2) };
|
||||
});
|
||||
Object.assign(posMap, realPos); // 真实坐标覆盖合成
|
||||
const synthetic = !hasReal;
|
||||
|
||||
const initPos = {}; refs.forEach(r => { if (posMap[r]) initPos[r] = posMap[r]; });
|
||||
const curPos = {};
|
||||
const posOf = a => curPos[a] || initPos[a] || { x: 0, z: 0 };
|
||||
|
||||
@ -94,7 +118,7 @@
|
||||
if (n.camera) { clips.push({ row: "镜头", kind: "camera", start: t, dur, label: "对焦 " + n.camera, focus: n.camera }); useRow("镜头"); }
|
||||
t += dur;
|
||||
} else if (k === "move") {
|
||||
const sp = n.actor || "P1", from = posOf(sp), to = anchorXZ(anchors, n.to) || from;
|
||||
const sp = n.actor || "P1", from = posOf(sp), to = posMap[n.to] || from;
|
||||
const speed = n.speed || MOVE_SPEED, dist = Math.hypot(to.x - from.x, to.z - from.z), dur = Math.max(0.3, dist / speed);
|
||||
clips.push({ row: "演员:" + sp, kind: "move", start: t, dur, label: "→ " + (n.to || ""), actor: sp, from, to });
|
||||
useRow("演员:" + sp); curPos[sp] = { x: to.x, z: to.z }; t += dur;
|
||||
@ -114,14 +138,18 @@
|
||||
rowSet.forEach(r => { if (r.startsWith("演员:") && !order.includes(r)) order.push(r); });
|
||||
["镜头", "剧情"].forEach(r => { if (rowSet.includes(r)) order.push(r); });
|
||||
|
||||
// 舞台要画的锚点:有真实坐标用真实;否则用合成布局(含点位名,作背景参照)
|
||||
const displayAnchors = hasReal ? (anchors || [])
|
||||
: refs.filter(r => posMap[r]).map(r => ({ name: r, pos: [posMap[r].x, 0, posMap[r].z] }));
|
||||
|
||||
// 世界坐标范围(含锚点 + 所有走位终点),用于俯视舞台适配
|
||||
const xs = [], zs = [];
|
||||
(anchors || []).forEach(a => { xs.push(a.pos[0]); zs.push(a.pos[2]); });
|
||||
displayAnchors.forEach(a => { xs.push(a.pos[0]); zs.push(a.pos[2]); });
|
||||
clips.forEach(c => { if (c.from) { xs.push(c.from.x); zs.push(c.from.z); } if (c.to) { xs.push(c.to.x); zs.push(c.to.z); } });
|
||||
const bounds = xs.length ? { minX: Math.min(...xs), maxX: Math.max(...xs), minZ: Math.min(...zs), maxZ: Math.max(...zs) }
|
||||
: { minX: 0, maxX: 1, minZ: 0, maxZ: 1 };
|
||||
|
||||
return { clips, rows: order, total: Math.max(t, 0.1), anchors: anchors || [], initPos, nm, roleName, bounds };
|
||||
return { clips, rows: order, total: Math.max(t, 0.1), anchors: displayAnchors, initPos, nm, roleName, bounds, synthetic };
|
||||
}
|
||||
|
||||
// ---- 播放期查询 ----
|
||||
@ -190,7 +218,7 @@
|
||||
playhead: null,
|
||||
};
|
||||
els.mapinfo.textContent = "点位集:" + psName + (ps.mapId ? "(地图 " + ps.mapId + ")" : "") +
|
||||
(anchors.length ? "" : " ⚠ 无坐标,走位/俯视图将退化为示意");
|
||||
(model.synthetic ? " · ⚠ 未取到真实坐标,按示意布局自动铺开(走位仍可预览)" : " · 真实坐标");
|
||||
stageCv = els.stage; stageCtx = stageCv.getContext("2d");
|
||||
|
||||
buildTracks();
|
||||
|
||||
Reference in New Issue
Block a user