进来了啥都别说,先一起喊:
Ciallo~ (∠・ω< )⌒★
欢迎来到我们的MC 旮旯给木(Galgame) 项目,本项目旨在通过简单的方式创建类似Galgame的对话框
- 运作方式:原版游戏,数据包
- 支持版本:>=1.21.5
- 支持多人并行使用
Ⅰ 环境准备
将数据包放在存档的datapacks文件夹下,为了方便后续编辑和调整,我们需要将压缩的数据包解压,然后使用Visual Studio Code打开数据包文件夹。
展开到demo文件夹里的foo.mcfunction,之后的演示和操作都建议在这个文件中进行
假设现在foo.mcfunction中有以下命令,现在看不懂没关系,只是做一个演示
data modify storage minecraft:skapi.dbox reg.event.foo set value {\
"main":[\
{"type":"tachie_load","character":"yoshino","tachie":"ciallo"},\
{"type":"jump","jump":"1"}\
],\
"1":[\
{"type":"text","text":"Ciallo~(∠・ω< )⌒ ★","character":"yoshino"},\
{"type":"tachie_animation","character":"yoshino","animation":"jump"},\
{"type":"sound","sound":"skdbox.demo2.ciallo"},\
{"type":"option","option":[{"text":"再来一次!","jump":"1"},{"text":"0721","jump":"2"}]}\
],\
"2":[\
{"type":"stop"}\
]\
}
不难注意到这条命令每行都以\
结尾,这并不利于我们编辑,一种解决方法是先将内容写在Json文件中,然后查找替换,给行末批量加上\
在开始之前,可以先体验数据包内置的一些示例程序,请在聊天栏运行以下命令
/function skdbox:dialog {id:"demo1"}
/function skdbox:dialog {id:"demo2"}
Ⅱ 事件列表
显示文字,显示立绘,播放语音等操作统称为事件,事件存放在事件列表中
基本
定义事件列表
data modify storage minecraft:skapi.dbox reg.event.<事件列表ID> set value <事件列表>
参数说明
<事件列表ID> 事件列表的ID,这是唯一的
<事件列表> 一个包含所有事件的复合标签,格式如下
播放事件列表
function skdbox:dialog
执行者 指定要向谁播放事件列表,只能指定单一玩家
参数
终止事件列表
事件列表可以在播放完毕时自动终止,当然你也可以直接使用该函数终止
function skdbox:stop
文本事件
文本事件,顾名思义,就是在玩家屏幕上显示文本,一条文本显示完毕后,将等待玩家按下空格键,然后显示下一条文本
现在来看一个演示,把下面的命令放到foo.mcfunction
中,然后重载数据包
关于"character":"roka"
,为了方便演示,数据包已经定义好了一些角色,详见定义角色
data modify storage minecraft:skapi.dbox reg.event.foo set value {\
"main":[\
{"type":"text","character":"NULL","text":"「嘛......要说也的确是好久没来了」"},\
{"type":"text","character":"roka","text":"「这就直接去志那都庄了?」"},\
{"type":"text","character":"NULL","text":"「是这个打算」"},\
{"type":"text","character":"roka","text":"「那我也一起走比较好吧,因为好久不见,再聊会吧」"},\
]\
}
在聊天栏中输入,就可以运行了
/function skdbox:dialog {id:"foo"}
在后面的演示中,为了方便修改编辑,将会直接以json的形式展示事件列表复合标签,像这样
{
"main":[
{"type":"text","character":"NULL","text":"「嘛......要说也的确是好久没来了」"},
{"type":"text","character":"roka","text":"「这就直接去志那都庄了?」"},
{"type":"text","character":"NULL","text":"「是这个打算」"},
{"type":"text","character":"roka","text":"「那我也一起走比较好吧,因为好久不见,再聊会吧」"},
]
}
延时事件
得益于时序控制,我们可以完成很多有用的操作
如果事件列表中只有文本事件,那它的时序是这样的
文本打字动画占用时间取决于文本的长度
{
"main":[
{"type":"text","character":"NULL","text":"第一句话"},
{"type":"text","character":"roka","text":"第二句话"},
]
}
gantt
axisFormat %S
dateFormat ss
section tick
加载文本事件 :active, after des1, 1s
打字动画 :4s
等待玩家按下空格 :3s
加载文本事件 :active, 1s
打字动画 :4s
等待玩家按下空格 :3s
如果在文本事件后添加了其他事件,那么这些事件将与文本事件同tick执行
{
"main":[
{"type":"text","character":"NULL","text":"第一句话"},
{"type":"tachie_load","character":"roka","tachie":"1"},
{"type":"text","character":"roka","text":"第二句话"},
{"type":"tachie_modify","character":"roka","tachie":"2"},
{"type":"tachie_animation","animation":"jump"}
]
}
gantt
axisFormat %S
dateFormat ss
section tick
加载text :active, a, after 0, 1s
打字动画 :b, after a ,4s
加载tachie_load :active, after 0, 1s
等待玩家按下空格 :c, after b,3s
加载文本事件 :active, d, after c,1s
打字动画 :e, after d,6s
加载tachie_modify :active, f, after c,1s
加载tachie_animation :active, g, after c,1s
播放立绘动画 :after g,3s
等待玩家按下空格 :after e,3s
不难发现tachie_modify事件和tachie_animation事件是同时执行的,但是我们希望执行完tachie_modify事件后延迟一段时间再执行tachie_animation事件
这种情况下需要使用延迟事件,格式如下
{
"main":[
{"type":"text","character":"NULL","text":"第一句话"},
{"type":"tachie_load","character":"roka","tachie":"1"},
{"type":"text","character":"roka","text":"第二句话"},
{"type":"tachie_modify","character":"roka","tachie":"2"},
{"type":"delay","time":2},
{"type":"tachie_animation","animation":"jump"}
]
}
gantt
axisFormat %S
dateFormat ss
section tick
加载text :active, a, after 0, 1s
打字动画 :b, after a ,4s
加载tachie_load :active, after 0, 1s
等待玩家按下空格 :c, after b,3s
加载文本事件 :active, d, after c,1s
打字动画 :e, after d,6s
加载tachie_modify :active, f, after c,1s
delay :active, h, after c,2s
加载tachie_animation :active, g, after h,1s
播放立绘动画 :after g,3s
等待玩家按下空格 :after e,3s
立绘事件
只有文本还是太单调了,立绘是必不可少的
立绘事件使用角色ID作为索引,所以不能在同一时间多次加载同一角色的立绘
关于角色定义,详见**定义角色**
加载立绘 tachie_load
修改立绘 tachie_modify
移除立绘 tachie_remove
关于参数position,可以参考这张图
关于参数color,用处是让立绘的色调更贴合环境,如图所示
流程控制事件
终止事件
顾名思义,终止事件可以让事件列表提前结束
{
"main":[
{"type":"text","character":"NULL","text":"这是一句话"},
{"type":"stop"},
{"type":"text","character":"roka","text":"事件列表提前终止了,这句话将不会显示"}
]
}
跳转事件
{
"main":[
{"type":"text","character":"NULL","text":"这是一句话"},
{"type":"jump","jump":"jmp01"}
],
"jmp01":[
{"type":"text","character":"roka","text":"使用jump事件可以跳转到这里"}
]
}
检查条件
检查一个条件,如果条件通过则跳转到指定的子事件列表,条件不通过则继续执行当前的子事件列表
用**@a[tag=skdbox.s]
**指代玩家
检查分数
检查分数,如果分数在指定区间内,则跳转到指定的子事件列表,如果分数不在任意一个指定的区间内, 则继续执行当前的子事件列表
用**@a[tag=skdbox.s]**指代玩家
执行命令
执行一条命令,命令的默认执行者为Marker,用**@a[tag=skdbox.s]**指代当前玩家
选项事件
选项是Galgame必不可少的元素之一,不同的选项往往决定不同的剧情走向
玩家按W(前进键)和S(后退键)来切换选项,按空格键确认选项
选项的数量没有限制,但如果同时显示很多选项,会造成选项显示重叠,这时候需要调整选项间距,详见**全局设置 选项**
当然关于选项,自然少不了各种各样的冥场面
原来是两句充满童趣的日文呀,不知道的还以为是选择里面外面呢
下面我们来看一个示例,播放Ciallo,玩家可以选择再来一次或者结束
flowchart TD
A(开始) --> B[加载立绘]
B --> C[Ciallo~(∠・ω< )⌒ ★]
C --> D[立绘动画jump]
D --> E[播放声音]
E --> F{显示选项}
F --> |再来一次| B
F --> |0721| G(结束)
{
"main":[
{"type":"tachie_load","character":"yoshino","tachie":"ciallo"},
{"type":"jump","jump":"1"}
],
"1":[
{"type":"text","text":"Ciallo~(∠・ω< )⌒ ★","character":"yoshino"},
{"type":"tachie_animation","character":"yoshino","animation":"jump"},
{"type":"sound","sound":"skdbox.demo2.ciallo"},
{"type":"option","option":[
{"text":"再来一次!","jump":"1"},
{"text":"0721","jump":"2"}
]}
],
"2":[
{"type":"stop"}
]
}
声音事件
播放声音
向当前玩家播放一个声音
语音事件
如果你想给每一句话都添加语音,使用播放声音事件处理起来很麻烦,这时就需要用到语音事件
只需要使用一次语音事件,就可以为每一句对话自动播放对应的语音
来看一个例子:
{
"main":[
{"type":"voice","path":"minecraft:skdbox.demo1.","index":1},
{"type":"text","character":"roka","text":"「这就直接去志那都庄了?」"},
{"type":"text","character":"NULL","text":"「是这个打算」"},
{"type":"text","character":"roka","text":"「那我也一起走比较好吧,因为好久不见,再聊会吧」"},
{"type":"text","character":"NULL","text":"「我当然可以,不过芦花姐,时间不紧吗?」"},
{"type":"text","character":"roka","text":"「没事没事,那走吧」"},
{"type":"text","character":"roka","text":"「......总觉得阿将啊,从刚才开始就好像在盯着我?」"},
{"type":"text","character":"roka","text":"「被这么盯着,姐姐可是要害羞了呢。怎么怎么?莫非是迷上我了?」"},
{"type":"text","character":"roka","text":"「还是说,脸上粘了什么东西吗?」"}
],
}
执行上面的事件列表,语音将会按照这样的顺序播放
可见语音自动跳过了名字为"NULL"的角色
语音 | 文本 |
---|---|
minecraft:skdbox.demo1.1 | 「这就直接去志那都庄了?」 |
「是这个打算」 | |
minecraft:skdbox.demo1.2 | 「那我也一起走比较好吧,因为好久不见,再聊会吧」 |
「我当然可以,不过芦花姐,时间不紧吗?」 | |
minecraft:skdbox.demo1.3 | 「没事没事,那走吧」 |
minecraft:skdbox.demo1.4 | 「......总觉得阿将啊,从刚才开始就好像在盯着我?」 |
minecraft:skdbox.demo1.5 | 「被这么盯着,姐姐可是要害羞了呢。怎么怎么?莫非是迷上我了?」 |
minecraft:skdbox.demo1.6 | 「还是说,脸上粘了什么东西吗?」 |
Ⅲ 添加新内容
定义角色
角色信息定义在function/cfg/character.mcfunction中,如图所示
你会发现这个文件里大部分都是\uXXXX转义字符,知道你很急但是你先别急,关于这些字符的用处,会在**导入立绘**这一节讲解
定义角色
data modify storage minecraft:skapi.dbox reg.character.<角色ID> set value <角色信息>
参数说明
<角色ID> 角色ID,这是唯一的
<角色信息> 一个包含该角色所有信息的复合标签,格式如下
来看一个示例:定义角色丛雨
{
display_name:"丛雨",
description:"Murasame"
tachie:{
}
}
导入立绘
刚才我们已经将丛雨的基本信息定义好了,接下来需要导入立绘
首先需要准备一些透明背景的立绘图片,当然这些是没法直接用的,我们需要对其进行进一步处理
具体处理方式为
- 宽度为256像素的整数倍
- 高度为2048像素
调整好大小之后,还需要全选图像,填充一个不透明度为1%的白色,如图所示,这样可以避免图像出现空像素从而造成文本错位,游戏渲染文本会丢掉透明度小于24(透明度0~255)的片段,所以在游戏里是看不见这一层白色的
将处理好的立绘放在资源包assets/minecraft/textures/font/skdbox/murasame路径下,当然,/skdbox/murasame可以改成自己喜欢的位置,文件名用数字命名,从1开始,如图所示
打开数据包文件夹中的encode.py程序,这个程序可以为图像上每一个256*256的区域分配一个码位
然后需要填写一些信息,根据实际情况填写就行了
- 起始码位先填上 0x60220,实际这里需要填什么下面会讲
- 导入的图片数量根据实际情况填写。这里一共需要导入26张立绘
- 处理好的立绘图片大小为1280*2048,可以分成5*8个256*256的区域,所以分割列数填5,分割行数填8
程序会生成一个output.txt文件,这个文件的内容分为三部分
第一部分作用是声明图像上每一个256*256的区域对应的码位,需要把这些内容添加到资源包assets/font/default.json中,刚才我们把立绘放到了assets/minecraft/textures/font/skdbox/murasame,所以在这里,要把文件中的font/skdbox/都替换成font/skdbox/murasame/
这部分的内容长这样
然后是第二部分,每行表示一张立绘,你可以直接在游戏内打印这些字符串来显示立绘图片
这部分的内容长这样
现在只需要将这些字符串放到角色的tachie标签下就可以了,这里演示只加了两行,实际应该把第二部分的字符串都加上
立绘ID可以是任意字符串,这里演示使用的数字
{
display_name:"丛雨",
description:"Murasame"
tachie:{
"1":"\ud940\ude20\ud8fb\udd41\ud940\ude21\ud8fb\udd41\ud940\ude22\ud8fb\udd41\ud940\ude23\ud8fb\udd41\ud940\ude24\ud8fb\udd41\n\n\ud940\ude25\ud8fb\udd41\ud940\ude26\ud8fb\udd41\ud940\ude27\ud8fb\udd41\ud940\ude28\ud8fb\udd41\ud940\ude29\ud8fb\udd41\n\n\ud940\ude2a\ud8fb\udd41\ud940\ude2b\ud8fb\udd41\ud940\ude2c\ud8fb\udd41\ud940\ude2d\ud8fb\udd41\ud940\ude2e\ud8fb\udd41\n\n\ud940\ude2f\ud8fb\udd41\ud940\ude30\ud8fb\udd41\ud940\ude31\ud8fb\udd41\ud940\ude32\ud8fb\udd41\ud940\ude33\ud8fb\udd41\n\n\ud940\ude34\ud8fb\udd41\ud940\ude35\ud8fb\udd41\ud940\ude36\ud8fb\udd41\ud940\ude37\ud8fb\udd41\ud940\ude38\ud8fb\udd41\n\n\ud940\ude39\ud8fb\udd41\ud940\ude3a\ud8fb\udd41\ud940\ude3b\ud8fb\udd41\ud940\ude3c\ud8fb\udd41\ud940\ude3d\ud8fb\udd41\n\n\ud940\ude3e\ud8fb\udd41\ud940\ude3f\ud8fb\udd41\ud940\ude40\ud8fb\udd41\ud940\ude41\ud8fb\udd41\ud940\ude42\ud8fb\udd41\n\n\ud940\ude43\ud8fb\udd41\ud940\ude44\ud8fb\udd41\ud940\ude45\ud8fb\udd41\ud940\ude46\ud8fb\udd41\ud940\ude47\ud8fb\udd41",
"2":"\ud940\ude48\ud8fb\udd41\ud940\ude49\ud8fb\udd41\ud940\ude4a\ud8fb\udd41\ud940\ude4b\ud8fb\udd41\ud940\ude4c\ud8fb\udd41\n\n\ud940\ude4d\ud8fb\udd41\ud940\ude4e\ud8fb\udd41\ud940\ude4f\ud8fb\udd41\ud940\ude50\ud8fb\udd41\ud940\ude51\ud8fb\udd41\n\n\ud940\ude52\ud8fb\udd41\ud940\ude53\ud8fb\udd41\ud940\ude54\ud8fb\udd41\ud940\ude55\ud8fb\udd41\ud940\ude56\ud8fb\udd41\n\n\ud940\ude57\ud8fb\udd41\ud940\ude58\ud8fb\udd41\ud940\ude59\ud8fb\udd41\ud940\ude5a\ud8fb\udd41\ud940\ude5b\ud8fb\udd41\n\n\ud940\ude5c\ud8fb\udd41\ud940\ude5d\ud8fb\udd41\ud940\ude5e\ud8fb\udd41\ud940\ude5f\ud8fb\udd41\ud940\ude60\ud8fb\udd41\n\n\ud940\ude61\ud8fb\udd41\ud940\ude62\ud8fb\udd41\ud940\ude63\ud8fb\udd41\ud940\ude64\ud8fb\udd41\ud940\ude65\ud8fb\udd41\n\n\ud940\ude66\ud8fb\udd41\ud940\ude67\ud8fb\udd41\ud940\ude68\ud8fb\udd41\ud940\ude69\ud8fb\udd41\ud940\ude6a\ud8fb\udd41\n\n\ud940\ude6b\ud8fb\udd41\ud940\ude6c\ud8fb\udd41\ud940\ude6d\ud8fb\udd41\ud940\ude6e\ud8fb\udd41\ud940\ude6f\ud8fb\udd41",
}
}
至此,已经完成了角色定义
output.txt的第三部分只有一行,就像这样: 但是如果我忘记了或者根本不知道上一次的编码从哪里结束怎么办?
很简单,如图所示,找到文件assets/font/default.json中最后一个编码,只需要将这个编码加一,就可以当作下一次的起始码位 值得注意的是,default.json中使用的是unicode代理对,这里我们需要将字节字符转为宽字符,听不懂没关系,看操作
比方说我们要转换这个字符
\ud941\ude2f
点击打开这个网站
按照下图的方式解码一次,然后再编码一次,得到6062f,加上1得到60630,下次编码就可以使用0x60630作为起始码位了
定义动画
动画信息定义在function/cfg/animation.mcfunction中
定义动画
data modify storage minecraft:skapi.dbox reg.animation.<动画ID> set value <动画信息>
参数说明
<动画ID> 动画ID,这是唯一的
<动画信息> 一个包含该动画所有信息的列表,格式如下
值得注意的是,translation列表中得坐标是相对于当前坐标的,而不是相对于原点坐标
以下是动画"jump"的定义
[
{
merge:{
transformation:{
translation:[0f,0.0055f,0f]
},
interpolation_duration:5
}
},
{
delay:5,
merge:{
transformation:{
translation:[0f,-0.0055f,0f]
},
interpolation_duration:5
}
}
]
Ⅳ 全局设置
全局设置定义在function/cfg/config.mcfunction中
需要更改请直接到文件中修改
但是并不是所有的设置都建议更改,后面会讲几个常用的设置
对话框
比较有用的主要是修改文本的拼接格式
默认是这样的,代表先显示一个“【”,然后放入角色名字,再显示一个"】",最后将文本颜色改为黄色,显示角色描述
format:[
{text:"【"},
{place:{type:"display_name"}},
{text:"】 \u00A7e"},
{place:{type:"description"}},
{text:"\u00A7r\n"}
]
立绘
修改或添加位置/颜色枚举,或者更改默认动画,以下是默认设置
tachie: {
position: {
"LEFT":0.01,
"M_LEFT":0.005,
"CENTER":0,
"M_RIGHT":-0.005,
"RIGHT":-0.01
},
color: {
default:"#FFFFFF",
midnight:"#8C9ACC",
noon:"#F0DEAD"
},
default_animation: {
load:"fade_in_left",
remove:"fade_out_left"
}
}
选项
如果有一次性显示很多选项的需求,请修改总行间距以避免造成选项重叠
声音
可以换成自己喜欢的音效,以下是默认设置
sound: {
option:"minecraft:ui.button.click",
text:"minecraft:ui.button.click"
},