本帖最后由 switefaster 于 2018-7-25 21:12 编辑

  在用指令做系统的时候,可能会需要求取两点之间的距离。在之前,人们大多数使用 √x^2+y^2+z^2 来获得距离。然而这个方法需要开平方,在MC中,利用指令开方是一个十分耗费时间的操作。那么,有没有办法不开平方,而只用四则运算获得两点之间的距离呢?
当然有啦!

注意:以下所有坐标皆以求取位置的其中一点为原点考虑。如果应用到系统中,可能需要做一个减法

让我们先接受降维打击考虑二维的情况,看图

如图,P为要求距离的点,x'为其x坐标(以下将直线x=x'与x轴交点称为X')。黑色的块状物体(雾,以下简称点N)为在原点(以下简称O)到P的直线(以下简称OP)上长度为1的位置,x为其x坐标(以下将直线x=x与x轴交点称为X)。∵△ONXOPX
∴OP/1=x'/x,OP=x'/x
至此,我们已经得到了用除法得到长度的方法。在游戏中,只要facing目标点,并且使用本地坐标^ ^ ^1来获取N的位置即可

进阶,三维空间!前面我们已经讲述了如何在平面空间内用除法得到距离。但是游戏内接触的大多是立体空间,所以我们需要考虑如何在立体空间内更简便地得到距离。事实上也并不比平面复杂多少,如法炮制而已。看图

有点乱……
图中,P为求距离的点,蓝色的线(以下简称OP')为线段OP在xz平面上的正投影,P'为P在xz平面的正投影,N'为N在xz平面的正投影。OP'上的黑点为从原点出发在蓝线上长度为1的位置,其x坐标为x''。其他都和二维的情况相同。
为了得到OP,我们可以将二维求距离法应用到△OPP',但是我们缺少ON'的长度和OP'的长度。所以我们先在△OX'P'上使用二维距离法得到OP'=x'/x'',在△ON'X内得到ON'=x/x''。得到△OPP'中,OP=OP'/ON'=(x'/x'')/(x/x'')=x'/x
所以三维内的距离求法依然是x'/x,即目标x坐标除以到目标直线上单位长度点的x坐标
然而你以为这就结束了么?

——不!

我们还有一个很严重的问题需要考虑——如果目标和我都在同一个x坐标上怎么办?这将会导致算出来的结果永远都是0,因为x'永远都是0!

聪明的读者可能已经想到了,可以用其他轴求取。没错!但是其他轴也面临着距离是0的问题,还需要做判断,非常之麻烦……

所以,我们将其整合起来,化为如下的公式

x'/x=y'/y=z'/z=(|x'|+|y'|+|z'|)/(|x|+|y|+|z|)

※ 式中带 ' 项均表示目标的坐标分项,而不带 ' 则表示与目标之间直线上长度为1的点的坐标分项

我们推荐使用等式最后一项,即(|x'|+|y'|+|z'|)/(|x|+|y|+|z|)。这个公式只会在x'=y'=z'=0的时候才会得到距离为0的结果,而此时距离确实是0!


在这里给出计算玩家眼睛和最近的具有target标签的实体距离的函数记分板:
  1. #距离
  2. scoreboard objectives add dist dummy
  3. #x y z
  4. scoreboard objectives add x dummy
  5. scoreboard objectives add y dummy
  6. scoreboard objectives add z dummy
  7. #x' y' z'
  8. scoreboard objectives add xd dummy
  9. scoreboard objectives add yd dummy
  10. scoreboard objectives add zd dummy
  11. #玩家坐标
  12. scoreboard objectives add xp dummy
  13. scoreboard objectives add yp dummy
  14. scoreboard objectives add zp dummy
  15. #|x|+|y|+|z|
  16. scoreboard objectives add bsum dummy
  17. #-1
  18. scoreboard players set minus dist -1
复制代码
计算部分:
  1. scoreboard players set @s dist 0
  2. scoreboard players set @s bsum 0
  3. execute as @e[tag=target,limit=1,sort=nearest] store result score @s xd run data get entity @s Pos[0] 10000
  4. execute as @e[tag=target,limit=1,sort=nearest] store result score @s yd run data get entity @s Pos[1] 10000
  5. execute as @e[tag=target,limit=1,sort=nearest] store result score @s zd run data get entity @s Pos[2] 10000
  6. execute store result score @s xp run data get entity @s Pos[0] 10000
  7. execute store result score @s yp run data get entity @s Pos[1] 10000
  8. execute store result score @s zp run data get entity @s Pos[2] 10000
  9. scoreboard players operation @e[tag=target,limit=1,sort=nearest] xd -= @s xp
  10. scoreboard players operation @e[tag=target,limit=1,sort=nearest] yd -= @s yp
  11. scoreboard players operation @e[tag=target,limit=1,sort=nearest] zd -= @s zp
  12. execute facing entity @e[tag=target,limit=1,sort=nearest] eyes run summon minecraft:area_effect_cloud ^ ^ ^1 {"Tags":["marker"]}
  13. execute as @e[tag=marker,limit=1,sort=nearest] store result score @s x run data get entity @s Pos[0] 10000
  14. execute as @e[tag=marker,limit=1,sort=nearest] store result score @s y run data get entity @s Pos[1] 10000
  15. execute as @e[tag=marker,limit=1,sort=nearest] store result score @s z run data get entity @s Pos[2] 10000
  16. execute as @e[tag=target,limit=1,sort=nearest] if score @s xd matches ..-1 run scoreboard players operation @s xd *= minus dist
  17. execute as @e[tag=target,limit=1,sort=nearest] if score @s yd matches ..-1 run scoreboard players operation @s yd *= minus dist
  18. execute as @e[tag=target,limit=1,sort=nearest] if score @s zd matches ..-1 run scoreboard players operation @s zd *= minus dist
  19. execute as @e[tag=marker,limit=1,sort=nearest] if score @s x matches ..-1 run scoreboard players operation @s x *= minus dist
  20. execute as @e[tag=marker,limit=1,sort=nearest] if score @s y matches ..-1 run scoreboard players operation @s y *= minus dist
  21. execute as @e[tag=marker,limit=1,sort=nearest] if score @s z matches ..-1 run scoreboard players operation @s z *= minus dist
  22. scoreboard players operation @s dist += @e[tag=target,limit=1,sort=nearest] xd
  23. scoreboard players operation @s dist += @e[tag=target,limit=1,sort=nearest] yd
  24. scoreboard players operation @s dist += @e[tag=target,limit=1,sort=nearest] zd
  25. scoreboard players operation @s bsum += @s x
  26. scoreboard players operation @s bsum += @s y
  27. scoreboard players operation @s bsum += @s z
  28. scoreboard players operation @s dist /= @s bsum
复制代码
※精度为0.0001

结语:
1.13带来的技术更新让之前许多麻烦的操作变得简单,但是很多可以简化的操作我们并没有想到。我们需要开动脑筋,做一个聪明的CBer!

鸣谢:

感谢 @ruhuasiyu 提供的思路


[groupid=546]Command Block Logic[/groupid]