【MCBE·命令】判斷實(shí)體視線是否穿越另一實(shí)體的碰撞箱

前言
本專欄參考自專欄A“【MC】用命令判斷實(shí)體碰撞箱被視線穿過(guò)”。
本專欄基本不包含指令,此處需要的是一個(gè)會(huì)思考的大腦。
本專欄的所述內(nèi)容的本質(zhì)與專欄A的基本一致,但本專欄的可讀性更強(qiáng)。
若要順利閱讀并理解本專欄所述內(nèi)容,至少需要小學(xué)學(xué)歷。
由于本專欄的受眾面要求,因此本專欄總體上較為拖沓。有能力的讀者可以跳讀本專欄。
本文總計(jì)4625字,對(duì)于只有小學(xué)學(xué)歷基礎(chǔ)的讀者,完全閱讀并詳細(xì)理解本專欄預(yù)計(jì)需要23+分鐘。

引入
? 在制作形似于起床戰(zhàn)爭(zhēng)的地圖時(shí),我們通常無(wú)法使用純指令達(dá)到我們想要的一些效果,其中就包括“不允許傷害同隊(duì)玩家”這一效果。那么我們應(yīng)當(dāng)如何解決形似這種問(wèn)題的這一類問(wèn)題呢?
如何解決問(wèn)題?
? 讓我們仔細(xì)思考一下。實(shí)際上“引入”提出的問(wèn)題可以通過(guò)解決“判斷實(shí)體視線是否穿越另一實(shí)體的碰撞箱”這一問(wèn)題來(lái)解決。當(dāng)我們確定同隊(duì)玩家看向了同隊(duì)玩家后(且看向的這個(gè)玩家距離他最近),就可以賦予其高等級(jí)力量效果以達(dá)到不能攻擊的目的(在高等級(jí)力量效果的作用下,被賦予該效果的玩家不能通過(guò)非投擲性物品對(duì)其他實(shí)體造成傷害)。
問(wèn)題的轉(zhuǎn)化·如何解決“判斷實(shí)體視線是否穿越另一實(shí)體的碰撞箱”?
問(wèn)題
??假設(shè)一個(gè)環(huán)境A中僅有2個(gè)物體,它們分別是實(shí)體A和實(shí)體B,那么如何判定實(shí)體A的視線是否穿過(guò)實(shí)體B?
? 要解決這個(gè)問(wèn)題,我們需要構(gòu)造“視線”和“碰撞箱”。
視線的構(gòu)造
? 那么我們應(yīng)當(dāng)如何構(gòu)造這個(gè)“視線”呢?
?在眾多的游戲中,玩家的視線一般是一條射線。我們可以通過(guò)描述玩家(也就是物體)的位置及其朝向(也就是物體看向的方向)來(lái)描述玩家的視線。嘗試想象一下,當(dāng)你看向正北方且平視的時(shí)候,在你的鼻子處(這個(gè)“鼻子處”就是上文提到的“位置”)描一個(gè)點(diǎn)M,然后讓點(diǎn)M向正北方且平視的這個(gè)方向(這個(gè)“正北方且平視的這個(gè)方向”就是上文提到的“朝向”)移動(dòng)無(wú)窮米(也就是∞m),那么這個(gè)點(diǎn)M移動(dòng)的軌跡就是你的視線。
? 在小學(xué)的時(shí)候,我們學(xué)到了如何用形似(a,b)的方式來(lái)表示同學(xué)所坐的座位位置。那么我們是否我們?cè)谟螒蛑袠?gòu)造一個(gè)三維的坐標(biāo)系,然后用3個(gè)值(長(zhǎng)、寬、高)來(lái)表示玩家在游戲(即場(chǎng)景)中的位置呢?答案是——可以!不難發(fā)現(xiàn),MCBE自帶有“坐標(biāo)”的功能,因此我們可以直接使用MCBE內(nèi)置的“坐標(biāo)法則”來(lái)表示一個(gè)實(shí)體所在的位置。關(guān)于如何使用指令在MCBE中獲取實(shí)體的坐標(biāo),詳見“[MCBE]CB:關(guān)于二分法,你了解多少”或“【minecraft命令】運(yùn)用二分法計(jì)算兩點(diǎn)的垂直視角(單擊該鏈接后自動(dòng)跳轉(zhuǎn)到1:55處)”。
? 對(duì)于MCBE中的一般情況,我們用兩個(gè)軸的值來(lái)描述朝向,它們分別是RX和RY,分別對(duì)應(yīng)垂直方向的旋轉(zhuǎn)角和水平方向的旋轉(zhuǎn)角。設(shè)想一下,當(dāng)你左右旋轉(zhuǎn)你的頭的時(shí)候,改變的是你的RY軸值,而當(dāng)你上下旋轉(zhuǎn)旋轉(zhuǎn)你的頭的時(shí)候,改變的便是你的RX軸值。但在解決本專欄所述的問(wèn)題的時(shí)候,我們?yōu)榱撕?jiǎn)便就采取了另一個(gè)方法來(lái)描述物體的朝向——向量。
? 向量不僅存在長(zhǎng)度,還存在方向(如下圖“圖1”)。在MCBE中,運(yùn)用Tp(或Teleport)的Facing(朝向)功能就可以讓目標(biāo)傳送到指定位置A并朝向指定位置B(A和B可以相同)。在這一過(guò)程中,你先被傳送到了指定位置A,然后看向了指定位置B,那么AB就可以稱為一個(gè)向量,它從A點(diǎn)指向B點(diǎn),有長(zhǎng)度且有方向[AB的長(zhǎng)度就是AB的模(數(shù)學(xué)上表示為|AB|,指的是線段AB的長(zhǎng)度);AB的方向可以通過(guò)一個(gè)過(guò)程來(lái)表現(xiàn),而這個(gè)過(guò)程就是“先用直尺連接A點(diǎn)和B點(diǎn),然后把一個(gè)筆的筆尖放在A點(diǎn)處并讓筆尖順著剛剛連接的這個(gè)線滑動(dòng)到B點(diǎn)”的過(guò)程]。在這里,A點(diǎn)可以用坐標(biāo)表示其位置,B點(diǎn)也可以用坐標(biāo)表示其位置,那么我們用2組坐標(biāo)并按照一定順序擺放這兩組坐標(biāo)來(lái)表示一個(gè)方向(見下圖“圖2”)。


? 在MCBE中,玩家的位置就可以看成“圖1”中的A點(diǎn)的坐標(biāo)。我們可以很簡(jiǎn)單的得到玩家的坐標(biāo),但我們?nèi)绾蔚玫搅硪粋€(gè)點(diǎn)B的坐標(biāo)呢?
? 這很簡(jiǎn)單。我們可以使用MCBE內(nèi)置的“局部坐標(biāo)系”的法則來(lái)表示并使用二分法得到點(diǎn)B的坐標(biāo)。要詳細(xì)了解這個(gè)法則及該法則的使用,可以前往“命令 - Minecraft Wiki_BWIKI_嗶哩嗶哩(單擊該鏈接后自動(dòng)跳轉(zhuǎn)到指定位置)”以詳細(xì)了解。我們通常用“^ ^ ^1”來(lái)表示實(shí)體的朝向,因此我們可以使用指令“Execute <玩家> ^ ^ ^1 Summon Minecraft:Armor_Stand”來(lái)在B點(diǎn)處生成一個(gè)盔甲架,然后該盔甲架的位置可以被表示為一組坐標(biāo),于是我們就得到B點(diǎn)的坐標(biāo)了。
碰撞箱的構(gòu)造
? 在MC中的一般情況下,只要是實(shí)體,那么都存在碰撞箱和“相對(duì)坐標(biāo)”*(目前已知的一個(gè)特例是MCJE中的“標(biāo)記”是不存在碰撞箱的)。我們可以把碰撞箱看做一個(gè)長(zhǎng)方體X,然后這個(gè)長(zhǎng)方體有一個(gè)實(shí)體N且這個(gè)實(shí)體被長(zhǎng)方體X完全蓋住。一般來(lái)說(shuō),碰撞箱和“相對(duì)坐標(biāo)”由實(shí)體的模型決定(對(duì)于末影龍和凋零來(lái)說(shuō),它們的碰撞箱不適用該法則),關(guān)于“模型”的詳細(xì)信息,可見“模型 - Minecraft Wiki_BWIKI_嗶哩嗶哩”。
? 我們無(wú)法在MCBE中直接用指令得到碰撞箱,因此我們可以通過(guò)預(yù)先設(shè)置碰撞箱的方法來(lái)構(gòu)造碰撞箱,本專欄預(yù)設(shè)的碰撞箱大小為1*1*2(長(zhǎng)1、寬1、高2;“*”指的是乘號(hào))。
? 什么是“相對(duì)坐標(biāo)”呢?“相對(duì)坐標(biāo)”可以理解為碰撞箱(也就是長(zhǎng)方體)的幾何中心,我們用直尺連接這個(gè)碰撞箱(也就是長(zhǎng)方體)的4個(gè)對(duì)角線,這些對(duì)角線的交點(diǎn)就是這個(gè)長(zhǎng)方體的幾何中心。碰撞箱可以用2組坐標(biāo)表示,例如(x,y,z)和(x',y',z'),那么幾何中心的坐標(biāo)可以表示為下圖“圖3”中所寫的形式。平面中的碰撞箱和幾何中心可以用下圖“圖4”中的例子來(lái)表現(xiàn)。

??

? 碰撞箱的表示方法跟“Fill”命令中起止點(diǎn)坐標(biāo)的表示方法一致,它們都用來(lái)表示一個(gè)長(zhǎng)方體區(qū)域。簡(jiǎn)而言之,這種坐標(biāo)表示方法用到了長(zhǎng)方體1組對(duì)角線的坐標(biāo)。你可以嘗試?yán)斫馍蠄D“圖4”中的(x,y)和(x',y'),這兩組坐標(biāo)就是長(zhǎng)方形的1組對(duì)角線的坐標(biāo)?!癋ill”命令的具體用法可見“命令/fill - Minecraft Wiki_BWIKI_嗶哩嗶哩”,當(dāng)你了解該命令關(guān)于起止點(diǎn)坐標(biāo)的表示方法后,你就會(huì)表示一個(gè)長(zhǎng)方體區(qū)域了。
正式開始解決問(wèn)題
??讓我們重新看到要解決的問(wèn)題——假設(shè)一個(gè)環(huán)境A中僅有2個(gè)物體,它們分別是實(shí)體A和實(shí)體B,那么如何判定實(shí)體A的視線是否穿過(guò)實(shí)體B?
? 我們可以通過(guò)章節(jié)“視線的構(gòu)造”和章節(jié)“碰撞箱的構(gòu)造”分別取得實(shí)體A的視線和實(shí)體B的碰撞箱。此處將實(shí)體A的視線表示為(X,Y,Z)(X',Y',Z'),它對(duì)應(yīng)的方向指的是“從(X,Y,Z)指向(X',Y',Z')”。此處將實(shí)體B的碰撞箱表示為(q,r,s)(q',r',s'),它表示的是一個(gè)長(zhǎng)方體區(qū)域,也就是從(q,r,s)到(q',r',s')間的區(qū)域。
? 我們讓X''的值等于X'減去X的值,Y''的值等于Y'減去Y的值,Z''的值等于Z'減去Z的值,于是我們可以將得到的X''、Y''和Z''表示為一組坐標(biāo),寫為(X'',Y'',Z'')。我們以實(shí)體A為原點(diǎn),構(gòu)建一個(gè)三維坐標(biāo)系,那么該原點(diǎn)指向(X'',Y'',Z'')的方向就是玩家的視線,因此我們可以用(X'',Y'',Z'')來(lái)表示玩家的視線,這樣更加方便。
? 需要注意的是,由于剛剛構(gòu)造了新的坐標(biāo)系,因此我們也需要讓表示碰撞箱的2組坐標(biāo)中的每一個(gè)軸的值減去對(duì)應(yīng)的原點(diǎn)坐標(biāo)中的每一個(gè)軸的值。本處不再贅述如何得到碰撞箱的2組新坐標(biāo),具體的原理與上一段落的處理辦法基本一致。
? 在“視線的構(gòu)造”中,我們學(xué)習(xí)了有關(guān)“向量”的知識(shí),因此我們知道玩家的視線是向量,它有長(zhǎng)度和方向。因此,我們可以在不改變實(shí)體A的視線方向的情況下,只改變實(shí)體A視線的長(zhǎng)度。我們讓X''、Y''和Z''乘上L(L指的是縮放倍數(shù),它可以讓視線的長(zhǎng)度縮小或放大L倍),得到了(LX'',LY'',LZ''),目前假定L是一個(gè)未知數(shù)。需要注意的是,L必須是正數(shù)或0,因?yàn)閷?shí)體A的視線相對(duì)于原點(diǎn)是往前的,不能往后(也就是反方向)。由于原有的“有長(zhǎng)度的視線(X'',Y'',Z'')”被縮放了L倍,因此現(xiàn)在得到的視線(LX'',LY'',LZ'')是一個(gè)長(zhǎng)度未知但是方向已知的視線(你可以把這個(gè)視線想象成一個(gè)在不斷拉伸的細(xì)繩,但這個(gè)細(xì)繩不能拉伸到實(shí)體A視線的負(fù)方向)。
? 為了讓問(wèn)題簡(jiǎn)單容易解決,我們可以將剛剛構(gòu)建好的三維坐標(biāo)系的3個(gè)軸(也就是X軸、Y軸和Z軸)拆開來(lái),先一個(gè)一個(gè)軸來(lái)解決,本專欄解決的是X軸。
? 我們知道實(shí)體B的碰撞箱是從(q,r,s)到(q',r',s'),因此實(shí)體B在X軸上的碰撞箱是從q到q'。如果實(shí)體A的視線穿越了實(shí)體B的碰撞箱,那么實(shí)體A在X軸上的視線LX''一定經(jīng)過(guò)從q到q'這個(gè)范圍(如下圖“圖5”)。

? 也就是說(shuō),如果“實(shí)體A在X軸上的視線LX''經(jīng)過(guò)從q到q'這個(gè)范圍”,那么必定存在至少一個(gè)L讓LX''得到的值在q到q'這個(gè)范圍內(nèi)(如下圖“圖6”)。

? 很顯然,在上圖“圖6”中,L取了一個(gè)值,讓LX''這個(gè)點(diǎn)落在了qq'這一線段上。于是,我們得到了不等式:

? 進(jìn)而得到:

? 目前,X''、q和q'均已知,僅L是一個(gè)未知數(shù),所以我們可以求出L的取值范圍。上圖“圖8”中的不等式指的是L可以取到q÷X''到q'÷X''之間的數(shù)。特別的,如果X''為0,那么L可以取遍數(shù)軸上的任意數(shù)。
? 于是,關(guān)于X軸的解決過(guò)程到此結(jié)束。
? 我們可以使用同樣的方法來(lái)解決Y軸和Z軸,因此此處不再贅述關(guān)于解決Y軸和Z軸的過(guò)程。最終,我們得到了3個(gè)不等式:



? 然后,我們得到了3個(gè)關(guān)于L的范圍。如果這三個(gè)范圍中能取到至少一個(gè)L同時(shí)滿足上圖“圖9”、“圖10”和“圖11”中的3個(gè)不等式,那么就存在一個(gè)L可以讓實(shí)體A的視線(LX'',LY'',LZ'')對(duì)應(yīng)的點(diǎn)[將滿足上述3個(gè)不等式條件的各個(gè)L依次代入坐標(biāo)(LX'',LY'',LZ'')則可以得到至少一組明確的坐標(biāo),而得到的每組明確的坐標(biāo)將對(duì)應(yīng)一個(gè)位于的點(diǎn)]落入實(shí)體B的碰撞箱內(nèi)。
? 換言之,如果找得到一個(gè)L滿足上述3個(gè)不等式,那么實(shí)體A的視線經(jīng)過(guò)實(shí)體B的碰撞箱。
? 于是,問(wèn)題“假設(shè)一個(gè)環(huán)境A中僅有2個(gè)物體,它們分別是實(shí)體A和實(shí)體B,那么如何判定實(shí)體A的視線是否穿過(guò)實(shí)體B?”就得到了解決。
數(shù)學(xué)語(yǔ)言
? 準(zhǔn)備:
? ? 以發(fā)出射線的實(shí)體處建立三維直角坐標(biāo)系.
? 定義:
? ? 1.?(x,y,z)是一個(gè)單位矢量。將該坐標(biāo)放大倍數(shù)n可得到視線上任何一點(diǎn),表示為n(x,y,z).
? ? 2.?長(zhǎng)方體的范圍是(a1,b1,c1)到(a2,b2,c2)且a1≤a2,b1≤b2,c1≤c2.
? 初步:
? ? 用集合表示射線上每一點(diǎn):A={n(x,y,z)|n≥0}.
? ? 用集合表示長(zhǎng)方體內(nèi)每一點(diǎn):B={(a,b,c)|a1≤a≤a2,?b1≤b≤b2,?c1≤c≤c2}.
? 根據(jù)題意可得:
? ? 若視線有一點(diǎn)穿越長(zhǎng)方體,則有A∩B≠?.
? ? 若A∩B≠?,則存在一個(gè)或多個(gè)n可以使(x,y,z)被縮放到視線上一點(diǎn)且該點(diǎn)在長(zhǎng)方體內(nèi),即可得:
? ? 不等式組{a1≤nx≤a2,?b1≤ny≤b2,?c1≤nz≤c2}的解集≠?.
? ? 其中,a1、a2、b1、b2、c1、c2、x、y和z均已知,那么可通過(guò)上述不等式組得到關(guān)于n的解集。如果n在上述不等式組的解集=?,則視線不穿過(guò)長(zhǎng)方體。如果n在上述不等式組的解集≠?,則視線穿過(guò)長(zhǎng)方體.

結(jié)語(yǔ)
? 本專欄講述的解決方法是“向量法”。解決這類問(wèn)題并不局限于本專欄的方法,更多的,是用“智慧的大腦”尋找解決問(wèn)題的關(guān)鍵,得到解決問(wèn)題的方法。
? 為了貫徹這一理念,本專欄基本上不會(huì)出現(xiàn)指令,而是純邏輯的講解。
? 該專欄寫于倉(cāng)促之間,難免存在問(wèn)題,歡迎各位大佬在“勘誤”樓中指出本專欄的錯(cuò)誤。同時(shí)感謝各位讀者花費(fèi)如此長(zhǎng)的時(shí)間閱讀本專欄,臨走前還希望各位能點(diǎn)一個(gè)免費(fèi)的“贊”呢~

注釋
? “相對(duì)坐標(biāo)”:此處及下文提到的“相對(duì)坐標(biāo)”指的是實(shí)體碰撞箱的幾何中心的坐標(biāo),而不是坐標(biāo)中的“~”。