这次来玩mc的datapack,做一个增强版的赛船系统

使用版本为1.21.4

在这记录这个赛船系统的制作过程

我希望它能够实现以下功能:

显示速度,计时等信息

与多个变量相关的集气

脱轨、碰撞的判定(用于氮气丢失)

以铺蓝冰实现加速的氮气释放

快捷的赛道起点终点设置

比赛开始,计时,结算等

比赛影像记录,播放和挑战

我之前做过一个基础的datapack,它能够显示速度,有集气和氮气加速的功能

不过那玩意比较简单粗暴,它通过计分板准则minecraft.custom:minecraft.boat_one_cm

每个游戏刻切片并清零来做速度表

然后在最后记录这一刻的速度,到了下一个游戏刻就是上一刻的速度

相减得到加速度

然后将负的加速度加和起来就是氮气值

暴力地舍弃过大的加速度(太大基本因为是跳表、脱轨或是碰撞)

我对这个旧版不是很满意,而且里面的构造确实屎山,我都不想改

所以直接开新的

速度表

这个原理和旧版一样,是用计分准则来实现的

不过拿来给人看是足够了,我甚至直接把旧版的搬过来

每个玩家只有一个actionbar,所以dummy创建一个actionbar计分板

还有速度计分板,上一刻速度,加速度

1
2
3
4
5
6
7
8
#data/common/function/load.mcfunction

scoreboard objectives add actionbar dummy

scoreboard objectives add boat_acceleration_present dummy

scoreboard objectives add boat_speed_present minecraft.custom:minecraft.boat_one_cm
scoreboard objectives add boat_speed_last_tick dummy

不同的数字对应不同的actionbar显示模式

1
2
3
4
#data/common/function/actionbar.mcfunction

execute if score @s actionbar matches 1 at @s run function boat_speed_monitor:speed_monitor_1
execute if score @s actionbar matches 2 at @s run function boat_speed_monitor:speed_monitor_2

里面有两种速度表,第一个是我新做的用于调试的速度表,第二个就是旧版的直接搬过来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#data/boat_speed_monitor/function/speed_monitor_1.mcfunction

title @s actionbar [{"text":"speed: ","color":"green"},{"score":{"objective":"boat_speed_present","name":"@s"},"color":"gold"},{"text":" acceleration: ","color":"green"},{"score":{"objective":"boat_acceleration_present","name":"@s"},"color":"gold"},{"text":" v_vec: [","color":"green"},{"score":{"objective":"v_vec0_p","name":"@s"},"color":"gold"},{"text":",","color":"green"},{"score":{"objective":"v_vec1_p","name":"@s"},"color":"gold"},{"text":",","color":"green"},{"score":{"objective":"v_vec2_p","name":"@s"},"color":"gold"},{"text":"]","color":"green"},{"text":" a_vec: [","color":"green"},{"score":{"objective":"a_vec0_p","name":"@s"},"color":"gold"},{"text":",","color":"green"},{"score":{"objective":"a_vec1_p","name":"@s"},"color":"gold"},{"text":",","color":"green"},{"score":{"objective":"a_vec2_p","name":"@s"},"color":"gold"},{"text":"]","color":"green"},{"text":" facing_vec: [","color":"green"},{"score":{"objective":"facing_vec0_p","name":"@s"},"color":"gold"},{"text":",","color":"green"},{"score":{"objective":"facing_vec1_p","name":"@s"},"color":"gold"},{"text":",","color":"green"},{"score":{"objective":"facing_vec2_p","name":"@s"},"color":"gold"},{"text":"]","color":"green"}]

#{"text":"speed: ","color":"green"},{"score":{"objective":"boat_speed_present","name":"@s"},"color":"gold"}
#速度显示

#{"text":" acceleration: ","color":"green"},{"score":{"objective":"boat_acceleration_present","name":"@s"},"color":"gold"}
#加速度显示

#{"text":" v_vec: [","color":"green"},{"score":{"objective":"v_vec0_p","name":"@s"},"color":"gold"},{"text":",","color":"green"},{"score":{"objective":"v_vec1_p","name":"@s"},"color":"gold"},{"text":",","color":"green"},{"score":{"objective":"v_vec2_p","name":"@s"},"color":"gold"},{"text":"]","color":"green"}
#速度向量显示

#{"text":" a_vec: [","color":"green"},{"score":{"objective":"a_vec0_p","name":"@s"},"color":"gold"},{"text":",","color":"green"},{"score":{"objective":"a_vec1_p","name":"@s"},"color":"gold"},{"text":",","color":"green"},{"score":{"objective":"a_vec2_p","name":"@s"},"color":"gold"},{"text":"]","color":"green"}
#加速度向量显示

{"text":" facing_vec: [","color":"green"},{"score":{"objective":"facing_vec0_p","name":"@s"},"color":"gold"},{"text":",","color":"green"},{"score":{"objective":"facing_vec1_p","name":"@s"},"color":"gold"},{"text":",","color":"green"},{"score":{"objective":"facing_vec2_p","name":"@s"},"color":"gold"},{"text":"]","color":"green"}
#朝向向量显示

因为我是做了几个模块后才开始写这个md,速度向量,加速度向量,和朝向向量后面会给出

1
2
3
4
5
6
7
8
#data/boat_speed_monitor/function/speed_monitor_2.mcfunction

execute if score @s boat_speed_present matches 0..79 run title @s actionbar [{"text":"速度:","color":"green"},{"score":{"objective":"boat_speed_present","name":"@s"},"color":"green"},{"text":"cm/tick","color":"green"}]
execute if score @s boat_speed_present matches 80..159 run title @s actionbar [{"text":"速度:","color":"gold"},{"score":{"objective":"boat_speed_present","name":"@s"},"color":"gold"},{"text":"cm/tick","color":"gold"}]
execute if score @s boat_speed_present matches 160..239 run title @s actionbar [{"text":"速度:","color":"light_purple"},{"score":{"objective":"boat_speed_present","name":"@s"},"color":"light_purple"},{"text":"cm/tick","color":"light_purple"}]
execute if score @s boat_speed_present matches 240..319 run title @s actionbar [{"text":"速度:","color":"red"},{"score":{"objective":"boat_speed_present","name":"@s"},"color":"red"},{"text":"cm/tick","color":"red"}]
execute if score @s boat_speed_present matches 320.. run title @s actionbar [{"text":"速度:","color":"dark_purple"},{"score":{"objective":"boat_speed_present","name":"@s"},"color":"dark_purple"},{"text":"cm/tick","color":"dark_purple"}]

这个就是直接从我以前做的包扒过来的,我挺满意这个

一开始我在想

集气系统应该跟这几个变量相关

加速度大小,速度大小,速度方向与船头方向的夹角大小

那肯定只用这两个变量是不够,因为缺少了方向

而且我觉得加速度的精度有点不够,因为这样得到的加速度绝对值基本只在0到4

计分板又只给整数,太少了

(因为船前进的牵引加速度只有0.04m/tick^2^)

我想了想,既然我需要能够描述“方向”,那我不如使用向量?

终点坐标减去起点坐标就是向量,可能做起来复杂一点,但不是不能实现

要得到向量就逃不开计算,要计算就逃不开计分板

因为计分板只能储存整数,那我必然逃不开精度问题

考虑什么样的精度比较合适确实让我思考了几天

1.int只有31位是数据位,如果精度过高,那么坐标范围就会比较有限

2.百分之一的精度对加速度是不够的

3.向量运算过程中很容易涉及乘法,我需要尽量保证向量能完整计算一次乘法

船的速度一般超不过4m/tick

蓝冰的最高速度也就40/11=3.6363…m/tick

加速度那更小

然后边想边试,暂时让精度为2^10

为了让精度比较好改,我把精度全部写在一个函数里

1
2
3
4
5
6
7
#data/common/function/pos_p_get.mcfunction

execute store result score @s Pos0_p run data get entity @s Pos[0] 1024
execute store result score @s Pos1_p run data get entity @s Pos[1] 1024
execute store result score @s Pos2_p run data get entity @s Pos[2] 1024
#向量倍率来自这里,更改精度改这里就好
#1024=2^10,int有31位数据位,所以允许坐标范围在±2^21=±2,097,152内

既然需要算速度,那自然需要储存上一刻的坐标

1
2
3
4
5
#data/common/function/pos_lt_get.mcfunction

scoreboard players operation @s Pos0_lt = @s Pos0_p
scoreboard players operation @s Pos1_lt = @s Pos1_p
scoreboard players operation @s Pos2_lt = @s Pos2_p

这个函数需要放在tick的最后部分执行

接下来把pos_p减去pos_lt就能得到v_p当前游戏刻速度向量

1
2
3
4
5
6
7
8
9
#data/common/function/v_vec_p_get.mcfunction

scoreboard players operation @s v_vec0_p = @s Pos0_p
scoreboard players operation @s v_vec1_p = @s Pos1_p
scoreboard players operation @s v_vec2_p = @s Pos2_p

scoreboard players operation @s v_vec0_p -= @s Pos0_lt
scoreboard players operation @s v_vec1_p -= @s Pos1_lt
scoreboard players operation @s v_vec2_p -= @s Pos2_lt

同理,获取上一刻速度向量v_lt

1
2
3
4
5
#data/common/function/v_vec_lt_get.mcfunction

scoreboard players operation @s v_vec0_lt = @s v_vec0_p
scoreboard players operation @s v_vec1_lt = @s v_vec1_p
scoreboard players operation @s v_vec2_lt = @s v_vec2_p

然后获取加速度向量a_p

1
2
3
4
5
6
7
8
9
#data/common/function/a_vec_p_get.mcfunction

scoreboard players operation @s a_vec0_p = @s v_vec0_p
scoreboard players operation @s a_vec1_p = @s v_vec1_p
scoreboard players operation @s a_vec2_p = @s v_vec2_p

scoreboard players operation @s a_vec0_p -= @s v_vec0_lt
scoreboard players operation @s a_vec1_p -= @s v_vec1_lt
scoreboard players operation @s a_vec2_p -= @s v_vec2_lt

脱轨、碰撞判定

本来的想法就比较直接

看看船的侧边、下方、侧边下方有没有非法方块

但是发现这玩意不好定义啊

侧边可以是类似火把,按钮,压力板一类不影响碰撞的方块,下边是冰一样能跑

也可以是像栅栏一样碰撞箱不完整的方块

(然后就有了个废稿type1)

那还不如只看下方和侧边下方的方块

如果没有墙那就只要在赛道侧边放一个方块就能表示边界

如果有墙就只需要在墙下边放一个方块就好,效果一样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#data/wall_hit_judge/function/type2/judge.mcfunction

#这个方案通过检测脚底和船侧底下的方块来检测是否出轨

scoreboard players set @s wall_hit_judge 1
execute on vehicle at @s if block ~ ~-0.01 ~ #wall_hit_judge:boat_can_run_on if block ~0.68751 ~-0.01 ~ #wall_hit_judge:boat_can_run_on if block ~ ~-0.01 ~0.68751 #wall_hit_judge:boat_can_run_on if block ~-0.68751 ~-0.01 ~ #wall_hit_judge:boat_can_run_on if block ~ ~-0.01 ~-0.68751 #wall_hit_judge:boat_can_run_on if block ~0.68751 ~-0.01 ~0.68751 #wall_hit_judge:boat_can_run_on if block ~-0.68751 ~-0.01 ~-0.68751 #wall_hit_judge:boat_can_run_on if block ~-0.68751 ~-0.01 ~0.68751 #wall_hit_judge:boat_can_run_on if block ~0.68751 ~-0.01 ~-0.68751 #wall_hit_judge:boat_can_run_on if block ~0.6875 ~-0.01 ~0.68751 #wall_hit_judge:boat_can_run_on if block ~0.68751 ~-0.01 ~0.6875 #wall_hit_judge:boat_can_run_on if block ~0.68751 ~-0.01 ~-0.6875 #wall_hit_judge:boat_can_run_on if block ~0.6875 ~-0.01 ~-0.68751 #wall_hit_judge:boat_can_run_on if block ~-0.6875 ~-0.01 ~0.68751 #wall_hit_judge:boat_can_run_on if block ~-0.68751 ~-0.01 ~-0.6875 #wall_hit_judge:boat_can_run_on if block ~-0.68751 ~-0.01 ~0.6875 #wall_hit_judge:boat_can_run_on if block ~-0.6875 ~-0.01 ~0.68751 #wall_hit_judge:boat_can_run_on on passengers at @s run scoreboard players set @s wall_hit_judge 0
#(0,0,0),(1.1,0,0),(0,0,1.1),(-1.1,0,0),(0,0,-1.1),(1.1,0,1.1),(-1.1,0,-1.1),(-1.1,0,1.1),(1.1,0,-1.1),(1,1.1),(1.1,1),(1.1,-1),(1,-1.1),(-1,-1.1),(-1.1,-1),(-1.1,1),(-1,1.1)

#execute on vehicle at @s run particle dust{color:16755200,scale:0.4} ~ ~2 ~
#execute on vehicle at @s run particle dust{color:16755200,scale:0.4} ~0.68751 ~2 ~
#execute on vehicle at @s run particle dust{color:16755200,scale:0.4} ~ ~2 ~0.68751
#execute on vehicle at @s run particle dust{color:16755200,scale:0.4} ~-0.68751 ~2 ~
#execute on vehicle at @s run particle dust{color:16755200,scale:0.4} ~ ~2 ~-0.68751
#execute on vehicle at @s run particle dust{color:16755200,scale:0.4} ~0.68751 ~2 ~0.68751
#execute on vehicle at @s run particle dust{color:16755200,scale:0.4} ~-0.68751 ~2 ~-0.68751
#execute on vehicle at @s run particle dust{color:16755200,scale:0.4} ~-0.68751 ~2 ~0.68751
#execute on vehicle at @s run particle dust{color:16755200,scale:0.4} ~0.68751 ~2 ~-0.68751

#execute on vehicle at @s run particle dust{color:16755200,scale:0.4} ~ ~1.5 ~
#execute on vehicle at @s run particle dust{color:16755200,scale:0.4} ~0.68751 ~1.5 ~
#execute on vehicle at @s run particle dust{color:16755200,scale:0.4} ~ ~1.5 ~0.68751
#execute on vehicle at @s run particle dust{color:16755200,scale:0.4} ~-0.68751 ~1.5 ~
#execute on vehicle at @s run particle dust{color:16755200,scale:0.4} ~ ~1.5 ~-0.68751
#execute on vehicle at @s run particle dust{color:16755200,scale:0.4} ~0.68751 ~1.5 ~0.68751
#execute on vehicle at @s run particle dust{color:16755200,scale:0.4} ~-0.68751 ~1.5 ~-0.68751
#execute on vehicle at @s run particle dust{color:16755200,scale:0.4} ~-0.68751 ~1.5 ~0.68751
#execute on vehicle at @s run particle dust{color:16755200,scale:0.4} ~0.68751 ~1.5 ~-0.68751

#execute on vehicle at @s run particle dust{color:16755200,scale:0.4} ~ ~1 ~
#execute on vehicle at @s run particle dust{color:16755200,scale:0.4} ~0.68751 ~1 ~
#execute on vehicle at @s run particle dust{color:16755200,scale:0.4} ~ ~1 ~0.68751
#execute on vehicle at @s run particle dust{color:16755200,scale:0.4} ~-0.68751 ~1 ~
#execute on vehicle at @s run particle dust{color:16755200,scale:0.4} ~ ~1 ~-0.68751
#execute on vehicle at @s run particle dust{color:16755200,scale:0.4} ~0.68751 ~1 ~0.68751
#execute on vehicle at @s run particle dust{color:16755200,scale:0.4} ~-0.68751 ~1 ~-0.68751
#execute on vehicle at @s run particle dust{color:16755200,scale:0.4} ~-0.68751 ~1 ~0.68751
#execute on vehicle at @s run particle dust{color:16755200,scale:0.4} ~0.68751 ~1 ~-0.68751

下边一坨注释是用来坐标测试的,不重要

大体意思就是一开始先把判定值设为1,如果每个判定点的方块都属于#wall_hit_judge:boat_can_run_on,那就把判定值设为0

判定值为1代表脱轨,0代表正常

1
2
3
4
5
6
7
8
9
#data/wall_hit_judge/tags/block/boat_can_run_on

{
"values": [
"#minecraft:air",
"#minecraft:ice",
"minecraft:water"
]
}

为了在撞墙后触发事件,我自然需要记录上一刻的碰撞状态

1
2
3
#data/common/function/tick.mcfunction

execute as @a at @s run scoreboard players operation @s wall_hit_judge_lt = @s wall_hit_judge

然后用一个新函数规定事件

1
2
3
4
5
6
7
##data/wall_hit_judge/function/oot_event.mcfunction

#oot=out of track

playsound minecraft:block.fire.extinguish voice @s ~ ~ ~ 100 2

#现在只有播放熄火音效的功能,剩下丢失集气的内容等集气系统写好了再规定

事件触发

1
2
3
#data/common/function/tick.mcfunction

execute as @a at @s if score @s wall_hit_judge matches 1 if score @s wall_hit_judge_lt matches 0 run function wall_hit_judge:oot_event

先睡了