这里是我在1.13快照水的第二贴:实现实体与方块的弹性碰撞。
(气氛突然物理起来)
就是无能量损失地碰撞而可能使速度大小或方向发生改变的一个过程,
常见的碰撞大都是非弹性碰撞(有能量损失,转化成热能等),非弹性碰撞也是可以实现的。
效果,先看几张GIF:
玩家向前走会使比较器输出信号,激活发射器击退村民,村民撞墙后弹回一段距离
村民从高处落下,像落到粘液块上一样向上弹起一段距离
(打了这个村民三箭又从这么高掉地上摔伤两次居然没死)
命令方块被激活后生成了一个有初速度的盔甲架,在某狭小空间内发生了若干次碰撞
命令方块被激活后生成了一个有初速度的盔甲架,在广阔空间内发生了若干次碰撞
原理:
经过多次实验,玩家类型的非特殊实体,
在水平方向上(xz轴确定的平面),实体在撞墙后,垂直于墙面方向的Motion会归零,平行于墙面的Motion不变,
在竖直方向上(y轴方向),实体在碰到撞到方块上后,y方向的Motion会变为-0.0785(保留四位小数),xz方向上的Motion不变,
而实际上,如果要满足弹性碰撞,应该是这样的,
在水平方向上,实体撞墙后,垂直于墙面方向的Motion大小不变,方向反过来,也就是垂直墙面方向的Motion变为相反数,平行于墙面的Motion不变,
在竖直方向,实体在撞到方块上后,y方向的Motion变为(y方向的Motion+0.0785)的相反数,xz方向上的Motion不变,
想要实现弹性碰撞,就要先检测实体撞墙,然后把Motion反过来,
怎么检测撞墙?前一刻这个方向的Motion还不是0(对于y来说是-785),而这一刻这个方向Motion变成0了,就可以判定它撞墙了。
实现这个需要的命令也是很简单的,看下面的function:
该function的tag为load(在第一次加载或使用reload时会自动加载一次):
- scoreboard objectives add MotionX dummy
- scoreboard objectives add MotionY dummy
- scoreboard objectives add MotionZ dummy
- scoreboard objectives add MotionXL dummy
- scoreboard objectives add MotionYL dummy
- scoreboard objectives add MotionZL dummy
复制代码
该function的tag为tick(每tick执行一次,也就是高频执行):
- execute as @e store result score @s MotionX run data get entity @s Motion[0] 10000
- execute as @e store result score @s MotionY run data get entity @s Motion[1] 10000
- execute as @e store result score @s MotionZ run data get entity @s Motion[2] 10000
- execute as @e if entity @s[scores={MotionX=0}] unless entity @s[scores={MotionXL=0}] store result entity @s Motion[0] double -0.00008 run scoreboard players get @s MotionXL
- execute as @e if entity @s[scores={MotionY=-785}] unless entity @s[scores={MotionYL=-785}] run scoreboard players add @s MotionYL 785
- execute as @e if entity @s[scores={MotionY=-785}] unless entity @s[scores={MotionYL=-4000..0}] store result entity @s Motion[1] double -0.00008 run scoreboard players get @s MotionYL
- execute as @e if entity @s[scores={MotionZ=0}] unless entity @s[scores={MotionZL=0}] store result entity @s Motion[2] double -0.00008 run scoreboard players get @s MotionZL
- execute as @e store result score @s MotionXL run data get entity @s Motion[0] 10000
- execute as @e store result score @s MotionYL run data get entity @s Motion[1] 10000
- execute as @e store result score @s MotionZL run data get entity @s Motion[2] 10000
复制代码
第一部分就是创建一堆dummy型的记分板,
第二部分就是主要原理部分了,
按照这个顺序:
把实体的Motion输出到MotinoA(A代表X或Z),倍数为10000
如果MotionAL(上一gt的motion)不是0,且这MotionA(这一gt的motion)是0,就把上一gt的Motion负过来,赋给实体本身,
(这里的写-0.00008代表这是非弹性碰撞(能量损失20%),如果想改成弹性碰撞就写-0.0001)
把实体的Motion输出到MotinoAL(A代表X或Z),倍数为10000,
效果就是MotionAL代表上1gt的motion,MotionA代表这一gt的motion,中间检测是否碰撞,如果碰撞,反转Motion,
(是否损失能量可以自定义,如果想的话,你甚至可以增加能量)
上面只说了xz方向,y方向比较特殊,需要先+785再负过来,所以中间只多了个add 785的命令,
那个4000的限制是因为如果不加这个限制,由于mc的客户端和服务端不同步,到最后的弹的那几下看起来会很鬼畜,
鉴于高度很低,完全没有弹起的必要,就直接忽略这部分的碰撞了。
原理真是太简单了是吧:)
我是不会告诉你就这玩意我研究了四天!!!!!