feat: global assetBase for scene JSON, convert demo to relative paths
This commit is contained in:
@@ -100,6 +100,7 @@ export interface GameData {
|
|||||||
scenes: Record<string, SceneNode>
|
scenes: Record<string, SceneNode>
|
||||||
startScene: string
|
startScene: string
|
||||||
variables: Record<string, number>
|
variables: Record<string, number>
|
||||||
|
assetBase?: string
|
||||||
chapters?: ChapterInfo[]
|
chapters?: ChapterInfo[]
|
||||||
achievements?: AchievementDef[]
|
achievements?: AchievementDef[]
|
||||||
endings?: EndingDef[]
|
endings?: EndingDef[]
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"assetBase": "",
|
||||||
"startScene": "intro",
|
"startScene": "intro",
|
||||||
"variables": {
|
"variables": {
|
||||||
"trust": 50,
|
"trust": 50,
|
||||||
@@ -32,43 +33,43 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"endings": [
|
"endings": [
|
||||||
{ "id": "trust_end", "label": "信任的伙伴", "sceneId": "trust_ending", "chapterId": "ch1", "thumbnail": "/images/end_trust.jpg" },
|
{ "id": "trust_end", "label": "信任的伙伴", "sceneId": "trust_ending", "chapterId": "ch1", "thumbnail": "images/end_trust.jpg" },
|
||||||
{ "id": "alone_end", "label": "独行之路", "sceneId": "alone_ending", "chapterId": "ch1", "thumbnail": "/images/end_alone.jpg" },
|
{ "id": "alone_end", "label": "独行之路", "sceneId": "alone_ending", "chapterId": "ch1", "thumbnail": "images/end_alone.jpg" },
|
||||||
{ "id": "continue_end", "label": "继续前行", "sceneId": "continue_ending", "chapterId": "ch3", "thumbnail": "/images/end_continue.jpg" }
|
{ "id": "continue_end", "label": "继续前行", "sceneId": "continue_ending", "chapterId": "ch3", "thumbnail": "images/end_continue.jpg" }
|
||||||
],
|
],
|
||||||
"chapters": [
|
"chapters": [
|
||||||
{
|
{
|
||||||
"id": "ch1",
|
"id": "ch1",
|
||||||
"label": "第一章:醒来",
|
"label": "第一章:醒来",
|
||||||
"startScene": "intro",
|
"startScene": "intro",
|
||||||
"thumbnail": "/images/ch1.jpg",
|
"thumbnail": "images/ch1.jpg",
|
||||||
"defaultVariables": { "trust": 50, "courage": 0, "investigation": 0 }
|
"defaultVariables": { "trust": 50, "courage": 0, "investigation": 0 }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "ch2",
|
"id": "ch2",
|
||||||
"label": "第二章:调查",
|
"label": "第二章:调查",
|
||||||
"startScene": "desk_detail",
|
"startScene": "desk_detail",
|
||||||
"thumbnail": "/images/ch2.jpg",
|
"thumbnail": "images/ch2.jpg",
|
||||||
"defaultVariables": { "trust": 60, "courage": 10, "investigation": 1 }
|
"defaultVariables": { "trust": 60, "courage": 10, "investigation": 1 }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "ch3",
|
"id": "ch3",
|
||||||
"label": "第三章:终局",
|
"label": "第三章:终局",
|
||||||
"startScene": "qte_success",
|
"startScene": "qte_success",
|
||||||
"thumbnail": "/images/ch3.jpg",
|
"thumbnail": "images/ch3.jpg",
|
||||||
"defaultVariables": { "trust": 70, "courage": 20, "investigation": 2 }
|
"defaultVariables": { "trust": 70, "courage": 20, "investigation": 2 }
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"scenes": {
|
"scenes": {
|
||||||
"intro": {
|
"intro": {
|
||||||
"id": "intro",
|
"id": "intro",
|
||||||
"videoUrl": "/videos/intro.mp4",
|
"videoUrl": "videos/intro.mp4",
|
||||||
"subtitleUrl": "/subtitles/intro.vtt",
|
"subtitleUrl": "subtitles/intro.vtt",
|
||||||
"subtitles": {
|
"subtitles": {
|
||||||
"zh": "/subtitles/intro.vtt",
|
"zh": "subtitles/intro.vtt",
|
||||||
"en": "/subtitles/intro_en.vtt"
|
"en": "subtitles/intro_en.vtt"
|
||||||
},
|
},
|
||||||
"bgmUrl": "/audio/calm_bgm.mp3",
|
"bgmUrl": "audio/calm_bgm.mp3",
|
||||||
"bgmVolume": 0.6,
|
"bgmVolume": 0.6,
|
||||||
"bgmCrossFade": 1.5,
|
"bgmCrossFade": 1.5,
|
||||||
"videoMuted": true,
|
"videoMuted": true,
|
||||||
@@ -105,8 +106,8 @@
|
|||||||
"id": "investigation_site",
|
"id": "investigation_site",
|
||||||
"type": "image",
|
"type": "image",
|
||||||
"videoUrl": "",
|
"videoUrl": "",
|
||||||
"imageUrl": "/images/investigation_scene.jpg",
|
"imageUrl": "images/investigation_scene.jpg",
|
||||||
"subtitleUrl": "/subtitles/investigation.vtt",
|
"subtitleUrl": "subtitles/investigation.vtt",
|
||||||
"hotspots": [
|
"hotspots": [
|
||||||
{
|
{
|
||||||
"id": "hs_desk",
|
"id": "hs_desk",
|
||||||
@@ -142,7 +143,7 @@
|
|||||||
},
|
},
|
||||||
"corridor": {
|
"corridor": {
|
||||||
"id": "corridor",
|
"id": "corridor",
|
||||||
"videoUrl": "/videos/corridor.mp4",
|
"videoUrl": "videos/corridor.mp4",
|
||||||
"hotspots": [
|
"hotspots": [
|
||||||
{
|
{
|
||||||
"id": "hs_left",
|
"id": "hs_left",
|
||||||
@@ -172,8 +173,8 @@
|
|||||||
},
|
},
|
||||||
"left_door": {
|
"left_door": {
|
||||||
"id": "left_door",
|
"id": "left_door",
|
||||||
"videoUrl": "/videos/left_door.mp4",
|
"videoUrl": "videos/left_door.mp4",
|
||||||
"subtitleUrl": "/subtitles/left_door.vtt",
|
"subtitleUrl": "subtitles/left_door.vtt",
|
||||||
"choices": [
|
"choices": [
|
||||||
{
|
{
|
||||||
"text": "与陌生人握手",
|
"text": "与陌生人握手",
|
||||||
@@ -192,9 +193,9 @@
|
|||||||
},
|
},
|
||||||
"right_door": {
|
"right_door": {
|
||||||
"id": "right_door",
|
"id": "right_door",
|
||||||
"videoUrl": "/videos/right_door.mp4",
|
"videoUrl": "videos/right_door.mp4",
|
||||||
"skippable": false,
|
"skippable": false,
|
||||||
"bgmUrl": "/audio/tense_bgm.mp3",
|
"bgmUrl": "audio/tense_bgm.mp3",
|
||||||
"bgmVolume": 0.7,
|
"bgmVolume": 0.7,
|
||||||
"bgmCrossFade": 2.0,
|
"bgmCrossFade": 2.0,
|
||||||
"videoMuted": true,
|
"videoMuted": true,
|
||||||
@@ -216,7 +217,7 @@
|
|||||||
},
|
},
|
||||||
"qte_success": {
|
"qte_success": {
|
||||||
"id": "qte_success",
|
"id": "qte_success",
|
||||||
"videoUrl": "/videos/qte_success.mp4",
|
"videoUrl": "videos/qte_success.mp4",
|
||||||
"choices": [
|
"choices": [
|
||||||
{
|
{
|
||||||
"text": "继续前进",
|
"text": "继续前进",
|
||||||
@@ -230,7 +231,7 @@
|
|||||||
},
|
},
|
||||||
"qte_fail": {
|
"qte_fail": {
|
||||||
"id": "qte_fail",
|
"id": "qte_fail",
|
||||||
"videoUrl": "/videos/qte_fail.mp4",
|
"videoUrl": "videos/qte_fail.mp4",
|
||||||
"choices": [
|
"choices": [
|
||||||
{
|
{
|
||||||
"text": "继续前进",
|
"text": "继续前进",
|
||||||
@@ -244,7 +245,7 @@
|
|||||||
},
|
},
|
||||||
"desk_detail": {
|
"desk_detail": {
|
||||||
"id": "desk_detail",
|
"id": "desk_detail",
|
||||||
"videoUrl": "/videos/continue_ending.mp4",
|
"videoUrl": "videos/continue_ending.mp4",
|
||||||
"choices": [
|
"choices": [
|
||||||
{
|
{
|
||||||
"text": "返回调查现场",
|
"text": "返回调查现场",
|
||||||
@@ -258,9 +259,9 @@
|
|||||||
},
|
},
|
||||||
"stay": {
|
"stay": {
|
||||||
"id": "stay",
|
"id": "stay",
|
||||||
"videoUrl": "/videos/stay_loop.mp4",
|
"videoUrl": "videos/stay_loop.mp4",
|
||||||
"subtitleUrl": "/subtitles/stay.vtt",
|
"subtitleUrl": "subtitles/stay.vtt",
|
||||||
"bgmUrl": "/audio/calm_bgm.mp3",
|
"bgmUrl": "audio/calm_bgm.mp3",
|
||||||
"bgmVolume": 0.6,
|
"bgmVolume": 0.6,
|
||||||
"videoMuted": true,
|
"videoMuted": true,
|
||||||
"loopStart": 3.0,
|
"loopStart": 3.0,
|
||||||
@@ -271,7 +272,7 @@
|
|||||||
},
|
},
|
||||||
"trust_ending": {
|
"trust_ending": {
|
||||||
"id": "trust_ending",
|
"id": "trust_ending",
|
||||||
"videoUrl": "/videos/trust_ending.mp4",
|
"videoUrl": "videos/trust_ending.mp4",
|
||||||
"choices": [
|
"choices": [
|
||||||
{
|
{
|
||||||
"text": "开启信任的旅程(需要 trust >= 80)",
|
"text": "开启信任的旅程(需要 trust >= 80)",
|
||||||
@@ -290,12 +291,12 @@
|
|||||||
},
|
},
|
||||||
"secret_ending": {
|
"secret_ending": {
|
||||||
"id": "secret_ending",
|
"id": "secret_ending",
|
||||||
"videoUrl": "/videos/continue_ending.mp4",
|
"videoUrl": "videos/continue_ending.mp4",
|
||||||
"choices": []
|
"choices": []
|
||||||
},
|
},
|
||||||
"alone_ending": {
|
"alone_ending": {
|
||||||
"id": "alone_ending",
|
"id": "alone_ending",
|
||||||
"videoUrl": "/videos/alone_ending.mp4",
|
"videoUrl": "videos/alone_ending.mp4",
|
||||||
"choices": [],
|
"choices": [],
|
||||||
"onEnter": [
|
"onEnter": [
|
||||||
{ "type": "set", "target": "completed_game", "value": 1 }
|
{ "type": "set", "target": "completed_game", "value": 1 }
|
||||||
@@ -303,7 +304,7 @@
|
|||||||
},
|
},
|
||||||
"continue_ending": {
|
"continue_ending": {
|
||||||
"id": "continue_ending",
|
"id": "continue_ending",
|
||||||
"videoUrl": "/videos/continue_ending.mp4",
|
"videoUrl": "videos/continue_ending.mp4",
|
||||||
"choices": []
|
"choices": []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,9 +111,43 @@ export function useGameEngine(videoEls: () => [HTMLVideoElement | null, HTMLVide
|
|||||||
store.setVideoTime(t)
|
store.setVideoTime(t)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function resolveAsset(base: string, path: string): string {
|
||||||
|
if (!path || path.startsWith('http://') || path.startsWith('https://') || path.startsWith('data:')) return path
|
||||||
|
const b = base.endsWith('/') ? base.slice(0, -1) : base
|
||||||
|
const p = path.startsWith('/') ? path : '/' + path
|
||||||
|
return b + p
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyAssetBase(data: GameData) {
|
||||||
|
const base = data.assetBase || ''
|
||||||
|
if (!base) return
|
||||||
|
for (const scene of Object.values(data.scenes)) {
|
||||||
|
if (scene.videoUrl) scene.videoUrl = resolveAsset(base, scene.videoUrl)
|
||||||
|
if (scene.subtitleUrl) scene.subtitleUrl = resolveAsset(base, scene.subtitleUrl)
|
||||||
|
if (scene.imageUrl) scene.imageUrl = resolveAsset(base, scene.imageUrl)
|
||||||
|
if (scene.bgmUrl) scene.bgmUrl = resolveAsset(base, scene.bgmUrl)
|
||||||
|
if (scene.subtitles) {
|
||||||
|
for (const k of Object.keys(scene.subtitles)) {
|
||||||
|
scene.subtitles[k] = resolveAsset(base, scene.subtitles[k])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data.endings) {
|
||||||
|
for (const e of data.endings) {
|
||||||
|
if (e.thumbnail) e.thumbnail = resolveAsset(base, e.thumbnail)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data.chapters) {
|
||||||
|
for (const c of data.chapters) {
|
||||||
|
if (c.thumbnail) c.thumbnail = resolveAsset(base, c.thumbnail)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function loadGame(dataUrl: string) {
|
async function loadGame(dataUrl: string) {
|
||||||
const resp = await fetch(dataUrl)
|
const resp = await fetch(dataUrl)
|
||||||
const data: GameData = await resp.json()
|
const data: GameData = await resp.json()
|
||||||
|
applyAssetBase(data)
|
||||||
engine.sceneManager.load(data)
|
engine.sceneManager.load(data)
|
||||||
engine.stateManager.init(data.variables)
|
engine.stateManager.init(data.variables)
|
||||||
store.setChapters(data.chapters || [])
|
store.setChapters(data.chapters || [])
|
||||||
|
|||||||
Reference in New Issue
Block a user