《我的世界》1.12版本新增了函數(shù)命令這一內(nèi)容,很多玩家覺(jué)得非常復(fù)雜,也有玩家發(fā)現(xiàn)這個(gè)命令可以完全脫離命令方塊而存在,那么下面小編就為大家?guī)?lái)《我的世界》1.12函數(shù)命令系統(tǒng)入門(mén)教程,告訴大家函數(shù)命令怎么用,希望大家喜歡。
寫(xiě)在前面的話
1.9的更新為我們帶來(lái)了三色命令方塊,讓命令方塊脫離了紅石成為獨(dú)立的體系,我們因此可以更好實(shí)現(xiàn)一些想法;時(shí)隔3個(gè)版本,MOJANG再次為我們帶來(lái)驚人的變革。
1.12中,函數(shù)與進(jìn)度系統(tǒng)的出現(xiàn),讓命令脫離命令方塊——這句曾經(jīng)說(shuō)過(guò)的玩笑般的預(yù)言,正式成為可能。
函數(shù)系統(tǒng)的構(gòu)成
函數(shù)系統(tǒng)的由來(lái)
函數(shù)(function)系統(tǒng),是 MC 1.12 Pre-1 版本中新增的一個(gè)功能,它將原來(lái)進(jìn)度系統(tǒng)中返回指令的部分單獨(dú)提取出來(lái),做成了現(xiàn)在的函數(shù)系統(tǒng)。
函數(shù)系統(tǒng)的形式
函數(shù)系統(tǒng)由命名空間和函數(shù)文件組成,這些文件保存在存檔目錄/data/functions/下。functions目錄下的文件夾,稱為命名空間,各個(gè)命名空間下存放不同的函數(shù)文件。實(shí)際上,命名空間就是方便我們編寫(xiě)者分類并管理各種函數(shù)文件。
函數(shù)文件是以.mcfunction為后綴名的文本文件,建議采用utf-8無(wú)BOM編碼以防顯示錯(cuò)亂。簡(jiǎn)單來(lái)講,一個(gè)函數(shù)等價(jià)于一個(gè)多行命令方塊,函數(shù)文件里面每一行寫(xiě)一條指令,當(dāng)執(zhí)行這個(gè)函數(shù)時(shí),里面的指令會(huì)按行依次執(zhí)行。如果在一個(gè)函數(shù)中調(diào)用其它函數(shù),那么在同一游戲刻,被調(diào)用的函數(shù)中所有指令先執(zhí)行完,再繼續(xù)當(dāng)前函數(shù)中后續(xù)的指令,就像插隊(duì)一樣,我們?cè)诤竺鎸?duì)比命令方塊時(shí)還會(huì)說(shuō)到這個(gè)。
請(qǐng)注意:在 1.12 Pre-3 版本中存在一個(gè)嚴(yán)重漏洞,即命令執(zhí)行體不能正確地通過(guò)execute傳遞到被調(diào)用的函數(shù)中去,這個(gè)漏洞有望在后續(xù)版本以及正式版修復(fù)。
以下是本文用到的一個(gè)函數(shù)系統(tǒng)的目錄,帶有"+"的表示為目錄
+ functions + say hi.mcfunction bye.mcfunction Text1.mcfunction text2.mcfunction + system + process _process.mcfunction _main.mcfunction player_tick.mcfunction
如何調(diào)用函數(shù)
在 1.12 中,MOJANG新增了function指令和一條名為gameLoopFunction的游戲規(guī)則來(lái)輔助我們使用函數(shù)系統(tǒng)。function指令的格式如下:
function <命名空間:函數(shù)名> function <命名空間:函數(shù)名> <if|unless> <選擇器>
這兩條都是可行的。其中,if|unless是在1.12 pre-4加入的功能,后面我會(huì)解釋到這個(gè)。我們先來(lái)說(shuō)說(shuō)第一種形式。例如上面的目錄中,要調(diào)用system這個(gè)命名空間下的_main文件,就是輸入這樣的指令:
function system:_main
現(xiàn)在,我們來(lái)看一個(gè)例子例如say命名空間下的Text1.mcfunction和text2.mcfunction,里面分別寫(xiě)上這些內(nèi)容
Text1.mcfunction
#這是一個(gè)范例,在function文件中可以用#來(lái)注釋行。請(qǐng)注意,不能夠使用//來(lái)注釋! say 1 function say:text2 say 2
text2.mcfunction
say 3 say 4
當(dāng)我在系統(tǒng)后臺(tái)輸入function say:text1時(shí),聊天框會(huì)出現(xiàn)這些內(nèi)容: [server] 1 [server] 3 [server] 4 [server] 2
也就是說(shuō),執(zhí)行function指令的人,會(huì)把函數(shù)里面的指令依次執(zhí)行——我在系統(tǒng)后臺(tái)輸入function指令,就是系統(tǒng)在執(zhí)行,我自己輸入function指令,就是我本人在執(zhí)行。大家可能注意到了,函數(shù)中支持使用#進(jìn)行注釋(舊版本支持//注釋,當(dāng)前版本已經(jīng)不再支持),也就是說(shuō)被注釋行不會(huì)作為指令而執(zhí)行,這一點(diǎn)有多方便相比不比我再說(shuō)了。同時(shí)需要大家注意:函數(shù)中所有指令不能夠以/開(kāi)頭。例如,你可以這樣寫(xiě):
say @s
但是不能這樣寫(xiě):
/say @s
最后有一點(diǎn)需要注意的是,在function指令中調(diào)用函數(shù)時(shí),不區(qū)分大小寫(xiě)。例如前面say命名空間下的Text1.mcfunction,我在調(diào)用的時(shí)候?qū)懙氖莝ay:text1
然后是第二種形式,也就是帶有if|unless的。我簡(jiǎn)單舉兩個(gè)例子,大家就知道是什么意思了。
say:tellraw.mcfunction scoreboard objectives add timer dummy 計(jì)時(shí)器 scoreboard players add @s timer 1 function random:title if @s[score_timer_min=1200] scoreboard players reset @s[score_timer_min=1200] timer
random:title.mcfunction summon area_effect_cloud ~ ~ ~ {Tags:["rnd_title","rnd_title1"]} summon area_effect_cloud ~ ~ ~ {Tags:["rnd_title","rnd_title2"]} summon area_effect_cloud ~ ~ ~ {Tags:["rnd_title","rnd_title3"]} summon area_effect_cloud ~ ~ ~ {Tags:["rnd_title","rnd_title4"]} summon area_effect_cloud ~ ~ ~ {Tags:["rnd_title","rnd_title5"]} entitydata @r[r=0,type=area_effect_cloud,tag=rnd_title] {CustomName:"rnd_title"} execute @e[name=rnd_title,tag=rnd_title1] ~ ~ ~ tellraw @a[r=0,c=1] ["1"] execute @e[name=rnd_title,tag=rnd_title2] ~ ~ ~ tellraw @a[r=0,c=1] ["2"] execute @e[name=rnd_title,tag=rnd_title3] ~ ~ ~ tellraw @a[r=0,c=1] ["3"] execute @e[name=rnd_title,tag=rnd_title4] ~ ~ ~ tellraw @a[r=0,c=1] ["4"] execute @e[name=rnd_title,tag=rnd_title5] ~ ~ ~ tellraw @a[r=0,c=1] ["5"]
將say:tellraw放到主進(jìn)程中
execute @a ~ ~ ~ function say:tellraw
則每位玩家每分鐘將會(huì)看到1~5中隨機(jī)一個(gè)數(shù)字出現(xiàn)在聊天框。也就是說(shuō),只有計(jì)時(shí)器分?jǐn)?shù)滿1200的人會(huì)執(zhí)行后面的隨機(jī)部分。那么很顯然,帶有if的意思就是,如果能找到后面的選擇器,就執(zhí)行這個(gè)函數(shù),否則不執(zhí)行。相當(dāng)于testfor。
那么unless的意思也就很明顯了:在找不到后面的選擇器的時(shí)候,執(zhí)行這個(gè)函數(shù),相當(dāng)于testfor+非門(mén)。
gameLoopFunction
講完調(diào)用,就該講講高頻了。玩命令方塊的人都知道高頻是實(shí)現(xiàn)許多功能的前提。在函數(shù)系統(tǒng)中,MOJANG 為我們提供了一條名為gameLoopFunction的游戲規(guī)則來(lái)實(shí)現(xiàn)高頻。它的格式是
gamerule gameLoopFunction <命名空間:函數(shù)>
也就是說(shuō),你可以指定一個(gè)函數(shù)來(lái)高頻執(zhí)行,這個(gè)高頻是20Hz的,也就是每一個(gè)游戲刻都會(huì)執(zhí)行一遍。新建的存檔如果沒(méi)有執(zhí)行過(guò)這條指令,而是用gamerule gameLoopFunction來(lái)查詢的話,得到的返回值是-
為了方便,我們將這個(gè)規(guī)則簡(jiǎn)稱為glf。在舊版本中,glf指定的函數(shù),由系統(tǒng)(server)作為執(zhí)行體;而在新的版本中,MOJANG 引入了虛擬執(zhí)行體,例如將 say:text2 指定為glf時(shí),每一個(gè)游戲刻得到的結(jié)果是這樣的
[say:text2] 3 [say:text2] 4
也就是說(shuō),系統(tǒng)不再作為執(zhí)行體,而是由虛擬的執(zhí)行體代為執(zhí)行。
關(guān)于 glf 多說(shuō)兩句。使用 glf 去高頻執(zhí)行一個(gè)函數(shù),和使用 RCB(循環(huán)型命令方塊,紫色那種)去執(zhí)行,是不一樣的。區(qū)別主要在于其更新順序先后。一般而言不會(huì)造成嚴(yán)重影響,但是在某些情況會(huì)不一樣。比如,使用 CB 能檢測(cè)到生物的{HurtTime:10s}這個(gè) NBT,而使用 glf 執(zhí)行函數(shù)只能檢測(cè)到的是{HurtTime:9s},檢測(cè)不到10,這是因?yàn)殛P(guān)于函數(shù)的更新,都放在了生物更新之后,而 CB 的更新則是在生物更新之前。詳情可以看這里。按照 Searge 的說(shuō)法,函數(shù)并不是命令方塊的完全替代。這個(gè)說(shuō)法,大家就見(jiàn)仁見(jiàn)智了。對(duì)我個(gè)人而言這個(gè)影響不大。
以上是函數(shù)系統(tǒng)的相關(guān)構(gòu)成,以及如何調(diào)用函數(shù)。接下來(lái)我們來(lái)了解一下函數(shù)系統(tǒng)的模塊分類。
函數(shù)系統(tǒng)的模塊調(diào)用
對(duì)于一個(gè)完整的命令系統(tǒng)而言,模塊一般可以分為三類:對(duì)執(zhí)行順序先后有要求的高頻模塊、對(duì)執(zhí)行順序先后無(wú)要求的高頻模塊、非高頻模塊。在函數(shù)系統(tǒng)中,我們同樣可以將模塊分成這三類。為了方便后續(xù)講解。我們作這樣的設(shè)定:
將 system:_main設(shè)為 glf ,并稱之為主進(jìn)程或者主時(shí)鐘
對(duì)于上面講到的三類模塊,我們通過(guò)三種不同的方式去調(diào)用。
對(duì)執(zhí)行順序先后有要求的高頻模塊,在主進(jìn)程中按照需要的順序排列好來(lái)調(diào)用。對(duì)執(zhí)行順序先后沒(méi)有要求的高頻模塊,在主進(jìn)程中可以比較隨意放置位置,但是一般不會(huì)考慮優(yōu)先執(zhí)行。特別地,如果這個(gè)模塊是針對(duì)每一個(gè)玩家獨(dú)立執(zhí)行的,可以使用進(jìn)度系統(tǒng)中的"tick"觸發(fā)器來(lái)調(diào)用,而不需要放在主進(jìn)程中。僅在特定情況下觸發(fā)的非高頻模塊,在主進(jìn)程中調(diào)用,但是輔以execute、scoreboard和選擇器參數(shù)去控制其在合適的時(shí)候被調(diào)用,這里的選擇器,包括了在1.12 pre-4中新增的if/unless的部分。
非高頻模塊在特定條件下激活,也在很大程度上減少了模塊中大量重復(fù)出現(xiàn)execute的現(xiàn)象,并完全杜絕了超長(zhǎng)的Conditional鏈,因?yàn)閒unction中并不直接支持Conditional。不直接支持,說(shuō)明可以間接支持,對(duì)吧。我們來(lái)看一個(gè)例子。
假設(shè)有紅藍(lán)兩隊(duì),在開(kāi)始前考慮到互毆問(wèn)題不進(jìn)行分隊(duì),而是采用掛tag的方式。 紅隊(duì)以tag=redTeam為標(biāo)記,藍(lán)隊(duì)則以tag=blueTeam為標(biāo)記,準(zhǔn)備觀戰(zhàn)的玩家以tag=specTeam為標(biāo)記 當(dāng)玩家站在相應(yīng)區(qū)域(紅藍(lán)兩隊(duì)的所有玩家還需要選擇了職業(yè))添加Ready的標(biāo)記,視為準(zhǔn)備就緒。 如果玩家不在相應(yīng)區(qū)域時(shí)就移除Ready的標(biāo)記。 選擇了職業(yè)的玩家,其記分板項(xiàng)selectClass數(shù)值大于等于1 全部玩家準(zhǔn)備就緒后,游戲進(jìn)入倒計(jì)時(shí),倒計(jì)時(shí)結(jié)束時(shí)游戲開(kāi)始 倒計(jì)時(shí)未結(jié)束,有玩家脫離準(zhǔn)備就緒的狀態(tài),則倒計(jì)時(shí)中斷
條件比較多,我們先來(lái)看看怎么寫(xiě)這個(gè)模塊,再進(jìn)行分析。在這里,我們準(zhǔn)備了一個(gè)名為gameStat的aec實(shí)體作為標(biāo)記,所有游戲進(jìn)程會(huì)以tag或者score的形式掛載到該實(shí)體上。請(qǐng)看指令部分
execute @p[tag=redTeam,score_selectClass_min=1] ~ ~ ~ execute @p[tag=blueTeam,score_selectClass_min=1] ~ ~ ~ scoreboard players tag @e[type=area_effect_cloud,name=gameStat,tag=notGaming] add allReady execute @p[tag=!Ready,m=2] ~ ~ ~ scoreboard players tag @e[name=gameStat,type=area_effect_cloud,tag=notGaming] remove allReady execute @p[tag=!Ready,m=2] ~ ~ ~ execute @e[name=gameStat,type=area_effect_cloud,tag=notGaming] ~ ~ ~ execute @s[tag=!allReady,score_waitTime_min=1] ~ ~ ~ title @a clear execute @p[tag=!Ready,m=2] ~ ~ ~ execute @e[name=gameStat,type=area_effect_cloud,tag=notGaming] ~ ~ ~ execute @s[tag=!allReady,score_waitTime_min=1] ~ ~ ~ title @a reset execute @p[tag=!Ready,m=2] ~ ~ ~ execute @e[name=gameStat,type=area_effect_cloud,tag=notGaming] ~ ~ ~ scoreboard players reset @s[tag=!allReady] waitTime scoreboard players add @e[name=gameStat,tag=allReady] waitTime 1 execute @e[name=gameStat,score_waitTime=1,score_waitTime_min=1] ~ ~ ~ title @a times 10 140 10 execute @e[name=gameStat,score_waitTime=1,score_waitTime_min=1] ~ ~ ~ title @a subtitle [{"color":"aqua","text":"請(qǐng)玩家站在準(zhǔn)備區(qū)域不要離開(kāi)"}] execute @e[name=gameStat,score_waitTime=1,score_waitTime_min=1] ~ ~ ~ title @a title [{"color":"yellow","text":"游戲即將開(kāi)始"}] execute @e[name=gameStat,score_waitTime=40,score_waitTime_min=40] ~ ~ ~ title @a subtitle [{"color":"aqua","text":"3"}] execute @e[name=gameStat,score_waitTime=40,score_waitTime_min=40] ~ ~ ~ execute @a ~ ~ ~ playsound block.note.pling voice @p ~ ~ ~ 1 0 execute @e[name=gameStat,score_waitTime=60,score_waitTime_min=60] ~ ~ ~ title @a subtitle [{"color":"aqua","text":"2"}] execute @e[name=gameStat,score_waitTime=60,score_waitTime_min=60] ~ ~ ~ execute @a ~ ~ ~ playsound block.note.pling voice @p ~ ~ ~ 1 0 execute @e[name=gameStat,score_waitTime=80,score_waitTime_min=80] ~ ~ ~ title @a subtitle [{"color":"aqua","text":"1"}] execute @e[name=gameStat,score_waitTime=80,score_waitTime_min=80] ~ ~ ~ execute @a ~ ~ ~ playsound block.note.pling voice @p ~ ~ ~ 1 0 execute @e[name=gameStat,score_waitTime_min=100] ~ ~ ~ title @a times 10 30 10 execute @e[name=gameStat,score_waitTime_min=100] ~ ~ ~ title @a title [{"color":"gold","text":"游戲開(kāi)始"}] scoreboard players set @e[name=gameStat,type=area_effect_cloud,score_waitTime_min=100] gameStat 1 scoreboard players reset @e[name=gameStat,score_gameStat_min=1,score_gameStat=1] waitTime scoreboard players tag @e[name=gameStat,score_gameStat_min=1,score_gameStat=1] remove allReady execute @e[type=area_effect_cloud,name=gameStat,score_gameStat_min=1,score_gameStat=1] ~ ~ ~ function system:StartGame
接下來(lái)我們來(lái)慢慢分析。
首先是開(kāi)始的條件。有紅藍(lán)兩隊(duì),那么這兩隊(duì)都肯定需要有人,才能夠開(kāi)始,考慮到同一選擇器中不能重復(fù)使用tag的參數(shù),我們保留了區(qū)分隊(duì)伍的參數(shù),而不是區(qū)分是否準(zhǔn)備就緒的參數(shù)。因此,第一條指令的意思是,當(dāng)存在選了職業(yè)并選紅隊(duì)的玩家以及選了職業(yè)并選藍(lán)隊(duì)的玩家,我們給中心實(shí)體加上allReady這個(gè)標(biāo)記,以表明可能滿足開(kāi)始條件。
至于滿足條件嗎?如果有未準(zhǔn)備就緒的玩家,就說(shuō)明不滿足,那我們就讓一個(gè)沒(méi)有準(zhǔn)備就緒的玩家來(lái)去掉allReady這個(gè)標(biāo)記好了。
對(duì)于3~5行,我們放后面點(diǎn)講。先看后面。滿足開(kāi)始條件以后,我們會(huì)給中心實(shí)體加分(使用waitTime這個(gè)記分板項(xiàng)),在第一刻加分后出現(xiàn)提示文字提示準(zhǔn)備開(kāi)始,然后進(jìn)入循環(huán)計(jì)時(shí),最后計(jì)時(shí)滿了,調(diào)用system:startgame這個(gè)函數(shù)來(lái)開(kāi)始游戲(這里不是例子的部分,不作說(shuō)明)。
那么回過(guò)頭來(lái)看3~5行,這里明顯是打斷的部分。打斷,就是要清掉提示文字、重置計(jì)時(shí)器。如果此時(shí)都還沒(méi)有進(jìn)行過(guò)加分,那么我們就不必進(jìn)行那三條指令,因此可以看到中間有個(gè)選擇器里有score_waitTime_min=1的參數(shù)加以限制。
重點(diǎn)來(lái)了,我們看到這3條指令前面相當(dāng)長(zhǎng)一串execute是重復(fù)的。因?yàn)樵谝郧坝胏b寫(xiě)的時(shí)候,這里我使用了Conditional,而現(xiàn)在函數(shù)不直接支持Conditional,所以我用了一大堆execute,但是這里我們可以稍作修改,對(duì)不對(duì)?請(qǐng)看下面
execute @p[tag=redTeam,score_selectClass_min=1] ~ ~ ~ execute @p[tag=blueTeam,score_selectClass_min=1] ~ ~ ~ scoreboard players tag @e[type=area_effect_cloud,name=gameStat,tag=notGaming] add allReady execute @p[tag=!Ready,m=2] ~ ~ ~ scoreboard players tag @e[name=gameStat,type=area_effect_cloud,tag=notGaming] remove allReady execute @p[tag=!Ready,m=2] ~ ~ ~ execute @e[name=gameStat,type=area_effect_cloud,tag=notGaming] ~ ~ ~ execute @s[tag=!allReady,score_waitTime_min=1] ~ ~ ~ function system:cond_breakstartcount scoreboard players add @e[name=gameStat,tag=allReady] waitTime 1 execute @e[name=gameStat,score_waitTime=1,score_waitTime_min=1] ~ ~ ~ title @a times 10 140 10 execute @e[name=gameStat,score_waitTime=1,score_waitTime_min=1] ~ ~ ~ title @a subtitle [{"color":"aqua","text":"請(qǐng)玩家站在準(zhǔn)備區(qū)域不要離開(kāi)"}] execute @e[name=gameStat,score_waitTime=1,score_waitTime_min=1] ~ ~ ~ title @a title [{"color":"yellow","text":"游戲即將開(kāi)始"}] execute @e[name=gameStat,score_waitTime=40,score_waitTime_min=40] ~ ~ ~ title @a subtitle [{"color":"aqua","text":"3"}] execute @e[name=gameStat,score_waitTime=40,score_waitTime_min=40] ~ ~ ~ execute @a ~ ~ ~ playsound block.note.pling voice @p ~ ~ ~ 1 0 execute @e[name=gameStat,score_waitTime=60,score_waitTime_min=60] ~ ~ ~ title @a subtitle [{"color":"aqua","text":"2"}] execute @e[name=gameStat,score_waitTime=60,score_waitTime_min=60] ~ ~ ~ execute @a ~ ~ ~ playsound block.note.pling voice @p ~ ~ ~ 1 0 execute @e[name=gameStat,score_waitTime=80,score_waitTime_min=80] ~ ~ ~ title @a subtitle [{"color":"aqua","text":"1"}] execute @e[name=gameStat,score_waitTime=80,score_waitTime_min=80] ~ ~ ~ execute @a ~ ~ ~ playsound block.note.pling voice @p ~ ~ ~ 1 0 execute @e[name=gameStat,score_waitTime_min=100] ~ ~ ~ title @a times 10 30 10 execute @e[name=gameStat,score_waitTime_min=100] ~ ~ ~ title @a title [{"color":"gold","text":"游戲開(kāi)始"}] scoreboard players set @e[name=gameStat,type=area_effect_cloud,score_waitTime_min=100] gameStat 1 scoreboard players reset @e[name=gameStat,score_gameStat_min=1,score_gameStat=1] waitTime scoreboard players tag @e[name=gameStat,score_gameStat_min=1,score_gameStat=1] remove allReady execute @e[type=area_effect_cloud,name=gameStat,score_gameStat_min=1,score_gameStat=1] ~ ~ ~ function system:StartGame
system:cond_breakstartcount.mcfunction
title @a clear title @a reset scoreboard players reset @s waitTime
雖然這個(gè)獨(dú)立出來(lái)的子模塊只有3條指令,但是如果分離出來(lái)的是30條而不是3條呢?能夠節(jié)省多少功夫想必不需要我解釋了吧?
以上是關(guān)于函數(shù)系統(tǒng)模塊調(diào)用的部分,當(dāng)中有提到使用進(jìn)度系統(tǒng)來(lái)調(diào)用部分獨(dú)立模塊,我們接下來(lái)來(lái)講這一部分。
函數(shù)系統(tǒng)與進(jìn)度系統(tǒng)的聯(lián)動(dòng)
advancement,亦簡(jiǎn)稱adv,目前wiki翻譯叫進(jìn)度。這里就不多作介紹了。在17w17b中MOJANG允許進(jìn)度返回指令作為達(dá)成進(jìn)度的獎(jiǎng)勵(lì),讓不少玩家發(fā)現(xiàn)了新大陸。隨后在17w18b中,MOJANG進(jìn)一步完善進(jìn)度系統(tǒng),使其可以完全獨(dú)立于命令方塊而建立起一個(gè)命令系統(tǒng);在1.12 pre1中,MOJANG又作出了修改,將進(jìn)度系統(tǒng)中的命令部分拿出來(lái)做成了如今的函數(shù)系統(tǒng)。
但是這并不意味著進(jìn)度系統(tǒng)就不可以參與到命令系統(tǒng)中來(lái),因?yàn)?font class="tc">如今的進(jìn)度系統(tǒng)可以返回函數(shù)作為達(dá)成進(jìn)度的獎(jiǎng)勵(lì)。
相信很多人已經(jīng)知道進(jìn)度系統(tǒng)的結(jié)構(gòu)了,但仍有相當(dāng)一部分朋友還沒(méi)有了解,在這里我們不妨來(lái)溫習(xí)一下。
自定義的進(jìn)度,所有文件都保存在存檔目錄/data/advancements/下,在這里新建的文件夾同樣都稱為命名空間,命名空間下存放各種進(jìn)度文件。進(jìn)度文件使用 json 格式。這里展示一個(gè)用于進(jìn)度命令系統(tǒng)的例子
所涉及的兩個(gè)文件分別是data/advancements/system/HelloTitle.json和data/functions/system/HelloTitle.mcfunction,這里進(jìn)度和函數(shù)都用同樣的命名空間和文件名方便記憶和管理,可以看到函數(shù)文件是 .mcfunction,而進(jìn)度文件是 .json
system:HelloTitle.json
{ "criteria":{ "custom_name":{ "trigger":"minecraft:tick" } }, "rewards":{ "function":"system:hellotitle" } }
system:HelloTitle.mcfunction
#revoke adv,用于下次再激活 advancement revoke @s only system:hellotitle #命令部分 scoreboard objectives add helloTitle stat.leaveGame scoreboard players tag @s[tag=HelloTitle,score_helloTitle_min=1] remove HelloTitle tellraw @s[tag=!HelloTitle] ["",{"text":"Hello ","color":"yellow"},{"selector":"@s"},{"text":"! Welcome to Minecraft!","color":"yellow"}] scoreboard players tag @s[tag=!HelloTitle] add HelloTitle scoreboard players reset @s[score_helloTitle_min=1] helloTitle
這個(gè)進(jìn)度會(huì)在下一個(gè)游戲刻達(dá)成,對(duì)象是全體在線玩家,達(dá)成進(jìn)度后會(huì)執(zhí)行HelloTitle.mcfunction中的指令。其實(shí)現(xiàn)的效果是,當(dāng)玩家進(jìn)入這個(gè)世界時(shí),會(huì)在聊天框看見(jiàn)問(wèn)候語(yǔ)(其他人看不到)。
可以看到,相比于以前命令方塊高頻,這里采用了進(jìn)度系統(tǒng)的 tick 觸發(fā)器和@s選擇器。如果單純用命令方塊高頻或者函數(shù)系統(tǒng),那么只需要這樣
scoreboard objectives add helloTitle stat.leaveGame scoreboard players tag @a[tag=HelloTitle,score_helloTitle_min=1] remove HelloTitle execute @a[tag=!HelloTitle] ~ ~ ~ tellraw @s ["",{"text":"Hello ","color":"yellow"},{"selector":"@s"},{"text":"! Welcome to Minecraft!","color":"yellow"}] scoreboard players tag @a[tag=!HelloTitle] add HelloTitle scoreboard players reset @a[score_helloTitle_min=1] helloTitle
區(qū)別就是選擇器上的不一樣。如果大家覺(jué)得進(jìn)度系統(tǒng)很麻煩,可以不去使用,但是接下來(lái)我們會(huì)看到一個(gè)使用進(jìn)度系統(tǒng)的其他觸發(fā)器來(lái)調(diào)用函數(shù)的例子。例如,要讓所有冒險(xiǎn)模式玩家入水即死。
rules:DieInWater.json
{ "criteria":{ "1":{ "trigger":"enter_block", "condition":{ "block":"water" } } }, "rewards":{ "function":"rules:dieinwater" } }
rules:DieInWater.mcfunction
#revoke advancement revoke @s only rules:dieinwater #commands scoreboard players tag @p[m=2,r=0] add waterKill execute @s[tag=waterKill] ~ ~ ~ tellraw @a [{"selector":"@s"},{"color":"white","text":" 被水淹沒(méi)了"}] execute @s[tag=waterKill] ~ ~ ~ gamerule showDeathMessages false kill @s[tag=waterKill] execute @s[tag=waterKill] ~ ~ ~ gamerule showDeathMessages true scoreboard players tag @s[tag=waterKill] remove waterKill
當(dāng)玩家踏入水中時(shí),我們要給玩家加上一個(gè)tag,然后殺掉他。至于為什么用@p而不用@s呢?因?yàn)?font class="tc">@p不能選中死人,而@s可以,如果不想看到聊天框刷屏,就不要選擇用@s。
以上是利用進(jìn)度系統(tǒng)的 enter_block(玩家進(jìn)入方塊) 這一觸發(fā)器來(lái)實(shí)現(xiàn)落水即死功能的,如果單純依靠函數(shù),不依靠進(jìn)度系統(tǒng)去實(shí)現(xiàn)的話,可以這樣寫(xiě)
rules:DieInWater_FUNCONLY.mcfunction
execute @a[m=2] ~ ~ ~ detect ~ ~ ~ water -1 scoreboard players tag @p[r=0] add waterKill execute @a[tag=waterKill] ~ ~ ~ tellraw @a [{"selector":"@s"},{"color":"white","text":" 被水淹沒(méi)了"}] execute @a[tag=waterKill] ~ ~ ~ gamerule showDeathMessages false kill @a[tag=waterKill] execute @a[tag=waterKill] ~ ~ ~ gamerule showDeathMessages true scoreboard players tag @a[tag=waterKill] remove waterKill
然后將這個(gè)函數(shù)扔進(jìn)主進(jìn)程中高頻執(zhí)行即可。
我們講完了函數(shù)系統(tǒng)與進(jìn)度系統(tǒng)的聯(lián)動(dòng)部分。道理而言已經(jīng)講完了函數(shù)系統(tǒng)的基礎(chǔ)使用,那么在最后,我們來(lái)聊聊函數(shù)系統(tǒng)與命令方塊系統(tǒng)的對(duì)比吧,看看它們各自的優(yōu)缺點(diǎn)。
函數(shù)系統(tǒng)與命令方塊的對(duì)比
如果你看上面的看得有點(diǎn)迷糊,那我們來(lái)簡(jiǎn)單講講函數(shù)系統(tǒng)和命令方塊(CB)系統(tǒng)的對(duì)比吧,進(jìn)度作為函數(shù)的聯(lián)動(dòng)觸發(fā)形式,就不作過(guò)多講解了。
前面講到的三種模塊中,對(duì)執(zhí)行順序無(wú)要求的高頻模塊無(wú)論是用函數(shù)還是CB都沒(méi)有什么問(wèn)題,而那些需要嚴(yán)格保證執(zhí)行順序的模塊,以前我會(huì)將他們?nèi)窟B在一起,只用一個(gè) RCB(循環(huán)型命令方塊,即高頻CB源)作為“信號(hào)源”。
為什么不劃出做成子模塊(通常以ICB-脈沖型命令方塊起頭,后面跟一串CCB -連接型命令方塊)調(diào)用呢?因?yàn)槟阍诋?dāng)前游戲刻調(diào)用了ICB子模塊以后,它會(huì)等到下一個(gè)游戲刻才執(zhí)行??刹灰】催@一個(gè)游戲刻的延遲,它往往可能讓你的系統(tǒng)出現(xiàn)意外,進(jìn)而產(chǎn)生各種蜜汁bug。
而函數(shù)系統(tǒng)中,調(diào)用的子模塊會(huì)立即插隊(duì)執(zhí)行,從而能夠嚴(yán)格保證執(zhí)行順序,出錯(cuò)的可能性大大降低了。
函數(shù)系統(tǒng)不能夠直接支持Conditional模式,也就是條件激活,而CB是支持的。關(guān)于這一點(diǎn),以我個(gè)人的經(jīng)驗(yàn),影響是不大的,過(guò)去1.8沒(méi)有
Conditional不也是這么過(guò)來(lái)了嗎?
函數(shù)系統(tǒng)的主進(jìn)程使用gamerule gameLoopFunction <命名空間:函數(shù)>來(lái)掛載,而CB系統(tǒng)的"主進(jìn)程"使用 RCB 作為高頻信號(hào)源。
在過(guò)去的版本,通過(guò)glf掛載的主進(jìn)程,其執(zhí)行者是系統(tǒng),也就是server。這個(gè)設(shè)定會(huì)產(chǎn)生各種各樣的安全隱患,于是在后來(lái)的版本中,MOJANG將其執(zhí)行者改成了glf所掛載的函數(shù)(前面也講到了)。就目前而言,僅僅通過(guò)函數(shù)系統(tǒng),就能夠?qū)崿F(xiàn)過(guò)去CB能夠?qū)崿F(xiàn)的功能,甚至還有一些是CB難以實(shí)現(xiàn)的功能。在這里就不過(guò)多講了,希望對(duì)大家有所啟發(fā),可以研發(fā)各種各樣的黑科技出來(lái)~
這里插入講一點(diǎn),我想對(duì)于地圖制作者來(lái)講是絕對(duì)的福音。
mcf系統(tǒng)直接支持樣式代碼§。
CB系統(tǒng)的顏色黑科技什么的在這個(gè)面前根本不值一提。
資源占用方面,簡(jiǎn)單說(shuō)一下我個(gè)人的經(jīng)驗(yàn)。
我們花了不到一天的時(shí)間把《喋血冰封II》升級(jí)到新的命令系統(tǒng)。新系統(tǒng)在資源占用方面明顯比之前龐大的CB系統(tǒng)少了很多,流暢度不降反升,這也得益于函數(shù)系統(tǒng)更加接近游戲底層。CB系統(tǒng)在方塊更新這一方面就輸?shù)袅艘淮蠼?。更何況它需要占地。
試想一下,如果你的系統(tǒng)足夠龐大,出生地可以加載的區(qū)域放多CB,你能夠記得住嗎?你在調(diào)試系統(tǒng)的時(shí)候,需要花多少時(shí)間去找到你要修改的指令呢?
此外,對(duì)于一些不放在出生點(diǎn)的模塊,我們還需要考慮到區(qū)塊加載的問(wèn)題,相信這也是讓許多人頭疼的問(wèn)題吧?
函數(shù)系統(tǒng)顯然不需要擔(dān)心這個(gè),因?yàn)樗械膬?nèi)容都保存在文件里,不具體地出現(xiàn)在游戲世界中,在資源占用方面相比與CB系統(tǒng)而言,是要占優(yōu)的。
我們知道,寫(xiě)一個(gè)功能可能只要一兩天,debug可能要一周。過(guò)去CB系統(tǒng),不依靠編輯器的話,你得手動(dòng)檢查,如果要在中間插入什么指令的話,還得整體移動(dòng)CB,實(shí)際工作效率是十分感人的;借助于編輯器,我們可以通過(guò)ooc導(dǎo)入的方式來(lái)實(shí)現(xiàn)快速修改
而函數(shù)系統(tǒng)呢?你需要改點(diǎn)什么,直接去翻文件改,改完了保存一下,再在游戲里通過(guò)/reload指令直接刷新,完事兒了。游戲都不用退出重進(jìn)。
但凡地圖制作者,知道了這些,都應(yīng)該會(huì)心動(dòng)的吧。
講了這么多,相信大家對(duì)新系統(tǒng)也有一定的了解了,說(shuō)不定已經(jīng)激動(dòng)得說(shuō)不出話來(lái)了吧,那么更多內(nèi)容就請(qǐng)大家自行去體驗(yàn)一下吧。在接下來(lái)的更新里,沒(méi)準(zhǔn)還會(huì)多出什么意想不到的東西呢!
如轉(zhuǎn)載涉及版權(quán)等問(wèn)題,請(qǐng)作者與我司聯(lián)系,我司將在第一時(shí)間刪除或支付稿酬。
全部評(píng)論