第六十九章 修飾子命令 上 三要素與修飾子命令
?。ㄓ?022/7/14重寫)
在第七章,我們總結(jié)出了指令執(zhí)行的三要素:執(zhí)行者、執(zhí)行位置和參數(shù)。
但在經(jīng)過了六十幾章的學(xué)習(xí)后,我們會發(fā)現(xiàn),這三要素似乎有點不對?。?p> 哪里不對呢?
執(zhí)行者肯定會有,就算沒有也會有默認值;執(zhí)行位置肯定也會有,并且它照樣也有默認值。
但參數(shù)呢?
雖然有些參數(shù)也有默認值,但有些指令它根本就沒有參數(shù)啊!比如/seed指令,根本就不需要參數(shù)。
所以在這,我們必須糾正一個已經(jīng)持續(xù)了幾十個章節(jié)的錯誤,那就是:
參數(shù)并不是指令必須要有的東西,它不算是指令執(zhí)行的要素。
那照這樣子說,我們的三要素是不是就要變?yōu)槎亓耍?p> NONONONO。三要素還是三要素,雖然參數(shù)不是了,但有一個一直以來都在被我們忽略的東西能夠被當作是指令執(zhí)行的三要素。
什么呢?難不成是目標選擇器的基準點?
答案是:執(zhí)行朝向。
執(zhí)行朝向是一個神奇的東西。我們第一次遇見它在第九章,花了較長的篇幅講解了水平旋轉(zhuǎn)角度和垂直旋轉(zhuǎn)角度;第二次遇見它在第二十六章,又講了一遍水平旋轉(zhuǎn)角度;第三次遇見在第六十四章,花了較長的篇幅講解了相對旋轉(zhuǎn)角度和基準部位;接下來,我們可能還要再次遇見它,講解它和局部坐標的關(guān)系。
不難發(fā)現(xiàn),相對于執(zhí)行位置、執(zhí)行者,我們卻很少遇見執(zhí)行朝向。但每次執(zhí)行朝向一出現(xiàn),我們就得花大量的筆墨去講解它。所以說,執(zhí)行朝向是指令執(zhí)行三要素中最難理解的部分,它不僅僅只是一個朝向那么簡單,它還關(guān)系到許許多多的東西。
現(xiàn)在,我們總結(jié)出了新的指令執(zhí)行三要素:執(zhí)行者、執(zhí)行位置和執(zhí)行朝向。其中,執(zhí)行位置還包括維度和坐標;執(zhí)行朝向包括旋轉(zhuǎn)角度和基準部位。而這些東西,我們都可以通過新版execute的8條修飾子命令來自定義:
as ——更改執(zhí)行者
at ——更改執(zhí)行位置(包括維度和坐標)和旋轉(zhuǎn)角度為指定實體的位置和旋轉(zhuǎn)角度
facing ——更改旋轉(zhuǎn)角度
rotated——更改旋轉(zhuǎn)角度
anchored——更改基準部位
positioned ——更改執(zhí)行位置中的坐標
in ——更改維度位置
align——將執(zhí)行位置中的坐標轉(zhuǎn)化為方塊坐標
這一章,我們就先講幾個簡單的:as、at和in。
as子命令可以更改執(zhí)行者,類似于舊版的『執(zhí)行者』參數(shù),但其僅僅只會更改執(zhí)行者,而舊版的『執(zhí)行者』參數(shù)還會更改旋轉(zhuǎn)角度。
as子命令的使用十分簡單,如下:
... as <執(zhí)行者:目標選擇器>...
舉個例子:
/execute as @a run 一條指令
(run子命令雖然我們沒有講過,但其實你應(yīng)該也懂了,『run +命令』就可以運行指令)
這將會將指令執(zhí)行者更改為所有玩家自身,指令在運行時會分別將每個玩家作為執(zhí)行者運行一遍。唉,什么叫『指令在運行時會分別將每個玩家作為執(zhí)行者運行一遍』呢?,之前講舊版的execute時可從未提到過?。?p> 舉個小小的例子,你就知道了:
/execute as @e[type=villager] run scoreboard players add villager_count an_objective 1
假設(shè)villager_count這個假玩家在計分項an_objective上的分數(shù)原先為0。請你猜一猜,運行這條指令過后,分數(shù)將會變?yōu)槎嗌伲恳欢〞優(yōu)?嗎?
上面這條指令的意思是:將所有村民作為執(zhí)行者,給villager_count在計分項an_objective上的分數(shù)加上1。而指令在運行過程中,會將每位村民都作為執(zhí)行者運行一遍指令,因此最終指令將會運行『村民總數(shù)量』遍,我們也成功地將村民的總數(shù)量作為分數(shù)記到了計分板上。假設(shè)現(xiàn)在有兩名村民,最終得到的分數(shù)將會是2,因為這兩個村民都分別運行了一遍『scoreboard players add villager_count an_objective 1』。
為什么指令在運行過程中,會將每位村民都作為執(zhí)行者運行一遍指令,而不是直接將所有村民作為執(zhí)行者,僅僅運行一遍指令?
原因很簡單:每條指令執(zhí)行過程中,執(zhí)行者、執(zhí)行位置、執(zhí)行朝向分別都只能有一個。
請你一定要記住這個知識點,因為接下來我們將會需要運用這個知識點來解決一些問題。
對了,既然新版本的execute有這個特性,那舊版本呢?
也是有的。
所以你能想到這有什么用處嗎?
testfor指令雖然可以用來探測指定實體是否存在,但想要將數(shù)量儲存到計分板中還需要一個/stats指令,十分麻煩且容易出錯。而execute如果這樣用,直接吊打testfor??!而且,這是可以用于基巖版的,做玩家人數(shù)檢測或?qū)嶓w數(shù)量檢測!
比如在基巖版,你可以這么干:
/execute @a[r=10,x=15,y=94,z=20,tag=游戲參與者]~~~ scoreboard players add player_count count 1
這將會計算出以(15,94,20)為中心,半徑10米內(nèi)帶有『游戲參與者』標簽的玩家數(shù)量,非常適合用于小游戲的人數(shù)判定。
回到正題啊,接下來我們來看看at子命令。
at子命令,可以更改執(zhí)行位置中的維度位置、坐標以及執(zhí)行朝向中的旋轉(zhuǎn)角度為指定實體的維度位置、坐標和旋轉(zhuǎn)角度。其格式也是十分滴簡單:
... at <實體:目標選擇器>...
舉個例子:
/execute at @e[type=minecraft:arrow] run summon villager ~~~
這將會在每支箭的位置,分別運行一遍『summon villager ~~~』來召喚一只村民。
可以發(fā)現(xiàn),雖然我們沒有更改執(zhí)行者,但相對坐標的基準點卻被更改了,這說明相對坐標默認不是將執(zhí)行者的位置作為基準點的,而是將指令執(zhí)行位置作為基準點。其實這個我們在第五章就已經(jīng)提到了,這里只不過再提一下而已。
另外,你有沒有注意到『分別』兩個字?也就是說,at子命令照樣會使得指令在每個位置分別運行一遍。不要小看這一點,待會你就會知道小看這一點會導(dǎo)致什么嚴重的災(zāi)難。
in可以更改指令執(zhí)行的維度位置,因此我們可以通過in指令來快速前往其他維度。其語法如下:
... in <維度ID>...
舉個例子:
/execute in minecraft:the_nether run tp @s ~~~
這將會把你傳送到地獄。需要注意的是,你的x和z坐標可能會被除以8。比如你原先的坐標是(16,80,16),傳送到下界后估計就會變成(2,80,2)。
為什么呢?如果你有一點MC常識的話,你就會知道,在大多數(shù)Minecraft版本中,下界中的1米相當于主世界的8米,所以自然而然,你從主世界傳送到下界,x和z軸就會除以8(可以通過添加其他子命令來避免這種情況)。
但在Java稍微舊一些的版本中,運行上述指令并不會改變你的坐標,也就是說不會除以8,還是會保留你原先的(16,80,16)。這是作者在1.14.4版本中發(fā)現(xiàn)的,但在最新的1.19版本中不會出現(xiàn)這種情況,因此應(yīng)該是在1.14.4~1.19間的某個版本修復(fù)了這個BUG。作者猜測可能是1.16更新,只不過懶得去測試了。
另外還需要注意,在1.18及更高版本中,雖然主世界的Y坐標向下延伸到了-64,但其他維度并未變化,因此如果你從Y坐標小于0的地方直接傳到地獄或其他維度,極有可能會掉進虛空!
現(xiàn)在我們來嘗試一下結(jié)合上面的三個子命令+run子命令,看看當多條子命令結(jié)合起來時會有什么效果。
我們來試著將所有實體往上抬1米:
/execute as @e at @e run tp @s ~~1 ~
運行之后,你會驚喜的發(fā)現(xiàn),所有實體都往上抬了1米,但也都傳到了同一個地方,甚至有些實體還被擠死了。
這是怎么回事?
這就是為什么我在前面一直提醒著你說at和as都會在每一個實體和每個位置運行一遍指令。
讓我們來逐步分析一下上面的指令出了什么bug,也就是模擬一下游戲運行這條指令的過程。
假設(shè)現(xiàn)在整個世界僅僅只有3個實體:一名玩家(最早生成),一位村民和一只羊(最晚生成)。
當這名玩家敲下回車鍵將指令發(fā)送給游戲時,游戲開始對這條指令進行解析并運行:
as子命令讓游戲知道,接下來要改變執(zhí)行者。而目標選擇器@e選擇了這三個實體,并使得它們按照生成時間的早晚排序,因此游戲根據(jù)as @e,先選擇了這名玩家作為執(zhí)行者,然后再選擇村民,最后選擇羊。
接下來游戲看到了at @e,由于之前已經(jīng)選擇好了執(zhí)行者,所以游戲根據(jù)上文還有這里,得知這名玩家將會依次在玩家的位置、村民的位置和羊的位置分別運行一遍指令,村民和羊同理。
最后游戲看到了run以及后面的指令,它已經(jīng)完全明白接下來要干什么事情了:
?、賹⑼婕覀魉椭镣婕疑戏?米的位置(玩家此時抬高了1米)
②將玩家傳送至村民上方1米的位置(玩家此時位于村民上方1米)
?、蹖⑼婕覀魉椭裂蛏戏?米的位置(玩家此時位于羊上方1米)
④將村民傳送至玩家上方1米的位置(村民此時位于玩家原本位置上方1米,玩家此時位于羊上方1米)
?、輰⒋迕駛魉椭链迕裆戏?米的位置(村民此時位于村民原本位置上方1米,玩家此時位于羊上方1米)
?、迣⒋迕駛魉椭裂蛏戏?米的位置(村民和玩家此時位于羊上方1米)
⑦將羊傳送至玩家上方1米的位置(村民和玩家此時位于羊原本位置上方1米,羊位于玩家原本位置上方1米)
⑧將羊傳送至村民上方1米的位置(村民和玩家此時位于羊原本位置上方1米,羊位于村民原本位置上方1米)
?、釋⒀騻魉椭裂蛏戏?米的位置(村民、玩家和羊此時都位于羊原本位置上方1米)
最終,三者都跑到了羊原先位置上面1米處,指令運行了3×3=9遍。
太離譜了是不是?但既然寫兩個@e會造成混亂的話,那么該怎樣寫呢?
因為第一個as子命令已經(jīng)更改了執(zhí)行者,后面的子命令都會按照更改后的來,所以我們只需要:
/execute as @e at @s run tp @s ~~1 ~
將at的@e改為@s即可。
或者說把at放前面,曲線救國一下:
/execute at @e as @e[limit=1,sort=nearest] run tp @s ~~1 ~
這也是可以的。
所以在execute中,各個子命令的順序十分重要,每個子命令都會影響到后面的子命令。希望你能記住這一點,不然可能會犯一些就像上面這樣的嚴重錯誤。
我們再來試試結(jié)合in和at兩個子命令,看看當兩個子命令所影響的范圍有重合時會發(fā)生什么:
/execute at @s in minecraft:the_nether run tp @s ~~~
/execute in minecraft:the_nether at @s run tp @s ~~~
你可以猜一猜,當你運行這兩條指令時,效果分別是怎樣的?
首先,第一條指令的效果和之前『/execute in minecraft:the_nether run tp @s ~~~』的效果不能說十分相似,只能說是完全一樣。at子命令將執(zhí)行位置和朝向設(shè)定為了你的位置和朝向,但由于本來就是這樣所以可以去掉。in雖然僅僅會影響到維度,但如果像是地獄這種特殊的維度,在影響到維度位置的同時,in也會對當前的坐標進行設(shè)置。所以說是完全一樣,并不會說你這樣設(shè)置可以將你傳到地獄而不改變坐標的。
而第二條指令就更不用說了,in剛剛把維度改過去,at又改了回來,所以第二條子命令相當于『/tp @s ~~~』,把自己傳送到自己的位置,實際上沒有任何效果。
所以說,execute就像是個流水線,指令的三要素從指令開頭出發(fā),依次經(jīng)過各個修飾子命令的洗禮,最終在run子命令『出廠』。采用這種『流水線思維』,我們才能更好地理解更加復(fù)雜的execute命令。
本章到此結(jié)束。