(此篇適用於java版MC 1.12以前的版本)
前提──排序執行的定義:
排序執行的定義上是在同一個執行時間(同一個tick)中,
可以對多個實體有「順序」執行的效果:
可以做出類似多人適用的傳送箭矢(不會受到距離所影響)、
'
(將所有目標進行分數的排序後,依序從實體1、2、3、4、...、8上執行)
或是單純的不同時的輪流施放技能(以一個座標點(位置)做為中心控制順序)
(以紅點為中心,先從實體1執行、再從實體2執行、最後從實體3執行)
而以下要先介紹如何給予實體「順序」,也就是給予分數進行排序:
實體分數排序方式與用途:
是指將多個實體進行編號排列,
像是把10個實體的分數排列為分數1~10之中的一個,
(有點類似於矩陣(Array)的做法:將10個物件各自順序的編號在同個矩陣中)
這很適合用在遊戲結束時的「結算排名」功能,
而我們會將這種分數排序分成兩種方式執行:
- 直接排序實體的分數(即是直接將多個實體進行分數排序)
- 間接排序實體的分數(會利用前提的距離控制順序的方式來處理)
一、直接排序實體分數
我們可以利用execute或是超頻執行(詳見文章:超頻執行指令)的方式,
將多個實體無視順序的排列各自的分數。
(由於execute用法會比較省資源與長度,所以在此只先介紹execute用法,
如果需要超頻執行運作直接排序分數的用法,可以在下方留言讓我知道。)
execute用法示範:
》假設我們要將加入tag為getOrder的玩家進行記分板為order的各自分數排列(由小到大) main/order.mcfunction文件裡面的內容:
》假設有3個玩家A、B、C有tag為getOrder,運行這條指令: execute @a[tag=getOrder] ~ ~ ~ function main:order 而最後會得到的結果為:A、B、C的order分數各自為1、2、3 原理: 我們先慢慢看過每條指令的執行狀況: 1. execute @a[tag=getOrder] ~ ~ ~ function main:order 從有getOrder標籤的各自玩家身上執行main:order, 由於有3個人(A、B、C),所以已知會執行3次main:order 2. execute @a[score_order_min=1] ~ ~ ~ scoreboard players add @p[x=0,y=0,z=0,tag=getOrder] order 1 從order分數大於1的所有玩家給予離中心座標(0,0,0)最近的玩家order分數為1 3. scoreboard players add @p[x=0,y=0,z=0,tag=getOrder] order 1 給予離中心座標(0,0,0)最近的玩家order分數為1 4. scoreboard players tag @p[x=0,y=0,z=0,tag=getOrder] remove getOrder 移除離中心座標(0,0,0)最近的玩家的getOrder標籤 由上述執行狀況可以知道,第2、3、4步驟一共會跑過3次, 再來,你要知道Minecraft的execute和function指令皆存在著一種獨特性: /execute後面的指令跑完前不會繼續跑下一個指令 /function所執行的函數檔案裏面的指令都跑過一次前不會繼續跑下一個指令 而利用這種特性,從A、B、C三者各自身上跑過一次main:order, 會是以A跑過一次main:order後再換B跑過一次main:order後再換C跑過一次main:order, 因此,當A跑過一次main:order時,A的order分數會變成1, 再換B跑過一次main:order時,最後B的order分數會變成2, 最後再換C跑過一次main:order時,而C的order分數會變成3。 》而這種作法有一個很類似的作法可以做出反序排列(由大到小) 假設我們要將加入tag為getOrder的玩家進行記分板為order的各自分數排列 main/order.mcfunction文件裡面的內容:
》假設有3個玩家A、B、C有tag為getOrder,運行這條指令: execute @a[tag=getOrder] ~ ~ ~ function main:order 而最後會得到的結果為:A、B、C的order分數各自為3、2、1 (在這裡感謝貓狗喵的修正) |
二、間接排序實體分數
通常會用在將已經有分數的實體間進行「排名」,
但Minecraft並沒有辦法直接為有分數的多個實體進行分數比對後排名,
所以我們可以利用國中教過的「數線」的方式來解決
我們可以利用二分法(詳見文章:二分法)將實體的分數轉換成位置(數線上的座標)後,
利用前提所提到的「距離排序」來達成讓多個有分數的實體排名的動作,
在這裡同樣使用execute來處理,也是因為超頻執行會比較吃效能的關係
(如果你不想要tp玩家的話,你可以利用盔甲架來幫助你在數線上進行排名的動作,
如果需要這種利用盔甲架為玩家排名的話,可以下方留言讓我知道。)
數線排序法示範:
》假設我們要將time分數為0以上的玩家進行記分板為order的各自分數排列 main/order.mcfunction文件裡面的內容:
》假設有3個玩家A、B、C的time分數為3、15、7,運行函數:function main:order 而最後會得到的結果為:A、B、C的order分數各自為1、3、2 (因為是由0,70,0(原點)開始偵測,所以結果會是以最低分往最高分排列) 原理: 我們先慢慢看過每條指令的執行狀況: (一) main:order內的指令目的是讓玩家傳送到數線上並排列好位置 1. tp @a[score_time_min=0] 0 70 0 將分數time>0將的玩家傳送到0,70,0(數線的原點) 2.tp @a[score_time_min=64] ~0.064 ~ ~ scoreboard players remove @a[score_time_min=64] 64 ... (即是所有灰色為底的指令部分) 進行二分法將實體的time分數轉換成座標在x軸上,直到time分數變成0 3. execute @a[score_time_min=0] ~ ~ ~ function main:order2 從time≥0的所有玩家身上執行main:order2, 由於有3個人(A、B、C),所以已知會執行3次main:order2 (二) main:order2內的指令目的是將數線上的玩家依照距離遠近進行分數排序 4. execute @a[score_order_min=1] ~ ~ ~ scoreboard players add @p[x=0,y=70,z=0,score_time_min=0] order 1 從order分數大於1的所有玩家給予離原點最近的玩家且time≥0的order分數+1 5. scoreboard players add @p[x=0,y=70,z=0,score_time_min=0] order 1 給予離原點最近的玩家且time≥0的order分數+1 6. scoreboard players reset @p[x=0,y=70,z=0,score_time_min=0] time 重置離原點最近的玩家且time≥0的time分數 由上述執行狀況可以知道,第1到第3步是在將實體分布在一條數線上, 會讓A被傳送到x=0.003、B被傳送到x=0.015、C被傳送到x=0.007, 並且重複執行三次order2:第一次將A的order分數為1、第二次會將C的order分數設為2、 第三次則會將B的order分數設為3 (order2內的運算原理請至剛才上方的介紹參閱)。 |
以上是將實體的順序給排序成分數的方法,
以下要介紹如何將這些排序的分數從低到高的依序執行一次指令:
排序執行的處理方式:
前提已經可以大致上的了解排序執行的功能在做什麼了,
接著你要知道execute指令與超頻演算的排序執行處理方法:
execute指令將以排序分數的實體進行排序執行:
》讓A、B、C這三個玩家依次執行指令:say hi (已經先設定A的order分數為1、B的order分數為2、C的order分數為3) main/output.mcfunction文件裡面的內容:
execute @a[score_order_min=1] ~ ~ ~ function main:output 而最後會得到的三條訊息由上而下依次為:[A] hi、[B] hi、[C] hi 原理: 我們先慢慢看過每條指令的執行狀況: 1. execute @a[score_order_min=1] ~ ~ ~ function main:output 從order≥1的玩家執行main:output裡的指令, 由於有3個人(A、B、C),所以已知會執行3次main:output 2. execute @a[score_order_min=1,score_order=1] ~ ~ ~ say hi 從order=1的玩家執行say hi 3. scoreboard players remove @a[score_order_min=1] order 1 從所有order≥1的玩家讓自己的order分數-1 上述執行狀況可以知道,利用execute來起步,是已經確定有order=1~3的實體, 並且把自己的order分數持續減1,直到為0,而當自己order=1的時候就執行一次say hi 》這種利用execute來處理排序執行的優點是在比較省資源,但會有缺點是: 如果其中的順序被打亂的話:假使A玩家離開遊戲了,而剩下玩家B和C, 這會使B只需1tick可以執行say hi,但C要2tick才能執行say hi 是因為執行main:output的次數由3次減少為2次, 因此1tick時的第一次執行output時會沒有人執行say hi,第二次執行時B才成功say hi, 第二次執行完output後的C的order分數會是1, 要等到下一個tick時再執行一次output才會讓C成功say hi。 以及一個共通性錯誤:如果有兩個以上實體分數一樣會導致錯誤 (老實說我並不推薦使用這種方法做排序執行,因為很容易就被打破循環) |
超頻演算將以排序分數的實體進行排序執行:
》讓A、B、C這三個玩家依次執行指令:say hi (已經先設定A的order分數為1、B的order分數為2、C的order分數為3) main/output.mcfunction文件裡面的內容:
》運行函數:function main:output 而最後會得到的三條訊息由上而下依次為:[A] hi、[B] hi、[C] hi 原理: 我們先慢慢看過每條指令的執行狀況: 1. execute @a[score_order_min=1,score_order=1] ~ ~ ~ say hi 從order=1的玩家執行say hi 2. scoreboard players remove @a[score_order_min=1] order 1 從所有order≥1的玩家讓自己的order分數-1 3. function main:output if @a[score_order_min=1] 如果有order≥1的玩家,則重複執行這個函數檔 直到所有玩家的order分數都不≥1為止 上述執行狀況可以知道,這種方式會執行到所有有order分數的玩家的order皆變成0為止, 過程中,當玩家的order=1的時候就會輸出一次指令, 而這種方式不會因為中間缺少某個連貫的數字(像是1、2、5、6)而出錯, 如果有實體的order最高是32的話就會超頻執行這個檔案32次 缺點也就是:當order數值非常大的時候會執行非常多次(也是超頻執行的缺點)、 有兩個實體的order分數一樣的時候會導致處理上的錯誤 (同分的錯誤只能從排序分數的時候或是用其他方式來排除) |
排序執行的使用例子:
『傳送弓(可多人且不被距離所引響)』
先創建一個偵測射出箭矢的記分板以及進行排序處理的記分板:
使用弓:/scoreboard objectives add useBow stat.useItem.minecraft.bow
紀錄你的排序分數:/scoreboard objectives add order dummy
執行你的排序分數:/scoreboard objectives add runorder dummy
以及取得一個傳送弓:
/give @p bow 1 0 {display:{Name:"傳送弓"},bowType:"teleportBow"}
接著持續執行下面這個函數檔內的指令(此函數檔可以隨意命名):
scoreboard players tag @a[score_useBow_min=1] add getOrder {SelectedItem:{id:"minecraft:bow",tag:{bowType:"teleportBow"}}} execute @a[tag=getOrder] ~ ~ ~ function teleportBow:order scoreboard players tag @e[tag=!inGround,score_order_min=1] add inGround {inGround:1b} function teleportBow:output if @e[type=arrow,tag=inGround] kill @e[tag=inGround,type=arrow] scoreboard players reset @a[score_useBow_min=1] useBow |
teleportBow:order函數檔案內的指令:
scoreboard players operation @s order >= @a[score_order_min=1] order scoreboard players add @s order 1 execute @s ~ ~ scoreboard players operation @s order = @e[c=1,type=arrow] order scoreboard players tag @s remove getOrder |
teleportBow:output函數檔案內的指令:
execute @e[score_order_min=1] ~ ~ ~ scoreboard players operation @s runorder = @s order function main:output |
teleportBow:loop函數檔案內的指令:
execute @e[score_runorder_min=1,score_runorder=1,type=arrow,tag=inGround] ~ ~ ~ teleport @a[score_runorder_min=1,score_runorder=1] ~ ~ ~ execute @e[score_runorder_min=1,score_runorder=1,type=arrow,tag=inGround] ~ ~ ~ scoreboard players set @a[score_runorder_min=1,score_runorder=1,c=1] order 0 scoreboard players remove @e[score_runorder_min=1] runorder 1 function main:output if @e[score_runorder_min=1] |
》這個系統的一些小特點:
1.當你射出的箭矢並沒有落地的話,order分數會維持著,但並不引響下次系統的執行
(我並沒有針對擊中生物的處理,所以這個特殊效果是故意的)
2.order函數檔的第一條指令與文章內講的方式不同(我不是以累加,而是取大於)
(取大於是一個比用累加還有安全性的方法,可以排除一開始在分配分數時會有同分的問題)
3.使用runorder記分板來處理排序執行
(每次有箭矢落地時必須要跑過每個order,並且會使order變成0,故箭矢落地時讓runorder為
order的數值並代替上述的執行效果)
4.當你很快速的射出兩支箭矢時,你會被傳送到第二支箭矢的位置,而不是第一支
(由於只要射出箭矢order分數就會+1的關係,因此第一支的箭矢的order與玩家order不一樣而
不會傳送)
(有關這個機關的運作方式是怎麼運作的你們可以想一想,如果想不出來再問我)