首頁>Club>
/function
13
回覆列表
  • 1 # 小童遊戲說

    《我的世界》1.12版本新增了函式命令這一內容,很多玩家覺得非常複雜,也有玩家發現這個命令可以完全脫離命令方塊而存在。

    寫在前面的話

    1.9的更新為我們帶來了三色命令方塊,讓命令方塊脫離了紅石成為獨立的體系,我們因此可以更好實現一些想法;時隔3個版本,MOJANG再次為我們帶來驚人的變革。

    1.12中,函式與進度系統的出現,讓命令脫離命令方塊——這句曾經說過的玩笑般的預言,正式成為可能。

    函式系統的構成

    函式系統的由來

    函式(function)系統,是 MC 1.12 Pre-1 版本中新增的一個功能,它將原來進度系統中返回指令的部分單獨提取出來,做成了現在的函式系統。

    函式系統的形式

    函式系統由名稱空間和函式檔案組成,這些檔案儲存在存檔目錄/data/functions/下。functions目錄下的資料夾,稱為名稱空間,各個名稱空間下存放不同的函式檔案。實際上,名稱空間就是方便我們編寫者分類並管理各種函式檔案。

    函式檔案是以.mcfunction為字尾名的文字檔案,建議採用utf-8無BOM編碼以防顯示錯亂。簡單來講,一個函式等價於一個多行命令方塊,函式檔案裡面每一行寫一條指令,當執行這個函式時,裡面的指令會按行依次執行。如果在一個函式中呼叫其它函式,那麼在同一遊戲刻,被呼叫的函式中所有指令先執行完,再繼續當前函式中後續的指令,就像插隊一樣,我們在後面對比命令方塊時還會說到這個。

    請注意:在 1.12 Pre-3 版本中存在一個嚴重漏洞,即命令執行體不能正確地透過execute傳遞到被呼叫的函式中去,這個漏洞有望在後續版本以及正式版修復。

    以下是本文用到的一個函式系統的目錄,帶有"+"的表示為目錄

    + functions+ sayhi.mcfunctionbye.mcfunctionText1.mcfunctiontext2.mcfunction+ system+ process_process.mcfunction_main.mcfunctionplayer_tick.mcfunction

    如何呼叫函式

    在 1.12 中,MOJANG新增了function指令和一條名為gameLoopFunction的遊戲規則來輔助我們使用函式系統。function指令的格式如下:

    function <名稱空間:函式名>function <名稱空間:函式名> <if|unless> <選擇器>

    這兩條都是可行的。其中,if|unless是在1.12 pre-4加入的功能,後面我會解釋到這個。我們先來說說第一種形式。例如上面的目錄中,要呼叫system這個名稱空間下的_main檔案,就是輸入這樣的指令:

    function system:_main

    現在,我們來看一個例子例如say名稱空間下的Text1.mcfunction和text2.mcfunction,裡面分別寫上這些內容

    Text1.mcfunction

    #這是一個範例,在function檔案中可以用#來註釋行。請注意,不能夠使用//來註釋!say 1function say:text2say 2

    text2.mcfunction

    say 3say 4

    當我在系統後臺輸入function say:text1時,聊天框會出現這些內容:[server] 1[server] 3[server] 4[server] 2

    也就是說,執行function指令的人,會把函數里面的指令依次執行——我在系統後臺輸入function指令,就是系統在執行,我自己輸入function指令,就是我本人在執行。大家可能注意到了,函式中支援使用#進行註釋(舊版本支援//註釋,當前版本已經不再支援),也就是說被註釋行不會作為指令而執行,這一點有多方便相比不比我再說了。同時需要大家注意:函式中所有指令不能夠以/開頭。例如,你可以這樣寫:

    say @s

    但是不能這樣寫:

    /say @s

    最後有一點需要注意的是,在function指令中呼叫函式時,不區分大小寫。例如前面say名稱空間下的Text1.mcfunction,我在呼叫的時候寫的是say:text1

    然後是第二種形式,也就是帶有if|unless的。我簡單舉兩個例子,大家就知道是什麼意思了。

    say:tellraw.mcfunctionscoreboard objectives add timer dummy 計時器scoreboard players add @s timer 1function random:title if @s[score_timer_min=1200]scoreboard players reset @s[score_timer_min=1200] timerrandom:title.mcfunctionsummon 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放到主程序中

    execute @a ~ ~ ~ function say:tellraw

    則每位玩家每分鐘將會看到1~5中隨機一個數字出現在聊天框。也就是說,只有計時器分數滿1200的人會執行後面的隨機部分。那麼很顯然,帶有if的意思就是,如果能找到後面的選擇器,就執行這個函式,否則不執行。相當於testfor。

    那麼unless的意思也就很明顯了:在找不到後面的選擇器的時候,執行這個函式,相當於testfor+非門。

    gameLoopFunction

    講完呼叫,就該講講高頻了。玩命令方塊的人都知道高頻是實現許多功能的前提。在函式系統中,MOJANG 為我們提供了一條名為gameLoopFunction的遊戲規則來實現高頻。它的格式是

    gamerule gameLoopFunction <名稱空間:函式>

    也就是說,你可以指定一個函式來高頻執行,這個高頻是20Hz的,也就是每一個遊戲刻都會執行一遍。新建的存檔如果沒有執行過這條指令,而是用gamerule gameLoopFunction來查詢的話,得到的返回值是-

    為了方便,我們將這個規則簡稱為glf。在舊版本中,glf指定的函式,由系統(server)作為執行體;而在新的版本中,MOJANG 引入了虛擬執行體,例如將 say:text2 指定為glf時,每一個遊戲刻得到的結果是這樣的

    [say:text2] 3[say:text2] 4

    也就是說,系統不再作為執行體,而是由虛擬的執行體代為執行。

    關於 glf 多說兩句。使用 glf 去高頻執行一個函式,和使用 RCB(迴圈型命令方塊,紫色那種)去執行,是不一樣的。區別主要在於其更新順序先後。一般而言不會造成嚴重影響,但是在某些情況會不一樣。比如,使用 CB 能檢測到生物的{HurtTime:10s}這個 NBT,而使用 glf 執行函式只能檢測到的是{HurtTime:9s},檢測不到10,這是因為關於函式的更新,都放在了生物更新之後,而 CB 的更新則是在生物更新之前。詳情可以看這裡。按照 Searge 的說法,函式並不是命令方塊的完全替代。這個說法,大家就見仁見智了。對我個人而言這個影響不大。

    函式系統的模組呼叫

    對於一個完整的命令系統而言,模組一般可以分為三類:對執行順序先後有要求的高頻模組、對執行順序先後無要求的高頻模組、非高頻模組。在函式系統中,我們同樣可以將模組分成這三類。為了方便後續講解。我們作這樣的設定:

    將 system:_main設為 glf ,並稱之為主程序或者主時鐘

    對於上面講到的三類模組,我們透過三種不同的方式去呼叫。

    對執行順序先後有要求的高頻模組,在主程序中按照需要的順序排列好來呼叫。對執行順序先後沒有要求的高頻模組,在主程序中可以比較隨意放置位置,但是一般不會考慮優先執行。特別地,如果這個模組是針對每一個玩家獨立執行的,可以使用進度系統中的"tick"觸發器來呼叫,而不需要放在主程序中。僅在特定情況下觸發的非高頻模組,在主程序中呼叫,但是輔以execute、scoreboard和選擇器引數去控制其在合適的時候被呼叫,這裡的選擇器,包括了在1.12 pre-4中新增的if/unless的部分。

    非高頻模組在特定條件下啟用,也在很大程度上減少了模組中大量重複出現execute的現象,並完全杜絕了超長的Conditional鏈,因為function中並不直接支援Conditional。不直接支援,說明可以間接支援,對吧。我們來看一個例子。

    假設有紅藍兩隊,在開始前考慮到互毆問題不進行分隊,而是採用掛tag的方式。紅隊以tag=redTeam為標記,藍隊則以tag=blueTeam為標記,準備觀戰的玩家以tag=specTeam為標記當玩家站在相應區域(紅藍兩隊的所有玩家還需要選擇了職業)新增Ready的標記,視為準備就緒。如果玩家不在相應區域時就移除Ready的標記。選擇了職業的玩家,其記分板項selectClass數值大於等於1全部玩家準備就緒後,遊戲進入倒計時,倒計時結束時遊戲開始倒計時未結束,有玩家脫離準備就緒的狀態,則倒計時中斷

    條件比較多,我們先來看看怎麼寫這個模組,再進行分析。在這裡,我們準備了一個名為gameStat的aec實體作為標記,所有遊戲程序會以tag或者score的形式掛載到該實體上。請看指令部分

    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 allReadyexecute @p[tag=!Ready,m=2] ~ ~ ~ scoreboard players tag @e[name=gameStat,type=area_effect_cloud,tag=notGaming] remove allReadyexecute @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 clearexecute @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 resetexecute @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 1execute @e[name=gameStat,score_waitTime=1,score_waitTime_min=1] ~ ~ ~ title @a times 10 140 10execute @e[name=gameStat,score_waitTime=1,score_waitTime_min=1] ~ ~ ~ title @a subtitle [{"color":"aqua","text":"請玩家站在準備區域不要離開"}]execute @e[name=gameStat,score_waitTime=1,score_waitTime_min=1] ~ ~ ~ title @a title [{"color":"yellow","text":"遊戲即將開始"}]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 0execute @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 0execute @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 0execute @e[name=gameStat,score_waitTime_min=100] ~ ~ ~ title @a times 10 30 10execute @e[name=gameStat,score_waitTime_min=100] ~ ~ ~ title @a title [{"color":"gold","text":"遊戲開始"}]scoreboard players set @e[name=gameStat,type=area_effect_cloud,score_waitTime_min=100] gameStat 1scoreboard players reset @e[name=gameStat,score_gameStat_min=1,score_gameStat=1] waitTimescoreboard 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

    接下來我們來慢慢分析。

    首先是開始的條件。有紅藍兩隊,那麼這兩隊都肯定需要有人,才能夠開始,考慮到同一選擇器中不能重複使用tag的引數,我們保留了區分隊伍的引數,而不是區分是否準備就緒的引數。因此,第一條指令的意思是,當存在選了職業並選紅隊的玩家以及選了職業並選藍隊的玩家,我們給中心實體加上allReady這個標記,以表明可能滿足開始條件。

    至於滿足條件嗎?如果有未準備就緒的玩家,就說明不滿足,那我們就讓一個沒有準備就緒的玩家來去掉allReady這個標記好了。

    對於3~5行,我們放後面點講。先看後面。滿足開始條件以後,我們會給中心實體加分(使用waitTime這個記分板項),在第一刻加分後出現提示文字提示準備開始,然後進入迴圈計時,最後計時滿了,呼叫system:startgame這個函式來開始遊戲(這裡不是例子的部分,不作說明)。

    那麼回過頭來看3~5行,這裡明顯是打斷的部分。打斷,就是要清掉提示文字、重置計時器。如果此時都還沒有進行過加分,那麼我們就不必進行那三條指令,因此可以看到中間有個選擇器裡有score_waitTime_min=1的引數加以限制。

    重點來了,我們看到這3條指令前面相當長一串execute是重複的。因為在以前用cb寫的時候,這裡我使用了Conditional,而現在函式不直接支援Conditional,所以我用了一大堆execute,但是這裡我們可以稍作修改,對不對?請看下面

    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 allReadyexecute @p[tag=!Ready,m=2] ~ ~ ~ scoreboard players tag @e[name=gameStat,type=area_effect_cloud,tag=notGaming] remove allReadyexecute @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 1execute @e[name=gameStat,score_waitTime=1,score_waitTime_min=1] ~ ~ ~ title @a times 10 140 10execute @e[name=gameStat,score_waitTime=1,score_waitTime_min=1] ~ ~ ~ title @a subtitle [{"color":"aqua","text":"請玩家站在準備區域不要離開"}]execute @e[name=gameStat,score_waitTime=1,score_waitTime_min=1] ~ ~ ~ title @a title [{"color":"yellow","text":"遊戲即將開始"}]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 0execute @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 0execute @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 0execute @e[name=gameStat,score_waitTime_min=100] ~ ~ ~ title @a times 10 30 10execute @e[name=gameStat,score_waitTime_min=100] ~ ~ ~ title @a title [{"color":"gold","text":"遊戲開始"}]scoreboard players set @e[name=gameStat,type=area_effect_cloud,score_waitTime_min=100] gameStat 1scoreboard players reset @e[name=gameStat,score_gameStat_min=1,score_gameStat=1] waitTimescoreboard 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 cleartitle @a resetscoreboard players reset @s waitTime

    雖然這個獨立出來的子模組只有3條指令,但是如果分離出來的是30條而不是3條呢?能夠節省多少功夫想必不需要我解釋了吧?

    以上是關於函式系統模組呼叫的部分,當中有提到使用進度系統來呼叫部分獨立模組,我們接下來來講這一部分。

    函式系統與進度系統的聯動

    advancement,亦簡稱adv,目前wiki翻譯叫進度。這裡就不多作介紹了。在17w17b中MOJANG允許進度返回指令作為達成進度的獎勵,讓不少玩家發現了新大陸。隨後在17w18b中,MOJANG進一步完善進度系統,使其可以完全獨立於命令方塊而建立起一個命令系統;在1.12 pre1中,MOJANG又作出了修改,將進度系統中的命令部分拿出來做成了如今的函式系統。

    但是這並不意味著進度系統就不可以參與到命令系統中來,因為如今的進度系統可以返回函式作為達成進度的獎勵。

    相信很多人已經知道進度系統的結構了,但仍有相當一部分朋友還沒有了解,在這裡我們不妨來溫習一下。

    自定義的進度,所有檔案都儲存在存檔目錄/data/advancements/下,在這裡新建的資料夾同樣都稱為名稱空間,名稱空間下存放各種進度檔案。進度檔案使用 json 格式。這裡展示一個用於進度命令系統的例子

    所涉及的兩個檔案分別是data/advancements/system/HelloTitle.json和data/functions/system/HelloTitle.mcfunction,這裡進度和函式都用同樣的名稱空間和檔名方便記憶和管理,可以看到函式檔案是 .mcfunction,而進度檔案是 .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.leaveGamescoreboard players tag @s[tag=HelloTitle,score_helloTitle_min=1] remove HelloTitletellraw @s[tag=!HelloTitle] ["",{"text":"Hello ","color":"yellow"},{"selector":"@s"},{"text":"! Welcome to Minecraft!","color":"yellow"}]scoreboard players tag @s[tag=!HelloTitle] add HelloTitlescoreboard players reset @s[score_helloTitle_min=1] helloTitle

    這個進度會在下一個遊戲刻達成,物件是全體線上玩家,達成進度後會執行HelloTitle.mcfunction中的指令。其實現的效果是,當玩家進入這個世界時,會在聊天框看見問候語(其他人看不到)。

    可以看到,相比於以前命令方塊高頻,這裡採用了進度系統的 tick 觸發器和@s選擇器。如果單純用命令方塊高頻或者函式系統,那麼只需要這樣

    scoreboard objectives add helloTitle stat.leaveGamescoreboard players tag @a[tag=HelloTitle,score_helloTitle_min=1] remove HelloTitleexecute @a[tag=!HelloTitle] ~ ~ ~ tellraw @s ["",{"text":"Hello ","color":"yellow"},{"selector":"@s"},{"text":"! Welcome to Minecraft!","color":"yellow"}]scoreboard players tag @a[tag=!HelloTitle] add HelloTitlescoreboard players reset @a[score_helloTitle_min=1] helloTitle

    區別就是選擇器上的不一樣。如果大家覺得進度系統很麻煩,可以不去使用,但是接下來我們會看到一個使用進度系統的其他觸發器來呼叫函式的例子。例如,要讓所有冒險模式玩家入水即死。

    rules:DieInWater.json

    {"criteria":{"1":{"trigger":"enter_block","condition":{"block":"water"}}},"rewards":{"function":"rules:dieinwater"}}

    rules:DieInWater.mcfunction

    #revokeadvancement revoke @s only rules:dieinwater#commandsscoreboard players tag @p[m=2,r=0] add waterKillexecute @s[tag=waterKill] ~ ~ ~ tellraw @a [{"selector":"@s"},{"color":"white","text":" 被水淹沒了"}]execute @s[tag=waterKill] ~ ~ ~ gamerule showDeathMessages falsekill @s[tag=waterKill]execute @s[tag=waterKill] ~ ~ ~ gamerule showDeathMessages truescoreboard players tag @s[tag=waterKill] remove waterKill

    當玩家踏入水中時,我們要給玩家加上一個tag,然後殺掉他。至於為什麼用@p而不用@s呢?因為@p不能選中死人,而@s可以,如果不想看到聊天框刷屏,就不要選擇用@s。

    以上是利用進度系統的 enter_block(玩家進入方塊) 這一觸發器來實現落水即死功能的,如果單純依靠函式,不依靠進度系統去實現的話,可以這樣寫

    rules:DieInWater_FUNCONLY.mcfunction

    execute @a[m=2] ~ ~ ~ detect ~ ~ ~ water -1 scoreboard players tag @p[r=0] add waterKillexecute @a[tag=waterKill] ~ ~ ~ tellraw @a [{"selector":"@s"},{"color":"white","text":" 被水淹沒了"}]execute @a[tag=waterKill] ~ ~ ~ gamerule showDeathMessages falsekill @a[tag=waterKill]execute @a[tag=waterKill] ~ ~ ~ gamerule showDeathMessages truescoreboard players tag @a[tag=waterKill] remove waterKill

    然後將這個函式扔進主程序中高頻執行即可。

    我們講完了函式系統與進度系統的聯動部分。道理而言已經講完了函式系統的基礎使用,那麼在最後,我們來聊聊函式系統與命令方塊系統的對比吧,看看它們各自的優缺點。

    函式系統與命令方塊的對比

    如果你看上面的看得有點迷糊,那我們來簡單講講函式系統和命令方塊(CB)系統的對比吧,進度作為函式的聯動觸發形式,就不作過多講解了。

    前面講到的三種模組中,對執行順序無要求的高頻模組無論是用函式還是CB都沒有什麼問題,而那些需要嚴格保證執行順序的模組,以前我會將他們全部連在一起,只用一個 RCB(迴圈型命令方塊,即高頻CB源)作為“訊號源”。

    為什麼不劃出做成子模組(通常以ICB-脈衝型命令方塊起頭,後面跟一串CCB -連線型命令方塊)呼叫呢?因為你在當前遊戲刻呼叫了ICB子模組以後,它會等到下一個遊戲刻才執行。可不要小看這一個遊戲刻的延遲,它往往可能讓你的系統出現意外,進而產生各種蜜汁bug。

    而函式系統中,呼叫的子模組會立即插隊執行,從而能夠嚴格保證執行順序,出錯的可能性大大降低了。

    函式系統不能夠直接支援Conditional模式,也就是條件啟用,而CB是支援的。關於這一點,以我個人的經驗,影響是不大的,過去1.8沒有

    Conditional不也是這麼過來了嗎?

    函式系統的主程序使用gamerule gameLoopFunction <名稱空間:函式>來掛載,而CB系統的"主程序"使用 RCB 作為高頻訊號源。

    在過去的版本,透過glf掛載的主程序,其執行者是系統,也就是server。這個設定會產生各種各樣的安全隱患,於是在後來的版本中,MOJANG將其執行者改成了glf所掛載的函式(前面也講到了)。就目前而言,僅僅透過函式系統,就能夠實現過去CB能夠實現的功能,甚至還有一些是CB難以實現的功能。在這裡就不過多講了,希望對大家有所啟發,可以研發各種各樣的黑科技出來~

    這裡插入講一點,我想對於地圖製作者來講是絕對的福音。

    mcf系統直接支援樣式程式碼§。

    CB系統的顏色黑科技什麼的在這個面前根本不值一提。

    資源佔用方面,簡單說一下我個人的經驗。

    我們花了不到一天的時間把《喋血冰封II》升級到新的命令系統。新系統在資源佔用方面明顯比之前龐大的CB系統少了很多,流暢度不降反升,這也得益於函式系統更加接近遊戲底層。CB系統在方塊更新這一方面就輸掉了一大截。更何況它需要佔地。

    試想一下,如果你的系統足夠龐大,出生地可以載入的區域放多CB,你能夠記得住嗎?你在除錯系統的時候,需要花多少時間去找到你要修改的指令呢?

    此外,對於一些不放在出生點的模組,我們還需要考慮到區塊載入的問題,相信這也是讓許多人頭疼的問題吧?

    函式系統顯然不需要擔心這個,因為它所有的內容都儲存在檔案裡,不具體地出現在遊戲世界中,在資源佔用方面相比與CB系統而言,是要佔優的。

    我們知道,寫一個功能可能只要一兩天,debug可能要一週。過去CB系統,不依靠編輯器的話,你得手動檢查,如果要在中間插入什麼指令的話,還得整體移動CB,實際工作效率是十分感人的;藉助於編輯器,我們可以透過ooc匯入的方式來實現快速修改

    而函式系統呢?你需要改點什麼,直接去翻檔案改,改完了儲存一下,再在遊戲裡透過/reload指令直接重新整理,完事兒了。遊戲都不用退出重進。

    但凡地圖製作者,知道了這些,都應該會心動的吧。

    講了這麼多,相信大家對新系統也有一定的瞭解了,說不定已經激動得說不出話來了吧,那麼更多內容就請大家自行去體驗一下吧。在接下來的更新裡,沒準還會多出什麼意想不到的東西呢!

    圖片轉自網路侵刪

  • 2 # 小其愛遊戲

    以下所述僅代表個人觀點哦

    首先函式系統是1.12版本以後的一個功能,他將原來進度系統中返回指令的部分單獨提取出來,做成現在的函式系統。

    函式系統有名稱空間和函式檔案組成。名稱空間就是方便我們編寫者分類管理各種函式檔案,而函式檔案,簡單來講,一個函式等價於多個命令方塊,函式檔案裡面每一行寫一條指令,當執行這個函式時,裡面的指令會按行依次執行。如果在一個函式中呼叫其他函式,那麼在同一遊戲刻,被呼叫的函式中,所有指令先執行完,就像插隊一樣。

    function指令格式如下

    function <名稱空間:函式名>

    function<名稱空間:函式名> <選擇器>

    此外,對於一些不放在出生點的模組,我們還需要考慮到區塊載入的問題,相信這也是讓許多人頭疼的問題吧。

    函式系統顯然不需要擔心這個,因為它所有的內容全都儲存在檔案裡,不具體的出現在遊戲世界中,再資源佔用方面相比於cb系統而言是要佔優勢的。

    我們知道,寫一個功能可能只要一兩天,bebug可能要一週。過去cb系統不依靠編輯器的話,你得手動檢查,如果要在中間插入什麼指令的話,還得整體移動,工作效率不高。

    而函式系統,你要改什麼,直接去翻檔案改,改完了儲存一下,再在遊戲裡透過/reload指令直接重新整理就完事兒了,遊戲都不用重進。

    想必大家對函式系統有了一定的瞭解了吧,那麼其他的大家一定還是要自己去探索。畢竟自己探索了才會記得更加牢固,還沒準兒會多出什麼意想不到的東西呢。

    注:以上只代表個人看法和以往經驗,並不代表官方權威資料。

  • 3 # 文超遊戲解說

    感謝方官大大:以下是我的講解《我的世界》1.12版本新增了函式命令這一內容,很多玩家覺得非常複雜,也有玩家發現這個命令可以完全脫離命令方塊而存在。今天文超將推薦的是《我的世界》1.12函式命令系統入門教程 ,希望大家能夠喜歡。

    函式系統的構成

    函式系統的由來

    函式(function)系統,是 MC 1.12 Pre-1 版本中新增的一個功能,它將原來進度系統中返回指令的部分單獨提取出來,做成了現在的函式系統。

    函式系統的形式

    函式系統由名稱空間和函式檔案組成,這些檔案儲存在存檔目錄/data/functions/下。functions目錄下的資料夾,稱為名稱空間,各個名稱空間下存放不同的函式檔案。實際上,名稱空間就是方便我們編寫者分類並管理各種函式檔案。

    函式檔案是以.mcfunction為字尾名的文字檔案,建議採用utf-8無BOM編碼以防顯示錯亂。簡單來講,一個函式等價於一個多行命令方塊,函式檔案裡面每一行寫一條指令,當執行這個函式時,裡面的指令會按行依次執行。如果在一個函式中呼叫其它函式,那麼在同一遊戲刻,被呼叫的函式中所有指令先執行完,再繼續當前函式中後續的指令,就像插隊一樣,我們在後面對比命令方塊時還會說到這個。

    請注意:在 1.12 Pre-3 版本中存在一個嚴重漏洞,即命令執行體不能正確地透過execute傳遞到被呼叫的函式中去,這個漏洞有望在後續版本以及正式版修復。

    以下是本文用到的一個函式系統的目錄,帶有"+"的表示為目錄

    + functions

    + say

    hi.mcfunction

    bye.mcfunction

    Text1.mcfunction

    text2.mcfunction

    + system

    + process

    _process.mcfunction

    _main.mcfunction

    player_tick.mcfunction

    如何呼叫函式

    在 1.12 中,MOJANG新增了function指令和一條名為gameLoopFunction的遊戲規則來輔助我們使用函式系統。function指令的格式如下:

    function <名稱空間:函式名>

    function <名稱空間:函式名> <if|unless> <選擇器>

    這兩條都是可行的。其中,if|unless是在1.12 pre-4加入的功能,後面我會解釋到這個。我們先來說說第一種形式。例如上面的目錄中,要呼叫system這個名稱空間下的_main檔案,就是輸入這樣的指令:

    function system:_main

    現在,我們來看一個例子例如say名稱空間下的Text1.mcfun

    Text1.mcfunction

    #這是一個範例,在function檔案中可以用#來註釋行。請注意,不能夠使用//來註釋!

    say 1

    function say:text2

    say 2

    text2.mcfunction

    say 3

    say 4

    當我在系統後臺輸入function say:text1時,聊天框會出現這些內容:

    [server] 1

    [server] 3

    [server] 4

    [server] 2

    也就是說,執行function指令的人,會把函數里面的指令依次執行——我在系統後臺輸入function指令,就是系統在執行,我自己輸入function指令,就是我本人在執行。大家可能注意到了,函式中支援使用#進行註釋(舊版本支援//註釋,當前版本已經不再支援),也就是說被註釋行不會作為指令而執行,這一點有多方便相比不比我再說了。同時需要大家注意:函式中所有指令不能夠以/開頭。例如,你可以這樣寫:

    say @s

    但是不能這樣寫:

    /say @s

    最後有一點需要注意的是,在function指令中呼叫函式時,不區分大小寫。例如前面say名稱空間下的Text1.mcfunction,我在呼叫的時候寫的是say:text1

    然後是第二種形式,也就是帶有if|unless的。我簡單舉兩個例子,大家就知道是什麼意思了。

    say:tellraw.mcfunction

    scoreboard objectives add timer dummy 計時器

    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"execute @a ~ ~ ~ function say:tellraw

    則每位玩家每分鐘將會看到1~5中隨機一個數字出現在聊天框。也就是說,只有計時器分數滿1200的人會執行後面的隨機部分。那麼很顯然,帶有if的意思就是,如果能找到後面的選擇器,就執行這個函式,否則不執行。相當於testfor。

    那麼unless的意思也就很明顯了:在找不到後面的選擇器的時候,執行這個函式,相當於testfor+非門。

    gameLoopFunction

    講完呼叫,就該講講高頻了。玩命令方塊的人都知道高頻是實現許多功能的前提。在函式系統中,MOJANG 為我們提供了一條名為gameLoopFunction的遊戲規則來實現高頻。它的格式是

    gamerule gameLoopFunction <名稱空間:函式>

    也就是說,你可以指定一個函式來高頻執行,這個高頻是20Hz的,也就是每一個遊戲刻都會執行一遍。新建的存檔如果沒有執行過這條指令,而是用gamerule gameLoopFunction來查詢的話,得到的返回值是-

    為了方便,我們將這個規則簡稱為glf。在舊版本中,glf指定的函式,由系統(server)作為執行體;而在新的版本中,MOJANG 引入了虛擬執行體,例如將 say:text2 指定為glf時,每一個遊戲刻得到的結果是這樣的

    [say:text2] 3

    [say:text2] 4

    也就是說,系統不再作為執行體,而是由虛擬的執行體代為執行。

    關於 glf 多說兩句。使用 glf 去高頻執行一個函式,和使用 RCB(迴圈型命令方塊,紫色那種)去執行,是不一樣的。區別主要在於其更新順序先後。一般而言不會造成嚴重影響,但是在某些情況會不一樣。比如,使用 CB 能檢測到生物的{HurtTime:10s}這個 NBT,而使用 glf 執行函式只能檢測到的是{HurtTime:9s},檢測不到10,這是因為關於函式的更新,都放在了生物更新之後,而 CB 的更新則是在生物更新之前。詳情可以看這裡。按照 Searge 的說法,函式並不是命令方塊的完全替代。這個說法,大家就見仁見智了。對我個人而言這個影響不大函式系統的模組呼叫

    對於一個完整的命令系統而言,模組一般可以分為三類:對執行順序先後有要求的高頻模組、對執行順序先後無要求的高頻模組、非高頻模組。在函式系統中,我們同樣可以將模組分成這三類。為了方便後續講解。我們作這樣的設定:

    將 system:_main設為 glf ,並稱之為主程序或者主時鐘

    對於上面講到的三類模組,我們透過三種不同的方式去呼叫。

    對執行順序先後有要求的高頻模組,在主程序中按照需要的順序排列好來呼叫。對執行順序先後沒有要求的高頻模組,在主程序中可以比較隨意放置位置,但是一般不會考慮優先執行。特別地,如果這個模組是針對每一個玩家獨立執行的,可以使用進度系統中的"tick"觸發器來呼叫,而不需要放在主程序中。僅在特定情況下觸發的非高頻模組,在主程序中呼叫,但是輔以execute、scoreboard和選擇器引數去控制其在合適的時候被呼叫,這裡的選擇器,包括了在1.12 pre-4中新增的if/unless的部分。

    非高頻模組在特定條件下啟用,也在很大程度上減少了模組中大量重複出現execute的現象,並完全杜絕了超長的Conditional鏈,因為function中並不直接支援Conditional。不直接支援,說明可以間接支援,對吧。我們來看一個例子。

    假設有紅藍兩隊,在開始前考慮到互毆問題不進行分隊,而是採用掛tag的方式。

    紅隊以tag=redTeam為標記,藍隊則以tag=blueTeam為標記,準備觀戰的玩家以tag=specTeam為標記

    當玩家站在相應區域(紅藍兩隊的所有玩家還需要選擇了職業)新增Ready的標記,視為準備就緒。

    如果玩家不在相應區域時就移除Ready的標記。

    選擇了職業的玩家,其記分板項selectClass數值大於等於1

    全部玩家準備就緒後,遊戲進入倒計時,倒計時結束時遊戲開始

    倒計時未結束,有玩家脫離準備就緒的狀態,則倒計時中斷

    條件比較多,我們先來看看怎麼寫這個模組,再進行分析。在這裡,我們準備了一個名為gameStat的aec實體作為標記,所有遊戲程序會以tag或者score的形式掛載到該實體上。請看指令部分

    execute @p[tag=redTeam,score_selectClass_min=1] ~ ~ ~ execute @p[tag=blueTeam,score_selectClas。,"rnd_title3"]}

    srules: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":" 被水淹沒了"}]

    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

    當玩家踏入水中時,我們要給玩家加上一個tag,然後殺掉他。至於為什麼用@p而不用@s呢?因為@p不能選中死人,而@s可以,如果不想看到聊天框刷屏,就不要選擇用@s。

    以上是利用進度系統的 enter_block(玩家進入方塊) 這一觸發器來實現落水即死功能的,如果單純依靠函式,不依靠進度系統去實現的話,可以這樣寫

    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":" 被水淹沒了"}]

    execute @a[tag=wa函式系統與命令方塊的對比

    如果你看上面的看得有點迷糊,那我們來簡單講講函式系統和命令方塊(CB)系統的對比吧,進度作為函式的聯動觸發形式,就不作過多講解了。

    前面講到的三種模組中,對執行順序無要求的高頻模組無論是用函式還是CB都沒有什麼問題,而那些需要嚴格保證執行順序的模組,以前我會將他們全部連在一起,只用一個 RCB(迴圈型命令方塊,即高頻CB源)作為“訊號源”。

    為什麼不劃出做成子模組(通常以ICB-脈衝型命令方塊起頭,後面跟一串CCB -連線型命令方塊)呼叫呢?因為你在當前遊戲刻呼叫了ICB子模組以後,它會等到下一個遊戲刻才執行。可不要小看這一個遊戲刻的延遲,它往往可能讓你的系統出現意外,進而產生各種蜜汁bug。

    而函式系統中,呼叫的子模組會立即插隊執行,從而能夠嚴格保證執行順序,出錯的可能性大大降低了。

    函式系統不能夠直接支援Conditional模式,也就是條件啟用,而CB是支援的。關於這一點,以我個人的經驗,影響是不大的,過去1.8沒有

    Conditional不也是這麼過來了嗎?

    函式系統的主程序使用gamerule gameLoopFunction <名稱空間:函式>來掛載,而CB系統的"主程序"使用 RCB 作為高頻訊號源。

    在過去的版本,透過glf掛載的主程序,其執行者是系統,也就是server。這個設定會產生各種各樣的安全隱患,於是在後來的版本中,MOJANG將其執行者改成了glf所掛載的函式(前面也講到了)。就目前而言,僅僅透過函式系統,就能夠實現過去CB能夠實現的功能,甚至還有一些是CB難以實現的功能。在這裡就不過多講了,希望對大家有所啟發,可以研發各種各樣的黑科技出來~

    這裡插入講一點,我想對於地圖製作者來講是絕對的福音。函式系統中,呼叫的子模組會立即插隊執行,從而能夠嚴格保證執行順序,出錯的可能性大大降低了。

    函式系統不能夠直接支援Conditional模式,也就是條件啟用,而CB是支援的。關於這一點,以我個人的經驗,影響是不大的,過去1.8沒有

    Conditional不也是這麼過來了嗎?

    函式系統的主程序使用gamerule gameLoopFunction <名稱空間:函式>來掛載,而CB系統的"主程序"使用 RCB 作為高頻訊號源。

    在過去的版本,透過glf掛載的主程序,其執行者是系統,也就是server。這個設定會產生各種各樣的安全隱患,於是在後來的版本中,MOJANG將其執行者改成了glf所掛載的函式(前面也講到了)。就目前而言,僅僅透過函式系統,就能夠實現過去CB能夠實現的功能,甚至還有一些是CB難以實現的功能。在這裡就不過多講了,希望對大家有所啟發,可以研發各種各樣的黑科技出來~

    這裡插入講一點,我想對於地圖製作者來講是絕對的福音。

    mcf系統直接支援樣式程式碼§。

    CB系統的顏色黑科技什麼的在這個面前根本不值一提。

    資源佔用方面,簡單說一下我個人的經驗。

    我們花了不到一天的時間把《喋血冰封II》升級到新的命令系統。新系統在資源佔用方面明顯比之前龐大的CB系統少了很多,流暢度不降反升,這也得益於函式系統更加接近遊戲底層。CB系統在方塊更新這一方面就輸掉了一大截。更何況它需要佔地想一下,如果你的系統足夠龐大,出生地可以載入的區域放多CB,你能夠記得住嗎?你在除錯系統的時候,需要花多少時間去找到你要修改的指令呢?

    此外,對於一些不放在出生點的模組,我們還需要考慮到區塊載入的問題,相信這也是讓許多人頭疼的問題吧?

    函式系統顯然不需要擔心這個,因為它所有的內容都儲存在檔案裡,不具體地出現在遊戲世界中,在資源佔用方面相比與CB系統而言,是要佔優的。

    我們知道,寫一個功能可能只要一兩天,debug可能要一週。過去CB系統,不依靠編輯器的話,你得手動檢查,如果要在中間插入什麼指令的話,還得整體移動CB,實際工作效率是十分感人的;藉助於編輯器,我們可以透過ooc匯入的方式來實現快速修改

    而函式系統呢?你需要改點什麼,直接去翻檔案改,改完了儲存一下,再在遊戲裡透過/reload指令直接重新整理,完事兒了。遊戲都不用退出重進。

    但凡地圖製作者,知道了這些,都應該會心動的吧。

    講了這麼多,相信大家對新系統也有一定的瞭解了,說不定已經激動得說不出話來了吧,那麼更多內容就請大家自行去體驗一下吧。在接下來的更新裡,沒準還會多出什麼意想不到的東西呢!

  • 中秋節和大豐收的關聯?
  • 最近熱播的電視劇《安家》裡面最可惡的角色是誰?