【MC進階命令】如何優(yōu)雅地獲取兩點間距離?
本教程適用于Java 1.13+版本
什么是坐標?
要求兩點間的距離,這兩點肯定也有著相應的坐標,因此我們先從坐標看起
先以平面坐標為例

或許換一種方式更適合理解

這種以原點(0,0)各軸延長一段正/負距離的坐標,被稱為絕對坐標
再看看這張圖:

先看黑色部分,這一部分的表示方法正是剛才講到的絕對坐標的表示方法,但仔細看表示點A位置的橘色坐標與黑色坐標,這兩個坐標并不一樣,但表示的點卻相同,它們之間的區(qū)別是什么呢?區(qū)別在于,橘色坐標是以另一個點(6,-2)延長而得到A的坐標的,獲得的坐標也正是點A相對(6,-2)的坐標,這種相對的坐標,就叫做相對坐標
回顧一下:
絕對坐標:以原點(0,0)延長一定距離(分正負)得到的坐標,表示絕對的位置
相對坐標:以另一點延長一定距離(分正負)得到的坐標,表示一點相對另一點的位置
這時候可能有人要說了:要求兩點間的距離,肯定求是連接兩點的斜線,又不是兩點的坐標差,講這些有什么用呢?
欸,你先別急,才剛剛開始
三角函數(shù)!
看到“三角函數(shù)”四個大字先不要怕,其實很好理解
首先,在坐標系上,要表示斜線,其實就是表示一定比例的坐標

如圖,x=2y形成了一條斜線,這意味著斜線上的每一點的x坐標與y坐標都滿足2:1的關系,如圖片中的一點(4,2),x坐標就是y坐標的兩倍
現(xiàn)在,表示出一條斜線上的任意幾個點的坐標,并觀察這些坐標:

如圖,表示P?,P?,P?,P?四個坐標的原點x軸延長線 y軸延長線 以及斜線本身構成了4個三角形:OP?Q?,?OP?Q?,?OP?Q?,?OP?Q?,觀察這些三角形,首先它們都是直角三角形,且其中三個三角形共用同一角,而剩下一個三角形的一角與前三個三角形共有的一角互為對頂角,因此四個三角形都是相似三角形
因此它們各自的三邊(各自的x軸坐標 y軸坐標 以及對應的斜線的一部分)之間只有倍率關系,而不管哪一個三角形,x軸 y軸 斜邊 這三邊任意兩個邊的比值都是固定,即:
P?Q??:?OP?? ?=???P?Q??:?OP??? =???...???=? ?對邊 : 斜邊? ?=???sin?n???(sin,全稱sine,中文名正弦)
OQ??:?OP????=???OQ??:?OP??? =???...???=? ?臨邊 : 斜邊? ?=? ?cos?n???(cos,全稱cosine,中文名余弦)
P?Q??:?OQ????=???P?Q??:?OQ?? ?=? ?...? ?=? ?對邊 : 臨邊? ?=? ?tan n? ?(tan,全稱tangent,中文名正切)
然而,這些函數(shù)名并不需要專門去記,只需要知道不同三角形的相同邊的比值是相同的就行了
三角函數(shù)又該如何計算呢?可以讓游戲幫我們算!
又是坐標?
在mc中,有三種坐標表示方法:
絕對坐標:直接使用坐標數(shù)值表示,例:tp 0 100 0 將會把執(zhí)行者傳送到0 100 0處
相對坐標:使用波浪號~加坐標數(shù)值,表示執(zhí)行位置上的相對坐標,例:execute at @a?run tp ~ ~1 ~ 將會讓所有玩家傳送到自己的位置向上1格處
#解析:execute是一條能夠在修改執(zhí)行信息 判斷條件 儲存返回值的情況下執(zhí)行另一條命令的命令,execute中的at子命令能夠?qū)?zhí)行位置改為指定實體的位置,這里又聯(lián)系到相對坐標的功能 “?執(zhí)行位置上的相對坐標?” ,也就是修改了相對坐標判定的點為玩家的位置
局部坐標:使用我不知道叫啥的符號^加坐標數(shù)值,表示執(zhí)行位置,執(zhí)行方向上的相對坐標:

注:不糾正位置的情況下,局部坐標默認使用和相對坐標一樣的腳部坐標(實際的坐標),而不是頭部的坐標
例:execute at @a anchored eyes run tp ^ ^ ^1?將會讓所有玩家朝視線前方傳送1格
再注:就跟圖片里的一樣,“執(zhí)行方向”與“局部z軸”是重疊的(為了看清除我特地拆開了,但其實是重疊的)因此視線前一格就需要表示為^ ^ ^1
再再再注:無論相對坐標還是局部坐標,只有在命令里使用的時候是存在的,最后改變的都是絕對坐標,例如tp到視線前方之后,你的坐標依然是絕對坐標
我們正好就可以用這個局部坐標計算三角函數(shù):
夢幻聯(lián)動!
現(xiàn)在終于可以開始看最初的問題了(喜
知道了斜邊與另一邊的比值是固定的,那我們就可以反過來用另一邊的長度求斜邊的長度了

還記得最初的問題嗎:獲取兩點間的距離
在這張圖中,要獲取距離的兩點分別為點A 點B,而連接它們的斜線h2自然就是要求的距離了
而x2作為兩點的x坐標的差,可以很簡單地得到
再回顧一下剛剛講到的局部坐標:表示執(zhí)行位置,執(zhí)行方向上的相對坐標,我們只要將執(zhí)行方向設為在點A上面向點B的方向,并獲取^ ^ ^1的x軸坐標,但是,此時獲取的坐標是相對坐標轉化為絕對坐標的值,要直接獲取相對坐標,還需要將原位置移到原點(0,0,0)(這一步相當于減去了點A的坐標)再獲取^ ^ ^1的x軸的值,此時的值才是x1的值,這時候可能就有人問了,h1在哪呢?哎,^ ^ ^1中的1就是h1的值?。?/span>
再次觀察圖片,可以看到構成了一組與 “ 三角函數(shù)!” 部分一樣的相似三角形,再根據(jù)該部分講述到的三角函數(shù)知識,我們知道 cos A? ?=???臨邊 : 斜邊? ?=? ?x1 : h1? ?=? ?x2 : h2
現(xiàn)在結果已經(jīng)顯而易見了?x1?:?h1???=? ?x2?:?h2? 已知?x1?h1?x2 求?h2
很容易地就能算出,h2 = x2/cos A =?x2/(x1:h1)
因為 h1 = 1 所以又可以簡化為 h2 = x2 / x1
原理到這里就已經(jīng)講完了,現(xiàn)在只需要求得x2與x1即可
命令實現(xiàn)!
首先是獲取x2 也就是點A 點B的x軸坐標差
十分的簡單,先獲取點A和點B的坐標,然后求差,這里可以先不取絕對值,后面再把h2取絕對值就行了
也就是獲取 A的x坐標 - B的x坐標:
execute store result score #0?fTemp run data get entity 0-0-0-0-0?Pos[0] 1000000
execute store result score #1 fTemp run data get entity 0-0-0-0-1 Pos[0] 1000000
scoreboard players operation #0?fTemp -= #1 fTemp
(這里以實體0-0-0-0-0與實體0-0-0-0-1分別作為A和B,可自行換為目標選擇器)
解析( 請自行省略其中的 “ < ” 和 “ > ” ):
execute store result score #0?fTemp run data get entity 0-0-0-0-0?Pos[0] 1000000
execute是一條以子命令為執(zhí)行單位,能夠在修改執(zhí)行信息,判斷條件,儲存返回值的情況下執(zhí)行另一條命令的命令
其中的store子命令可以將最后一個子命令的返回值儲存在指定的位置,格式是 ... store <result>或<success> <位置> ...
當選擇result模式時,會將儲存命令的結果,而這里的“位置”,可以是一個計分板分數(shù),格式如下:... score <分數(shù)擁有者> <計分項id> ...
而data get可以獲取指定nbt的值,并返回這個值,而這個值,又剛好可以被execute store result儲存,但由于store只能儲存整型,計分板也只能記錄整型數(shù)據(jù),不能記錄小數(shù),因此要先進行放大再儲存,以兼容小數(shù),我這里乘上1000000(1000^2)是因為除數(shù)與商需要乘上1000倍,因此這里要乘1000^2倍
Pos是一個實體nbt,表示實體的坐標,Pos[0]則表示Pos數(shù)組中的第一項(也就是x軸坐標)
execute store result score #1 fTemp run data get?entity 0-0-0-0-1 Pos[0] 1000000
這條跟上一條一樣,只不過換成了另一個實體
scoreboard players operation #0?fTemp -= #1 fTemp
意思是計算減法 計算 #0?fTemp 減去?#1 fTemp 的值,并把計算結果儲存在#0?fTemp里
最后得到的#0,fTemp就是x2的值了
然后是獲取x1,這在前面已經(jīng)講過一次了
也不會多難,先在A的位置上把執(zhí)行方向設為看向B的方向,然后講執(zhí)行位置設為(0,0,0)此時將臨時實體tp到^ ^ ^1處,臨時實體的x軸坐標就是x1的值了:
execute at 0-0-0-0-0?facing entity 0-0-0-0-1?feet positioned .0 .0 .0 run tp 0-0-0-0-2 ^ ^ ^1
execute store result score #2 fTemp run data get entity 0-0-0-0-2 Pos[0] 1000
(這里以實體0-0-0-0-2表示臨時實體,可自行換成目標選擇器)
解析( 請自行省略其中的?“?<?”?和?“?>?”?):
execute?at?0-0-0-0-0?facing?entity?0-0-0-0-1?feet?positioned?.0?.0?.0?run?tp?0-0-0-0-2?^?^?^1
又是熟悉的execute命令,這次先用execute中的at(修改執(zhí)行位置)將執(zhí)行位置改為了0-0-0-0-0(點A)的位置
隨后用facing(效果是修改執(zhí)行方向,格式是facing <坐標>或<entity 實體>?<feet>或<eyes>,將執(zhí)行方向改為看向指定實體或指定坐標時的方向)在這個位置上把執(zhí)行方向改為了面向0-0-0-0-1(點B)的方向
又用positioned把執(zhí)行位置改為了0.0,0.0,0.0(.0的寫法省去了整數(shù)部分,默認為0)
最后把臨時實體0-0-0-0-2傳送到了以前面修改的方向上,局部坐標^ ^ ^1代表的位置
execute?store?result?score?#2?fTemp?run?data?get?entity?0-0-0-0-2?Pos[0]?1000
跟前面一樣的獲取坐標
最后得到的#2?fTemp就是x1的值了
這一大堆,說白了就是獲取A面向B時^ ^ ^1轉化為相對于A的位置時的x軸相對坐標
再說清楚點,就是點A朝點B移動一格,x軸增加了多少
最后計算 x2 / x1 的結果并取絕對值就行了
scoreboard players operation #0 fTemp /= #2 fTemp
把#0?fTemp的值設為#0 fTemp除以#2 fTemp的值
即 h2 = x2 / x1
execute if score #result fTemp matches ..-1 run scoreboard players operation #0 fTemp *= #-1 fInt
如果得到的數(shù)值為負數(shù),則取相反數(shù)
最后的#0?fTemp就是兩個實體間的距離啦
還可以加個tellraw @a {"score":{"name":"#0","objective":"fTemp"}}測試測試
要是有哪里不理解的可以來私信我
好了以上就是本教程的所有內(nèi)容了,后面真的沒有了,可以退出去了,不過看我寫的這么認真可以給個三聯(lián)或者評論下再退出去嗎~?啊不管給沒給都謝謝啦~