Story文件格式:修订间差异
小 (→vns文件: say的匹配正则) |
小无编辑摘要 |
||
(未显示同一用户的1个中间版本) | |||
第6行: | 第6行: | ||
{{特殊页面导航}} | {{特殊页面导航}} | ||
*'''这只是对Story文件的格式分析,并不表示任何人可以曲解剧情。''' | *'''这只是对Story文件的格式分析,并不表示任何人可以曲解剧情。''' | ||
*'''本页面将保持孤立状态 | *'''本页面将保持孤立状态 , 严禁链入/引用!''' | ||
在apk包中,所有关于剧情的部分都在/assets/app-data中, 目录树如下(仅示意,有省略) 。 | |||
在apk包中,所有关于剧情的部分都在 | <pre> | ||
app-data | |||
├─story | |||
│ │ paths | |||
│ ├─cg | |||
│ │ 1-1.jpg | |||
│ ├─main | |||
│ │ entries_1 | |||
│ │ u.csb | |||
│ │ vn | |||
│ ├─side | |||
│ │ entries_3 | |||
│ │ s.csb | |||
│ │ vn | |||
│ └─vn | |||
│ │ vns | |||
│ │ zettai_zh-Hans.vns | |||
│ └─res | |||
│ │ wind.ogg | |||
│ └─zettai | |||
│ bg.jpg | |||
└─story2 | |||
ActSelect.csb | |||
ordering | |||
</pre> | |||
==main和side文件夹== | |||
在main和side文件夹下可以看见entries文件,vn文件和csb格式文件。 | |||
===entries文件=== | ===entries文件=== | ||
entries文件为每个剧情的信息, | 名字类似<code>entries_\d+</code>的文件记录了每个剧情的信息,例如 | ||
<syntaxhighlight lang="json"> | |||
{ | |||
"entries": [{ | |||
"minor": 1, | |||
"requiredPurchase": "zettai", | |||
"storyType": "nvl", | |||
"clearSongId": "antithese", | |||
"clearCharaId": -1, | |||
"charIcon1": 23, | |||
"charIcon2": -1, | |||
"storyCgPath": "app-data/story/cg/3-1.jpg", | |||
"icon": "entry_z" | |||
}, | |||
{ | |||
"minor": 2, | |||
"requiredPurchase": "zettai", | |||
"requiredMinor": 1, | |||
"storyType": "nvl", | |||
"clearSongId": "corruption", | |||
"clearCharaId": -1, | |||
"charIcon1": 23, | |||
"charIcon2": -1, | |||
"icon": "entry_z" | |||
}, | |||
{ | |||
"minor": 3, | |||
"requiredPurchase": "zettai", | |||
"requiredMinor": 2, | |||
"storyType": "vn", | |||
"storyData": "zettai", | |||
"clearSongId": "blackterritory", | |||
"clearCharaId": 23, | |||
"charIcon1": 23, | |||
"charIcon2": -1, | |||
"icon": "entry_cyaegha" | |||
}] | |||
} | |||
</syntaxhighlight> | |||
可以看到, 文件 遵循json协议,格式 为<code>{"entries": [{故事条目1},{故事条目2},…,{故事条目n}]}</code>。 每个 条目为一个简单对象,其schema为 | |||
<syntaxhighlight lang="json"> | |||
{ | |||
"type": "object", | |||
"required": [ "minor" ], | |||
"additionalProperties": false, | |||
"properties": { | |||
"minor": { "type": "integer" }, | |||
"storyType": { "type": "string", "enum": [ "nvl", "vn" ] }, | |||
"storyData": { "type": "string" }, | |||
"requiredPurchase": { "type": "string" }, | |||
"requiredMinor": { "type": "integer" }, | |||
"additionalRequires": { | |||
"type": "array", | |||
"items": { "type": "string" } | |||
}, | |||
"clearSongId": { "type": "string" }, | |||
"requirementAnomalyId": { "type": "string" }, | |||
"clearCharaId": { "type": "integer", "minimum": 0 }, | |||
"storyCgPath": { "type": "string" }, | |||
"hasAlternative": { "type": "boolean" }, | |||
"hiddenFromCount": { "type": "boolean" }, | |||
"bgmOverride": { "type": "string" }, | |||
"unlockedSongId": { "type": "string" }, | |||
"mapId": { "type": "string" }, | |||
"icon": { "type": "string" } | |||
}, | |||
"patternProperties": { | |||
"^charIcon[12]$": { | |||
"type": "integer", | |||
"enum": [ -1, { "type": "integer", "minimum": 0 } ] | |||
}, | |||
"^alternate(Pre|Suf)fix$": { "type": "string" } | |||
} | |||
} | |||
</syntaxhighlight> | |||
*minor:故事条目id | |||
*storyType:故事的展现模式为普通文本 剧情 (nvl)/视觉小说(vn) | |||
**bgmOverride:故事展现为nvl时,播放的bgm | |||
**storyCgPath:故事展现为nvl时,cg 的 文件名 | |||
**storyData:故事展现为vn时,所使用的视觉小说名称(无后缀) | |||
*requiredPurchase:须获取的曲包或单曲id,详见[[曲包 信息(packlist)格式]]。 | |||
*requiredMinor:须前置阅读的故事条目id | |||
**additionalRequires:有多个前置故事的写法,格式为<code>"additionalRequires": ["故事编号", "故事编号", …]</code> , 故事编号详见[[#vn文件]]。 | |||
*clearSongId:需要通过的曲目id | |||
*requirementAnomalyId:需要通过[[解歌系统#特殊解歌系统|异象]]的曲目id | |||
*unlockedSongId:需要 解锁 的曲目id | |||
*clearCharaId:需要采用的[[搭档]]数字id | |||
*mapId: 需 要完成的[[世界模式]]地图id | |||
*hiddenFromCount:故事解锁 前 不计入对应幕的总数 | |||
*hasAlternative:故事具有[[洞烛(至高:第八探索者)#搭档相 关|变化]]后的形式 | |||
*alternatePrefix、alternateSuffix:故事实际显示编号所采用的前后缀 | |||
*charIcon1、charIcon2:显示在剧情中的搭档头像对应的搭档数字id,-1为空 | |||
*icon:故事条目图标文件名(无后缀) | |||
===vn文件=== | |||
vn文件遵循json协议, 格式 为<code>{" 故事编号1": { 对应故事1}," 故事 编号2": {对应故事2},…}</code>,其中故事编号格式为<code>entriesId-minorId</code>,即对应[[#entries文件]]id与条目id为前后缀的拼合。对应故事的格式归纳为 | |||
<syntaxhighlight lang="json"> | |||
{ | |||
"故事编号": { | |||
"en": "English story", | |||
"ja": "日本語の物語", | |||
"ko": "한국어 이야기", | |||
"zh-Hans": "简体中文的故事", | |||
"zh-Hant": "正體中文的 故事" | |||
} | |||
} | |||
</syntaxhighlight> | |||
例如 | |||
<syntaxhighlight lang="json"> | |||
{ | |||
"102-7": { | |||
"en": "%%CG:app-data/story/cg/F-7-1.jpg%%|Paradise.\nAfter life, \"heaven\", the world of the dead.\nIt ...", | |||
"ja": "%%CG:app-data/story/cg/F-7-1.jpg%%| …… それは例えば、楽園(パラダイス)。\n死後の世界...", | |||
"ko": "%%CG:app-data/story/cg/F-7-1.jpg%%|낙원.\n사후 세계, “천국”, 망자의 나라. \n삶을 ...", | |||
"zh-Hans": "%%CG:app-data/story/cg/F-7-1.jpg%%|乐园——\n在生命结束后,那个名为“天堂”的世界。\n那是...", | |||
"zh-Hant": "%%CG:app-data/story/cg/F-7-1.jpg%%|樂園——\n在生命結束後,那個名為「天堂」的世界。\n那是..." | |||
} | |||
} | |||
</syntaxhighlight> | |||
需要注明的是: | |||
*<code>\n</code>为换行符 | |||
*<code>\"</code>转义文本中的英文双引号 | |||
*<code>|</code>为分页符 | |||
*<code>%%CG:%%</code>用于展示图片文件 | |||
*<code>%% %%{}</code>、<code>^^ ^^{}</code>用于展示字符注释(ruby) | |||
*<code>$e: $</code>用于展示特殊样式 | |||
==vn文件夹== | |||
vn文 件夹记录 视觉小说剧情 信息,包含vns文件、vns格式文件和res 文件夹。 | |||
===vns格式 文件=== | |||
vns 格式 文件为视觉小说 的 脚 本文 件, 文 件 名 格式为<code>视觉小说名_语言.vns</code>。vns文件 是 所有vns格式 文 件 的 清单。 | |||
=== | |||
一个vns格式 文件包含有 以 下 指令 | |||
<pre>say <content></pre> | |||
<pre>say_legacy <content></pre> | |||
*显示 剧情 内容 , 每次say会清空屏幕已有剧情 , 注意双引号同样 有 转义 , “$e: $”特殊样式同样会展现; 一 个推荐的匹配正则是<code>(?<=^say(_legacy)? ")([^\\"]|\\.|\n)*</code> | |||
<pre>play <audio> <volume> [loop]</pre> | |||
*播放音频(可以使用stop停止) | *播放音频(可以使用stop停止) | ||
*loop为循环至剧情结束,可选 | *loop为循环至剧情结束,可选 | ||
<pre>stop <audio> <duration></pre> | |||
*停止音频 | *停止音频 | ||
*duration为淡出时长 | *duration为淡出时长 | ||
<pre>volume <audio> <duration></pre> | |||
*调节音频音量 | |||
<pre>show <pic> <posX>:<posY> <anchorX>:<anchorY> <scaleX>:<scaleY> <transition> <superposition> [scale]</pre> | |||
*显示图片 | *显示图片 | ||
*pos为图片锚点在图片上的位置,图片左下角为(0,0),右上角为(1,1) | *pos为图片锚点在图片上的位置,图片左下角为<code>(0,0)</code> ,右上角为<code>(1,1)</code> | ||
*anchor为图片锚点在屏幕上的位置,屏幕左下角为(0,0),右上角为(1,1) | *anchor为图片锚点在屏幕上的位置,屏幕左下角为<code>(0,0)</code> ,右上角为<code>(1,1)</code> | ||
*scale为图片缩放比例,1为原始大小 | *scale为图片缩放比例,1为原始大小 | ||
**锚点为图片缩放过程中的原点 | **锚点为图片缩放过程中的原点 | ||
**不论设备分辨率如何,播放区域的比例在16:9和19.5:9之间,若屏幕比例在这之外会有黑边。原始大小指播放区域纵向分辨率为720时图片的大小 | **不论设备分辨率如何,播放区域的比例在16:9和19.5:9之间,若屏幕比例在这之外会有黑边。原始大小指播放区域纵向分辨率为720时图片的大小 | ||
*transition为渐变效果 | *transition为 淡入淡出 渐变效果 ,格式为<code>fade(<duration>,<curve>)</code> | ||
*superposition为叠加方式,出现过的有normal,overlay和overlayplus | *superposition为叠加方式,出现过的有normal,overlay和overlayplus | ||
*在结尾添加"scale",则原始大小变为播放区域横向分辨率为1280时图片的大小 | *在结尾添加"scale",则原始大小变为播放区域横向分辨率为1280时图片的大小 | ||
<pre>hide <pic> [transition]</pre> | |||
*隐藏图片 | *隐藏图片 | ||
*transition为渐变效果,可选 | *transition为渐变效果,可选 | ||
<pre>move <pic> <dx>:<dy> <duration> <curve></pre> | |||
*移动图片 | *移动图片 | ||
*dx:dy为移动的距离,单位为像素 | *dx:dy为移动的距离,单位为像素 | ||
*duration为移动时长 | *duration为移动时长 | ||
*curve为移动过程使用的曲线 | *curve为移动过程使用的曲线,支持linear、sineout、sineinout、cubicout,详见[https://easings.net easings 一览] | ||
<pre>scale <pic> <scaleX>:<scaleY> <duration> <curve></pre> | |||
*放缩图片 | |||
<pre>wait <duration></pre> | |||
*进行下面的操作的等待时间 | *进行下面的操作的等待时间 | ||
<pre>autoplay_legacy <duration></pre> | |||
<pre>auto <duration></pre> | |||
指令会依次执行,有下列特性 | 指令会依次执行,有下列特性 | ||
第171行: | 第212行: | ||
*移动可以叠加 | *移动可以叠加 | ||
*后显示的图片会在先显示的图片之上 | *后显示的图片会在先显示的图片之上 | ||
===res文件夹=== | |||
res文件夹下包含视觉小说所需的图像、音效资源,以及对这些资源的鸣谢。各子文件夹下为故事专用资源,子文件夹名取视觉小说名开头连续的字母。 | |||
==其他== | |||
===csb格式文件=== | |||
csb格式为cocos2dx的UI布局文件,定义了对应分支的界面布局,比较难以编辑,详见[https://google.github.io/flatbuffers/ FlatBuffer] | |||
不过在某些情况下,可以通过[https://github.com/lyzz0612/csb2csd csb2csd]将csb文件转换为csd文件后使用旧版Cocos Studio打开csd文件便可编辑(发布时可发布为csb格式) | |||
===paths文件=== | |||
决定story界面图像排布(已废弃?) | |||
===ordering文件=== | |||
决定story界面图像排布、剧透警告等 | |||
{{Usercheck-end}} | {{Usercheck-end}} |
2024年12月6日 (五) 23:31的最新版本
此页面为技术型隐藏页面 |
已确认。
- 这只是对Story文件的格式分析,并不表示任何人可以曲解剧情。
- 本页面将保持孤立状态,严禁链入/引用!
在apk包中,所有关于剧情的部分都在/assets/app-data中,目录树如下(仅示意,有省略)。
app-data ├─story │ │ paths │ ├─cg │ │ 1-1.jpg │ ├─main │ │ entries_1 │ │ u.csb │ │ vn │ ├─side │ │ entries_3 │ │ s.csb │ │ vn │ └─vn │ │ vns │ │ zettai_zh-Hans.vns │ └─res │ │ wind.ogg │ └─zettai │ bg.jpg └─story2 ActSelect.csb ordering
main和side文件夹
在main和side文件夹下可以看见entries文件,vn文件和csb格式文件。
entries文件
名字类似entries_\d+
的文件记录了每个剧情的信息,例如
{
"entries": [{
"minor": 1,
"requiredPurchase": "zettai",
"storyType": "nvl",
"clearSongId": "antithese",
"clearCharaId": -1,
"charIcon1": 23,
"charIcon2": -1,
"storyCgPath": "app-data/story/cg/3-1.jpg",
"icon": "entry_z"
},
{
"minor": 2,
"requiredPurchase": "zettai",
"requiredMinor": 1,
"storyType": "nvl",
"clearSongId": "corruption",
"clearCharaId": -1,
"charIcon1": 23,
"charIcon2": -1,
"icon": "entry_z"
},
{
"minor": 3,
"requiredPurchase": "zettai",
"requiredMinor": 2,
"storyType": "vn",
"storyData": "zettai",
"clearSongId": "blackterritory",
"clearCharaId": 23,
"charIcon1": 23,
"charIcon2": -1,
"icon": "entry_cyaegha"
}]
}
可以看到,文件遵循json协议,格式为{"entries": [{故事条目1},{故事条目2},…,{故事条目n}]}
。每个条目为一个简单对象,其schema为
{
"type": "object",
"required": [ "minor" ],
"additionalProperties": false,
"properties": {
"minor": { "type": "integer" },
"storyType": { "type": "string", "enum": [ "nvl", "vn" ] },
"storyData": { "type": "string" },
"requiredPurchase": { "type": "string" },
"requiredMinor": { "type": "integer" },
"additionalRequires": {
"type": "array",
"items": { "type": "string" }
},
"clearSongId": { "type": "string" },
"requirementAnomalyId": { "type": "string" },
"clearCharaId": { "type": "integer", "minimum": 0 },
"storyCgPath": { "type": "string" },
"hasAlternative": { "type": "boolean" },
"hiddenFromCount": { "type": "boolean" },
"bgmOverride": { "type": "string" },
"unlockedSongId": { "type": "string" },
"mapId": { "type": "string" },
"icon": { "type": "string" }
},
"patternProperties": {
"^charIcon[12]$": {
"type": "integer",
"enum": [ -1, { "type": "integer", "minimum": 0 } ]
},
"^alternate(Pre|Suf)fix$": { "type": "string" }
}
}
- minor:故事条目id
- storyType:故事的展现模式为普通文本剧情(nvl)/视觉小说(vn)
- bgmOverride:故事展现为nvl时,播放的bgm
- storyCgPath:故事展现为nvl时,cg的文件名
- storyData:故事展现为vn时,所使用的视觉小说名称(无后缀)
- requiredPurchase:须获取的曲包或单曲id,详见曲包信息(packlist)格式。
- requiredMinor:须前置阅读的故事条目id
- additionalRequires:有多个前置故事的写法,格式为
"additionalRequires": ["故事编号", "故事编号", …]
,故事编号详见#vn文件。
- additionalRequires:有多个前置故事的写法,格式为
- clearSongId:需要通过的曲目id
- requirementAnomalyId:需要通过异象的曲目id
- unlockedSongId:需要解锁的曲目id
- clearCharaId:需要采用的搭档数字id
- mapId:需要完成的世界模式地图id
- hiddenFromCount:故事解锁前不计入对应幕的总数
- hasAlternative:故事具有变化后的形式
- alternatePrefix、alternateSuffix:故事实际显示编号所采用的前后缀
- charIcon1、charIcon2:显示在剧情中的搭档头像对应的搭档数字id,-1为空
- icon:故事条目图标文件名(无后缀)
vn文件
vn文件遵循json协议,格式为{"故事编号1": {对应故事1},"故事编号2": {对应故事2},…}
,其中故事编号格式为entriesId-minorId
,即对应#entries文件id与条目id为前后缀的拼合。对应故事的格式归纳为
{
"故事编号": {
"en": "English story",
"ja": "日本語の物語",
"ko": "한국어 이야기",
"zh-Hans": "简体中文的故事",
"zh-Hant": "正體中文的故事"
}
}
例如
{
"102-7": {
"en": "%%CG:app-data/story/cg/F-7-1.jpg%%|Paradise.\nAfter life, \"heaven\", the world of the dead.\nIt ...",
"ja": "%%CG:app-data/story/cg/F-7-1.jpg%%|……それは例えば、楽園(パラダイス)。\n死後の世界...",
"ko": "%%CG:app-data/story/cg/F-7-1.jpg%%|낙원.\n사후 세계, “천국”, 망자의 나라. \n삶을 ...",
"zh-Hans": "%%CG:app-data/story/cg/F-7-1.jpg%%|乐园——\n在生命结束后,那个名为“天堂”的世界。\n那是...",
"zh-Hant": "%%CG:app-data/story/cg/F-7-1.jpg%%|樂園——\n在生命結束後,那個名為「天堂」的世界。\n那是..."
}
}
需要注明的是:
\n
为换行符\"
转义文本中的英文双引号|
为分页符%%CG:%%
用于展示图片文件%% %%{}
、^^ ^^{}
用于展示字符注释(ruby)$e: $
用于展示特殊样式
vn文件夹
vn文件夹记录视觉小说剧情信息,包含vns文件、vns格式文件和res文件夹。
vns格式文件
vns格式文件为视觉小说的脚本文件,文件名格式为视觉小说名_语言.vns
。vns文件是所有vns格式文件的清单。
一个vns格式文件包含有以下指令
say <content>
say_legacy <content>
- 显示剧情内容,每次say会清空屏幕已有剧情,注意双引号同样有转义,“$e: $”特殊样式同样会展现;一个推荐的匹配正则是
(?<=^say(_legacy)? ")([^\\"]|\\.|\n)*
play <audio> <volume> [loop]
- 播放音频(可以使用stop停止)
- loop为循环至剧情结束,可选
stop <audio> <duration>
- 停止音频
- duration为淡出时长
volume <audio> <duration>
- 调节音频音量
show <pic> <posX>:<posY> <anchorX>:<anchorY> <scaleX>:<scaleY> <transition> <superposition> [scale]
- 显示图片
- pos为图片锚点在图片上的位置,图片左下角为
(0,0)
,右上角为(1,1)
- anchor为图片锚点在屏幕上的位置,屏幕左下角为
(0,0)
,右上角为(1,1)
- scale为图片缩放比例,1为原始大小
- 锚点为图片缩放过程中的原点
- 不论设备分辨率如何,播放区域的比例在16:9和19.5:9之间,若屏幕比例在这之外会有黑边。原始大小指播放区域纵向分辨率为720时图片的大小
- transition为淡入淡出渐变效果,格式为
fade(<duration>,<curve>)
- superposition为叠加方式,出现过的有normal,overlay和overlayplus
- 在结尾添加"scale",则原始大小变为播放区域横向分辨率为1280时图片的大小
hide <pic> [transition]
- 隐藏图片
- transition为渐变效果,可选
move <pic> <dx>:<dy> <duration> <curve>
- 移动图片
- dx:dy为移动的距离,单位为像素
- duration为移动时长
- curve为移动过程使用的曲线,支持linear、sineout、sineinout、cubicout,详见easings 一览
scale <pic> <scaleX>:<scaleY> <duration> <curve>
- 放缩图片
wait <duration>
- 进行下面的操作的等待时间
autoplay_legacy <duration>
auto <duration>
指令会依次执行,有下列特性
- show在内容完全显示并点击后结束执行
- wait只会在特定时间后结束执行
- 其它指令都是“开始”某种画面变化,执行不占用时间,所以一串指令会同时执行
- 移动可以叠加
- 后显示的图片会在先显示的图片之上
res文件夹
res文件夹下包含视觉小说所需的图像、音效资源,以及对这些资源的鸣谢。各子文件夹下为故事专用资源,子文件夹名取视觉小说名开头连续的字母。
其他
csb格式文件
csb格式为cocos2dx的UI布局文件,定义了对应分支的界面布局,比较难以编辑,详见FlatBuffer
不过在某些情况下,可以通过csb2csd将csb文件转换为csd文件后使用旧版Cocos Studio打开csd文件便可编辑(发布时可发布为csb格式)
paths文件
决定story界面图像排布(已废弃?)
ordering文件
决定story界面图像排布、剧透警告等