这个数据包从根本上、低消耗地解决了原版非完整方块碰撞判定的问题。
特点
- 碰撞极为精准,和自带的黑框别无二致
- 开销较低,穿过一个方块平均消耗命令200条左右
- 可任意控制判定距离,灵活度高
- 建模方便,可增加新的自定义碰撞箱
如何使用
给任意一个药水云的三个计分板(mrcd_x0、mrcd_y0和mrcd_z0)分别在三个方向上要前进的距离(单位是毫块,也就是坐标乘1000),再执行mrcd:generic/start这个函数即可。该药水云会传送至碰撞终点。若发生碰撞,则会产生mrcd_touch_edge和mrcd_touch_DIRECTION这些tag。其中后一个tag表示该药水云碰撞的面。(包括x_plus、x_minus、y_plus、y_minus、z_plus和z_minus)
如果希望药水云可以穿透一些玩家可穿透的方块,给该药水云加上mrcd_bullet这个tag即可。
如果希望药水可以和实体碰撞的话,给该药水云加上mrcd_entity这个tag即可。若碰撞到实体,则会产生mrcd_touch_entity这个tag,同时被碰撞的实体会被打上mrcd_tagget_entity这个tag。默认情况下不识别玩家,如果想要更改的话就在mrcd:target这个实体标签下添加player即可。可以与mrcd_bullet标签同时使用。
具体实现可以查看mrcd:raycast,mrcd:bullet和mrcd:entity这三个函数,分别表示了视线追踪的应用、发射子弹和识别实体的应用。
限制
由于游戏内花花和竹子的碰撞箱是根据方块坐标计算变化的,因此暂时没有将其实现,视为完整方块。
没有任何限制啦!在最新版v1.3中MRCD已能够计算竹子和花花的坐标偏移。没有任何方块能逃脱MRCD的眼睛!
图片欣赏
更新日志
- v1.0
- 初次发布
- 初次发布
- v1.1
- 添加浆果丛支持
- 添加浆果丛支持
- v1.2
- 现在AEC可以碰撞实体了
- 一些命令改进
- v1.2.1
- 现在AEC将会传送到被碰撞实体的边界上了
- 现在AEC将会传送到被碰撞实体的边界上了
- v1.2.2
- bug修复:触碰完整方块x+,y+和z+面的AEC将会停留在碰撞前的一个方块内
- 例子函数经过了一些修改
- v2.0
- 更名为超精准射线碰撞检测器(Minecraft Ray Collision Detector)
- bug修复:蘑菇被当做完整方块
- bug修复:子弹可穿过竹子
- 添加花花、竹笋和竹子的支持
- v2.1
- 支持1.16
- 添加火、灵魂火、灵魂灯笼、灵魂火把、灵魂营火、锁链、下界藤蔓、下界草、下界菌的支持
- 为红石线、墙添加新的方块状态
- bug修复:藤蔓被当作完整方块
版本对应
- 数据包版本2.1 对应游戏版本1.16
- 数据包版本2.0 对应游戏版本1.14.4, 1.15.2
特别鸣谢
- SPGoding 这里还有他基于这个包写的超赞的轻量级喷漆系统,spg超棒的!
- 00ll00
- 素学姐 他的在MC中计算MD5启示了我有关hashcode计算的想法
- CBL的各位
原理
将药水云的三个计分板视为位移向量,将MC中的方块每个面视为平面,计算线面交点,若交点坐标在某一范围内,则发生碰撞。具体实现如下:
药水云的位移向量是:(#total_x, #total_y, #total_z)
首先获取当前位置的坐标,并把它转换成方块坐标,保存到(#block_x, #block_y, #block_z)里。(也就是以所在方块xyz值最小那个点为原点的坐标)
- scoreboard players set #const_1000 mrcd_system 1000
- execute store result score #block_x mrcd_system run data get entity @s Pos[0] 1000
- execute store result score #block_y mrcd_system run data get entity @s Pos[1] 1000
- execute store result score #block_z mrcd_system run data get entity @s Pos[2] 1000
- scoreboard players operation #block_x mrcd_system %= #const_1000 mrcd_system
- scoreboard players operation #block_y mrcd_system %= #const_1000 mrcd_system
- scoreboard players operation #block_z mrcd_system %= #const_1000 mrcd_system
位移向量转化为空间直线方程,用点向式:(x-#block_x)/#total_x=(y-#block_y)/#total_y=(z-#block_z)/#total_z。
这里以x=0平面为例,平面方程为x=0,带入可解得:
y=(-#block_x)#total_y/#total_x+#block_y
z=(-#block_x)#total_z/#total_x+#block_z
用命令表示就是这样:
- # y
- scoreboard players set #target_y mrcd_system 0
- scoreboard players operation #target_y mrcd_system -= #block_x mrcd_system
- scoreboard players operation #target_y mrcd_system *= @s mrcd_y0
- scoreboard players operation #target_y mrcd_system /= @s mrcd_x0
- scoreboard players operation #target_y mrcd_system += #block_y mrcd_system
- # z
- scoreboard players set #target_z mrcd_system 0
- scoreboard players operation #target_z mrcd_system -= #block_x mrcd_system
- scoreboard players operation #target_z mrcd_system *= @s mrcd_z0
- scoreboard players operation #target_z mrcd_system /= @s mrcd_x0
- scoreboard players operation #target_z mrcd_system += #block_z mrcd_system
之后进行判定,若yz坐标值均在0-1000范围内,则说明发生碰撞。
- execute if score #target_y mrcd_system matches 0..1000 if score #target_z mrcd_system matches 0..1000 run tag @s add mrcd_touch_edge
具体数据包内对以上过程进行了简化和整合,此处特别感谢SPGoding提出的简化想法!让我不至于建模到头凸。
翻阅Minecraft源码,得到坐标偏移计算代码如下(经过简化):
- public Vec3d getOffsetPos(BlockState state, BlockView view, BlockPos blockPos) {
- long long6 = MathHelper.hashCode(blockPos.getX(), 0, blockPos.getZ());
- return new Vec3d(((long6 & 0xFL) / 15.0f - 0.5) * 0.5, 0.0, ((long6 >> 8 & 0xFL) / 15.0f - 0.5) * 0.5);
- }
- public static long hashCode(int x, int y, int z) {
- long long4 = (long)(x * 3129871) ^ z * 116129781L ^ (long)y;
- long4 = long4 * long4 * 42317861L + long4 * 11L;
- return long4 >> 16;
- }
其中hashcode计算涉及到long类型的乘、加和异或运算。但注意到,getOffsetPos函数调用hashcode时,仅使用到了hashcode的低16位。而在hashcode函数最后,又将hash运算结果右移了16位。因此,实际只需运算结果的低32位。又因为乘、加和异或运算数据高位对低位没有影响,因此不用另写大数计算的方法,直接调用计分板运算即可。
这里最关键问题是两个int类型的异或,这是消耗命令数最多的地方,我消耗了300+条命令,不知道有没有简便方法[groupid=546]Command Block Logic[/groupid]