新命令/bossbar 自定义boss血条实现指向效果

大家好我是switefaster
18w05a发布了
加了一个很有意思的命令:/bossbar
作用是自定义boss血条。至于具体教程……新闻版里已经有了,请自行翻阅。
那么我就抢新体验一把,拿bossbar做个小东西吧:

指向标
听不懂嘛?先上图:
从图中我们可以看到当玩家指向盔甲架的时候,TARGET这个bossbar满了。当玩家朝向和玩家指向盔甲架的向量的夹角θ超过90°时,TARGET变红,并且夹角θ越大,TARGET的值越大。过程不受抬头/低头影响。(gif中由于鼠标抖动而造成有一点不稳定)
看着挺高端,其实原理超级简单。就是最基础的Vector Math:点积(dot product)
我们先上命令:
init.mcfunction [LOAD]
  1. # Initialization
  2. # Create bossbar
  3. bossbar create bossbar:target "TARGET"
  4. # Player's location (X,Z)
  5. scoreboard objectives add pos_p_x dummy
  6. scoreboard objectives add pos_p_z dummy
  7. # Target's location (X,Z)
  8. scoreboard objectives add pos_t_x dummy
  9. scoreboard objectives add pos_t_z dummy
  10. # Player-facing vector (X,Z)
  11. scoreboard objectives add vec_yw_x dummy
  12. scoreboard objectives add vec_yw_z dummy
  13. # Target-to-player vector (X,Z)
  14. scoreboard objectives add vec_nvt_x dummy
  15. scoreboard objectives add vec_nvt_z dummy
  16. # Current dot product
  17. scoreboard objectives add int_dp_vt dummy
  18. # Max dot product
  19. scoreboard objectives add int_dp_mx dummy
  20. # Caclation output flag (to current or  to max)
  21. scoreboard objectives add con_mx_bool dummy
  22. # Temp score when calculating abs
  23. scoreboard objectives add tmp_abs_swp dummy

复制代码

bossbar_navigation.mcfunction [TICK]

  1. # Calculation
  2. # Set storage flag(to max or current)
  3. # Max
  4. scoreboard players set @a con_mx_bool 1
  5. # Calculate
  6. execute as @a at @s facing entity @e[tag=target,sort=nearest,limit=1] eyes run function bossbar:bossbar_dotproduct_calculation
  7. # Store max
  8. execute as @a store result bossbar bossbar:target max run scoreboard players get @s int_dp_mx
  9. # Current
  10. scoreboard players set @a con_mx_bool 0
  11. # Calculate
  12. execute as @a at @s run function bossbar:bossbar_dotproduct_calculation
  13. # Set display
  14. # Current equals or is greater than 0
  15. execute as @a[scores={int_dp_vt=0..}] run function bossbar:bossbar_positive_display
  16. # Current is less than 0
  17. execute as @a[scores={int_dp_vt=..-1}] run function bossbar:bossbar_minus_display
  18. # Glow the target so that player can identify them easily
  19. execute as @a at @s run effect give @e[tag=target,sort=nearest,limit=1] minecraft:glowing 1

复制代码

bossbar_dotproduct_calculation.mcfunction

  1. # Reset Scoreboards
  2. # Current case
  3. execute if entity @s[scores={con_mx_bool=0}] run scoreboard players set @s int_dp_vt 0
  4. # Max case
  5. execute if entity @s[scores={con_mx_bool=1}] run scoreboard players set @s int_dp_mx 0
  6. # Get player's location (X,Z)
  7. execute store result score @s pos_p_x run data get entity @s Pos[0] 100
  8. execute store result score @s pos_p_z run data get entity @s Pos[2] 100
  9. # Get player's facing vector(have not been normalized)
  10. # Summon length-marker
  11. execute if entity @s[scores={con_mx_bool=0}] rotated ~ 0 run summon minecraft:area_effect_cloud ^ ^ ^1 {CustomName:"[\"p_dir\"]"}
  12. # Get length-marker's location
  13. execute store result score @s pos_t_x run data get entity @e[name=p_dir,sort=nearest,limit=1] Pos[0] 100
  14. execute store result score @s pos_t_z run data get entity @e[name=p_dir,sort=nearest,limit=1] Pos[2] 100
  15. # Calculate the vector
  16. scoreboard players operation @s vec_yw_x = @s pos_t_x
  17. scoreboard players operation @s vec_yw_z = @s pos_t_z
  18. execute run scoreboard players operation @s vec_yw_x -= @s pos_p_x
  19. execute run scoreboard players operation @s vec_yw_z -= @s pos_p_z
  20. # Get target-to-player vector
  21. # Get target's location
  22. # Summon plainizer(X,Z)
  23. execute run summon minecraft:area_effect_cloud ~ ~ ~ {CustomName:"[\"plainizer\"]"}
  24. # Correct the plainizer's location(Tx,Py,Tz)
  25. execute store result entity @e[name=plainizer,sort=nearest,limit=1] Pos[0] double 1 run data get entity @e[tag=target,sort=nearest,limit=1] Pos[0]
  26. execute store result entity @e[name=plainizer,sort=nearest,limit=1] Pos[1] double 1 run data get entity @s Pos[1]
  27. execute store result entity @e[name=plainizer,sort=nearest,limit=1] Pos[2] double 1 run data get entity @e[tag=target,sort=nearest,limit=1] Pos[2]
  28. # Summon length-marker
  29. execute facing entity @e[name=plainizer,sort=nearest,limit=1] eyes run summon minecraft:area_effect_cloud ^ ^ ^1 {CustomName:"[\"tp_dir\"]"}
  30. # Get length-marker's location
  31. execute store result score @s pos_t_x run data get entity @e[name=tp_dir,sort=nearest,limit=1] Pos[0] 100
  32. execute store result score @s pos_t_z run data get entity @e[name=tp_dir,sort=nearest,limit=1] Pos[2] 100
  33. # Calculate the vector
  34. scoreboard players operation @s vec_nvt_x = @s pos_t_x
  35. scoreboard players operation @s vec_nvt_z = @s pos_t_z
  36. execute run scoreboard players operation @s vec_nvt_x -= @s pos_p_x
  37. execute run scoreboard players operation @s vec_nvt_z -= @s pos_p_z
  38. # Max case
  39. execute if entity @s[scores={con_mx_bool=1}] run scoreboard players operation @s vec_yw_x = @s vec_nvt_x
  40. execute if entity @s[scores={con_mx_bool=1}] run scoreboard players operation @s vec_yw_z = @s vec_nvt_z
  41. # Dot Product
  42. # Multiply the x and z of direction-vector to the x and z of target-to-player vector
  43. execute run scoreboard players operation @s vec_nvt_x *= @s vec_yw_x
  44. execute run scoreboard players operation @s vec_nvt_z *= @s vec_yw_z
  45. # Add the result to output-scoreboards
  46. # Current case
  47. execute if entity @s[scores={con_mx_bool=0}] run scoreboard players operation @s int_dp_vt += @s vec_nvt_x
  48. execute if entity @s[scores={con_mx_bool=0}] run scoreboard players operation @s int_dp_vt += @s vec_nvt_z
  49. # Max case
  50. execute if entity @s[scores={con_mx_bool=1}] run scoreboard players operation @s int_dp_mx += @s vec_nvt_x
  51. execute if entity @s[scores={con_mx_bool=1}] run scoreboard players operation @s int_dp_mx += @s vec_nvt_z

复制代码

※ 暂未想到办法简化第二个plainizer...感谢玄素dalao提供rotated思路
bossbar_positive_display.mcfunction
  1. # Set color
  2. bossbar set bossbar:target color white
  3. # Write result to bossbar
  4. execute store result bossbar bossbar:target value run scoreboard players get @s int_dp_vt

复制代码

bossbar_minus_display.mcfunction

  1. # Set color
  2. bossbar set bossbar:target color red
  3. # Get |int_dp_vt| by |int_dp_vt| = -int_dp_vt(int_dp_vt < 0) = int_dp_vt - 2 * int_dp_vt
  4. scoreboard players operation @s tmp_abs_swp = @s int_dp_vt
  5. scoreboard players operation @s int_dp_vt -= @s tmp_abs_swp
  6. scoreboard players operation @s int_dp_vt -= @s tmp_abs_swp
  7. # Write result to bossbar
  8. execute store result bossbar bossbar:target value run scoreboard players get @s int_dp_vt

复制代码

※ 以上所有文件都存在于bossbar命名空间内。请根据需要自行修改
※ [LOAD]为load.json内的function [TICK]为tick.json内的function
※ 注意,尽管我使用了@a,但是这个命令组只支持一个玩家。因为bossbar只有一个。
看着好长一大串对不对?别怕,很多工作都是重复的。但是我们先抛开mc,来考虑一下数学计算。

定义几个量:

u 玩家朝向的二维向量(因为朝向只存在于xz平民啊内,只考虑x和z) v 玩家指向目标的二维向量(同样存在于xz平面内)
我们要干什么?是得到 u·v,为了方便操作防止超精度我们得normalize一下,所以得到
normalize(u) · normalize(v)
这就是我们要求的东西了

现在我们知道我们要干啥了,那么怎么怎么在MC里实现呢?

大家先看一下创建的记分板,我的记分板是有命名规律的:
  pos_开头表示坐标 vec_开头表示向量 这两种量的第三项都是[x,y,z]以表示分量。因为在xz平面内操作所以只用到了x和z
  剩下的int_ con_ tmp_都是一些常量啊整数啊临时值啊之类的东西
我们把目光移向两个pos:
  pos_p:存玩家的x,z坐标
  pos_t:存指向目标的x,z坐标
再看看两个vec:
  vec_yw:存玩家的yaw向量,也就是左右看时头朝向的单位向量
  vec_nvt:存玩家指向目标方向的单位向量
坐标的获取已经是1.13的基本操作了我就不细讲了。
重点是这两个向量的求法了。前者的获取很简单,以rotated ~ 0执行以确保平行于xz平面,然后在^ ^ ^1位置生成一个aec,获取坐标然后相减。
后者的获取也不难
主要过程就是,因为玩家和目标可能不在一个高度上,导致xz平面上的二维向量长度不是1。所以我们召唤一个aec,把其x,z轴设置为目标的x,z轴,把其y轴设置为玩家的y轴。这样我们就相当于拥有了和玩家在同一高度的这个实体了。
然后我们获取这个aec的坐标,然后让玩家面向它在视角前方生成另一个aec。我们获取这个aec的坐标,然后获取这个aec指向玩家的向量。怎么获取呢?只要(AEC-玩家坐标)就行了。
※ 我们把坐标当作向量来操作。相减就能得到两个向量的头指向另一个向量的头的向量。
那么我们获取到了我们需要进行点积操作的两个向量。我们要进行点积了。这时候我们需要看一下点积的定义(最直接的):

u · v = uxvx + uyvy所以我们需要做的就是分别把两个向量的分量相乘然后相加了。
好的!我们现在拥有了这两个向量的点积。但是只要稍加尝试就会发现点积的最大值会随玩家到目标的距离增加而增加……但是我们的bossbar需要最大值啊?所以我们只要计算最大值就行了。根据点积的定义,只要我们的 yaw向量=(-1 * 玩家-目标向量),点积的值就是最大的。所以我们只要获取一个玩家面向目标时的点积就行了。具体做法就看我发上去的命令吧。
核心部分已经解决了……我们现在来看看最不重要的部分——显示
分为两种情况:1.≥0 2.<0
前者只需改个颜色直接写入即可。后者则得取一下绝对值
上过初中的同学都知道|a|=-a(a≤0),所以a-2a=-a=|a|(a≤0),这就是取绝对值操作了……

EXTRA
这是个没什么用的额外部分……主要说一下已知bug之类的

发表评论

您必须 登录 才能发表留言!