- UID
- 350279
- 阅读权限
- 100
- 注册时间
- 2020-2-19
- 最后登录
- 2024-9-29
- 在线时间
- 583 小时
- 性别
- 保密
征服者
- 耕战
- 5612
- 鹰币
- 12990
- 天龙币
- 0
- 回帖
- 80
|
本帖最后由 艾约bright 于 2022-6-4 00:31 编辑
战役制作中一直缺少跨关卡联动的功能,如果说玩家上一关的选择能够影响到下一关的发展,那么能极大地丰富战役的可玩性。现在,决定版中利用xs脚本的读写文件功能,能够间接地实现这一想法。
要实现跨关卡联动,就必然要有一个文件用来记录之前关卡的状态。首先给出遗朝网站上对xs脚本读写文件功能的介绍,参考:.xs scripting in Age of Empires II: Definitive Edition - Forgotten Empires
所以xsCreateFile这个函数是用来创建文件的,xsOpenFile这个函数是用来打开并读取文件内容的,xsWriteString、xsWriteInt等一系列函数是向文件写值的,文件后缀名是.xsdat。下面对这几个函数进行一些解释,因为它们和常见编程语言中的用法存在差异:
- xsCreateFile:创建文件的函数无法指定路径和文件名称,xs脚本会自动将文件创建在固定位置。根据场景的不同,创建位置也有差别。如果是随机地图或者在地编里运行,则会创建在C:\Users\用户名\Games\Age of Empires 2 DE\STEAMID\profile这个文件夹下,并且文件名等于场景名;如果打包成了战役,则会创建在resources/_common/campaign文件夹下,并且文件名等于.aoe2campaign文件的名称。而且后者还可再细分为几种情形,如果是一个单独的.aoe2campaign文件,没有打包成模组形式,那么生成的.xsdat文件在C:\Users\用户名\Games\Age of Empires 2 DE\STEAMID\resources\common\campaign文件夹下,否则会在mods/local或者mods/subscribed对应的路径下。除此之外,这个函数有一个参数append,是用来控制写入模式的,如果设为true就会接着已有内容写,如果设为false就会创建一个新文件
- xsOpenFile:打开文件后默认为只读模式,是无法写入的。也就是说,如果要写入文件就必须先xsCreateFile。这个函数的参数为文件名(不含.xsdat后缀),但这个文件名其实隐含了一个路径,如果你特别指定,那它就会去C:\Users\用户名\Games\Age of Empires 2 DE\STEAMID\profile下面找
- xsWriteString:这一系列的函数就是用来写入值的,注意必须xsCreateFile后才能写入,xsOpenFile是不行的
介绍完了这几个函数后,我们开始构建一个简单的应用场景。比如说我们的战役有两关,第一关中设置了一个成就任务,如果完成了,第二关会给一个额外的奖励。那么我们需要在第一关结束时进行结算,如果完成了任务就写入一个值(比如1),否则就写入默认值(比如0),然后在第二关读取这个文件,判断玩家是否完成了成就任务。
这里最需要注意的是文件路径问题,我们需要使用相对路径来为xsOpenFile函数指定读取文件的位置,否则它就跑去profile下面找了。如上所述,当在一个打包的模组战役里运行的时候,生成的.xsdat文件是在mods/local或subscribed/模组名/resources/_common/campaign下的,那么根据profile文件的位置,我们可以写出以下的相对路径:../mods/local或subscribed/模组名/resources/_common/campaign/.xsdat文件名。这里需要注意的是,subscribed里的模组文件夹前面有个数字编号,需要预先上传一下模组,获取这个编号后再来定义路径。
我们定义三个函数,分别执行状态的初始化、更新和读取:
void initValue()
{
xsCreateFile(false);
xsWriteInt(0);
xsCloseFile();
}
void changeValue()
{
xsCreateFile(false);
xsWriteInt(1);
xsCloseFile();
xsChatData("Value has been written");
}
void checkValue()
{
bool flag = xsOpenFile("../mods/local/xs_test/resources/_common/campaign/xs_test");
if (flag == false) {
xsOpenFile("../mods/subscribed/72551_xs_test/resources/_common/campaign/xs_test");
}
int val = xsReadInt();
xsCloseFile();
if(val == 1){
xsChatData("Read your record!");
}
else{
xsChatData("No record!");
}
}
在第一个场景中,我们首先调用initValue函数来初始化val,如果达成了任务,就调用changeValue函数将val修改为1;在第二个场景中,我们调用checkValue函数,来读取并检测val的值,判断玩家是否完成了任务。这里考虑到玩家可能使用本地模组或者订阅模组,我们写了两个路径来判断(不考虑玩家下载下来后又自己改名等特殊情况)。
大家可以在模组中订阅“xs_test”这个测试样例,来体验一下此功能。在第一个场景中等待20秒,即视为完成了成就任务,此时进入第二关,左上角会提示“Read your record!”;如果没有完成任务,第二关的左上角则会提示“No record!”;重进第一关则会刷新状态。
以上是一个简单例子的展示。在实际应用中,大家可以在文件中读写更加复杂的数据。例如,文件中定义6个数字分别作为6个场景的状态码,然后根据实际情况进行初始化、更新和读取,即可实现多关卡联动。可以用来实现库曼5进阶版的多线剧情,你在第一关选择了帮助匈牙利,那么后续关卡也是匈牙利作为你的盟友等等。
补充介绍一下关于file offset相关的操作:
在较复杂的场景设置中,可能需要用到多个数字来记录状态。例如,第一关有两个成就任务,在第二关中分别对应两个奖励;除此之外我们还想将第二关的剩余资源继承到第三关。这个时候,按照一般思路,我们首先需要2个int来记录是否完成了第一关的两个成就任务,然后还需要4个float来记录4种资源。但是这里存在一个问题,第三关的时候我们并不需要读取第一关成就任务的完成情况,只要读第二关的剩余资源就可以了。此时就需要设置文件读取时的offset,来控制从特定位置开始读取了。
首先说明一下,文件的offset是从0开始计算的,每个int和float都是4个字节,所以说每读了一个int或者float,offset都会+4(你调用read系列函数的时候,读完会自动加)。主要有三个函数:
- xsGetFilePosition():获取当前读取位置的offset
- xsSetFilePosition(int n):设置当前读取位置的offset,这个是绝对值,比如第一个int占据0-3字节,你设成4,就从第二个数字开始读了
- xsOffsetFilePosition(int n):和上面函数的区别是,它是将读取位置的offset向前移动n个字节,所以是一个相对值
那么我们的做法如下所述:首先涉及到多关卡多状态联动的情境,在写入文件的时候我推荐是先把里面的值全部读出来,然后修改相应字节位数的值,再重新创建文件写入。因为它默认的追加写入模式会写在最后,你没法控制,重新创建再写是最方便的。所以我建议大家首先列一个要用到的状态序列,比如这里是X=(int, int, float, float, float, float),开始时进行一次全0初始化,这样每一位是对应的什么含义就很清楚了。在第二关结束时我们将第二关剩余资源写入,那么过程就是首先把X的值都读出来,然后保持前两个int不变,修改后面4个float值(分别填入肉木金石的资源),再重新创建文件并写入。在第三关开场我们打开文件后,利用xsSetFilePosition或者xsOffsetFilePosition函数,将读取的位置移动到8,即跳过0-3和4-7这两个共占据8字节的int。这样我们连续执行4次xsReadFloat,读取到的就是刚才存储的4种资源余量了,于是我们就可以利用这些值来修改资源达到效果。
简单概括一下,file offset的相关操作可以让你从任意位置开始读取写入的变量,这样你就可以控制和划分不同关卡、不同状态的变量范围,从而实现复杂的多关卡联动了。
|
评分
-
查看全部评分
|