--- title: 在datapack裡做出迴圈與延遲! tags: 巴哈發文 lang: zh-tw image: description: 想知道怎麼在datapack裡用迴圈與延遲嗎?點進來吧~ --- # 嗨,哈囉,大家好,我是流星若風 今天發文主要是想講述datapack的三個觀念 1. Function (函式) 2. loop (迴圈) 3. variable (變數) 4. delay (延遲) ## 提醒:一定要按照教學全部做完再執行function,不然絕對是災難,最重要的是,看完他!!! ### 撰文動機 在製作[我的專案(翻轉歷史)](https://hackmd.io/@MC-HistoryRPG/historyrpg)時,遇到對話的指令方塊太冗長、datapack也沒有好用的方法,因此在想出後與各位巴友分享 # 名詞解釋 ## function > 是一個大型程式中的某部份程式碼,由一個或多個語句塊組成。它負責完成某項特定工作,而且相較於其他程式碼,具備相對的獨立性。 ——引述自 [維基百科](https://zh.wikipedia.org/wiki/%E5%AD%90%E7%A8%8B%E5%BA%8F) **簡單來說,function(函式)就是一段能重複使用的程式,在minecraft中 ==一個.mcfunction檔案就是一個function==** ## loop(迴圈) > 迴圈是一段在程式中只出現一次,但可能會連續執行多次的程式碼。迴圈中的程式碼會執行特定的次數,或者是執行到特定條件成立時結束迴圈,或者是針對某一集合中的所有項目都執行一次。 —— 引述自 [維基百科](https://zh.m.wikipedia.org/zh-tw/%E8%BF%B4%E5%9C%88) **簡單來說,Loop(迴圈)就是能夠重複執行自身的一段程式** 那function跟Loop的差別是什麼呢? 我認為,**其實loop就是一種會重複執行(呼叫)自己的function** ## variable(變數) > 變數可以指在電腦記憶體裏存在值的被命名的儲存空間。 —— 引述自 [維基百科](https://zh.m.wikipedia.org/zh-tw/%E8%AE%8A%E6%95%B8) ## delay(延遲) > 指做出觸發動作與得到回應之間的時間間隔 ——引述自 [維基百科](https://zh.m.wikipedia.org/zh-tw/%E5%BB%B6%E8%BF%9F_(%E5%B7%A5%E7%A8%8B%E5%AD%A6)) **簡單來說,deley(延遲)就是等待一段時間後,繼續往下執行剩下的指令** # function ## 什麼是.mcfunction .mcfunction 是一種文字檔的副檔名,專門存放以 mcfunction (一行一行的Minecraft原版指令)的一種檔案,==**一個檔案就是一個function**==,但是是 mc(MInecraft) 專用的function ,所以叫mcfunction ## function存放規則 順便補一點基本知識: 1. minecraft 的datapack 通常都存放在 ==world== 資料夾 2. 要用小寫取代大寫 3. 用_(底線)取代空格 本篇以 world 資料夾當作起點,有些會把 world 存在 .minecraft/save 裡,這裡的 world 也可指代地圖存檔或別人做好的地圖檔 準確地說,路徑是這樣的 >world >>datapack >>>data >>>>1. minecraft(特殊用途) >>>>> >>>>2. 自訂名稱資料夾1(以下以"foobar1"代替資料夾名稱) >>>>>function >>>>>>1. func1.mcfunction >>>>>>> >>>>>>2. 自定名稱資料夾2(以下以"foobar2"代替資料夾名稱) >>>>>>> func2.mcfunction (這已經是稍微簡化的分法了,詳細請看[阿猴大文章](https://forum.gamer.com.tw/Co.php?bsn=18673&sn=880050)) 也就是說,所有的 .mcfunction 檔案有兩個可存放的地方,分別是"datapack"資料夾 下的 1. foobar1/function/func1.mcfunction 2. foobar1/function/foobar2/func2.mcfunction 理論上,你甚至可以在foobar2底下新增foobar3/foobar4 ......更多資料夾 PS: 1. 沒有附檔名即為資料夾 2. Foobar在程式中是指無意義,在此作為資料夾名稱 3. func為function之縮寫 ### [詳見阿猴大文章](https://forum.gamer.com.tw/Co.php?bsn=18673&sn=880050) ## 指令中的函數路徑 **簡單來說,就是 " foobar1:foobar2/func2 "** 先打 **"data"** 底下的資料夾名稱(foobar1), 跳過一層function資料夾, 然後一層一層(foobar2/foobar3 ...)找到需要的 .mcfunction 檔案 ps: 1. /指令 <必填參數> [選填參數] 2. 另外還有像 {NBT標籤} 這樣的用法 3. 懶得輸入全部語法的可以善用tab跟上下鍵,打/fu就可以看到選單了,再用上下鍵選則要的指令然後按tab就能自動輸入了,根本懶人福音 4. 只有在聊天欄(按t)才要加"/",指令方塊家族跟 /execute run <要執行的指令>,都不用加"/" 5. 詳情請見[Minecraft wiki(點我)](https://minecraft.fandom.com/zh/wiki/%E5%91%BD%E4%BB%A4?variant=zh-tw) 當函式名稱為"func2.mcfunction" 並存放在 world/datapack/data/foobar1/function/foobar2/func2.mcfunction > world >> data >>> foobar1(必要) >>>> function >>>>> foobar2 >>>>>> func2.mcfunction # Loop 要怎麼達到loop的效果呢? 很簡單,剛剛有提過,loop 就是會重複執行的 function 反過來說,讓 function 重複執行就可以做出 loop 了 那要怎麼執行(呼喚)function 呢 只要在 function 的最後自己呼喚自己就好了 這時候就要用到/function了 ## /function [Minecraft wiki(點我)](https://minecraft.fandom.com/zh/wiki/%E5%91%BD%E4%BB%A4/function) 這個指令的語法也十分簡單,只有: ```mcfunction! /function <函數路徑> ``` 中文直翻: ```mcfunction! /函數 <函數路徑> ``` 那啥是函數路徑(wiki寫的是"\<name>")? 只要寫成這樣子: ```mcfunction! /function foobar1:foobar2/func2 ``` 就能讓function~~自攻自受~~自己呼叫自己了 因為它會呼叫自己、再呼叫自己......循環往復 所以他一開始就不會停下來 也達到Loop的效果 # variable 可是他這樣就會鬼畜的一直呼叫自己、一直呼叫自己 停都停不下來 我不要 什麼?你問那我想讓他重複幾次? Hmmm... 先來個10次如何? 所以,我們要讓程式數數(ㄕㄨˇ ㄕㄨˋ ) 也就是用到variable(變數) 在minecraft中則是使用scorebroad(記分版) ## /scoreboard [Minecraft wiki(點我)](https://minecraft.fandom.com/zh/wiki/%E8%AE%B0%E5%88%86%E6%9D%BF#%E5%87%86%E5%88%99) 語法 (只寫會用到的) ```mcfunction! /scoreboard objectoves add <準則> <項分板名稱> ``` ```mcfunction! /scorrboard players add <玩家> <記分項名稱> ``` 中文直翻 ```mcfunction! /記分板 計分項 增加 <準則> <記分項名稱> ``` ```mcfunction! /記分板 玩家 增加 <玩家> <準則> <記分項名稱> ``` ### 準則? 計分項的準則(criterion,複數為 criteria,遊戲內叫「條件」)決定了其行為,也就是要跟蹤什麼。下方的表格列舉了所有的準則。 #### 簡單來說,就是這個計分項(變數)要跟什麼事情掛勾? 但在這裡,內建的準則只有符合我們的需求,我們必須使用 dummy(虛擬型) 這個準則,也就是不和其他東西掛勾,只用指令來調整 ### 實作 先增加一個幫我記loop次數的計分項(變數),並命名為loop_count ```mcfunction! /scoreboard objectoves add dummy loop_count ``` 但有了計分項,還需要有個目標承載計分項 所以我在假目標($名稱,$loop_count)身上的loop_count 增加1,讓假目標身上增加loop_count這個計分項 然後讓 $loop_count 的 loop_count 都增加1 ```mcfunction! /scoreboard players add $loop_count loop_count 1 ``` 所以我現在全部的程式碼變成這樣: ```mcfunction= /scoreboard objectoves add dummy loop_count /scoreboard players add $loop_count loop_count 1 /function foobar1:foobar2/func2 ``` 由於minecraft的datapack是由上往下執行,所以 ```mcfunction! /function foobar1:foobar2/func2 ``` 要放在最後才能讓前面的指令執行 這樣就能讓每 loop 一次就讓 loop_count 增加1了 要查看目前 loop_count 的數值,可以用 ```mcfunction! /scoreboard players get $loop_count loop_count ``` 可是,讓程式會數數以後,我還是沒辦法讓他停下來啊 所以要用到下個指令 ## /execute [Minecraft wiki(點我)](https://minecraft.fandom.com/zh/wiki/%E5%91%BD%E4%BB%A4/execute?so=search) 語法 (只寫會用到的) ```mcfunction! /execute unless score <目標> <計分項> matches <數值> run function <函數路徑> ``` ```mcfunction! /execute if score <目標> <計分項> matches <數值> run function <函數路徑> ``` 中文直翻: ```mcfunction! /執行 如果後面的條件不成立則 [分數 <目標> <計分項> 符合 <數值>]([]是條件的部分) 執行 迴圈 <函數路徑> ``` ```mcfunction! /執行 如果後面的條件成立則 [分數 <目標> <計分項> 符合 <數值>]([]是條件的部分) 執行 函數 <函數路徑> ``` 這又是一個大指令...詳細去看wiki吧 現在我只想讓他執行十次,那就「判斷是否迴圈執行到第十次,是則結束迴圈」這樣如何? Too young too simple mcfunction這種~~垃圾~~指令才不會給你 ```return``` 或 ```break``` 呢 所以要反向思考,「判斷是否迴圈執行到第十次,否則繼續迴圈(呼叫Function本身)」 所以我現在全部的程式碼變成這樣: ```mcfunction! /execute unless score $loop_count loop_count matches 10 run function foobar1:foobar2/func2 ``` 所以目前的指令統合後會像這樣: ```mcfunction= /scoreboard objectoves add dummy loop_count /scoreboard players add $loop_count loop_count 1 /execute unless score $loop_count loop_count matches 10 run function foobar1:foobar2/func2 ``` Ya~問題都解決了,可以去領便當了? 嗯?你問不是還有個deley? 可惡,既然被發現了,那我就勉強告訴你吧(傲嬌? # delay 我想要在每次執行之間都隔一秒,怎麼辦? 用/schedule吧 ## /schedule 語法 ```mcfunction! /schedule <函數路徑> <數值><時間單位> ``` 中文直翻: ```mcfunction! /計畫 <函數路徑> <數值><時間單位> ``` 這個函式的功能就是在<數值><時間單位>後呼叫函式 那麼,我們只要把 run 後面的 function(指令名稱) 改成 schedule就好了啊 所以我現在全部的程式碼變成這樣: ```mcfunction= /scoreboard objectoves add dummy loop_count /scoreboard players add $loop_count loop_count 1 /execute unless score $loop_count loop_count matches 10 run schedule foobar1:foobar2/func2 1s ``` Hmm......可是,我[專案](https://hackmd.io/@MC-HistoryRPG/historyrpg)中的對話需要不同的延遲時間ㄟ 那麼用unless就不太合適了 unless是某值以外的所有值 if是只有某值,方便分別每次的 所以我們需要使用if,來針對不同迴圈次數調整不同的的延遲時間 當有一個 if 就不能用 unless 了 不然,先遇到 unless 後面的 if 就沒了 e.g.: ```mcfunction= /scoreboard objectoves add dummy loop_count /scoreboard players add $loop_count loop_count 1 /execute unless $loop_count loop_count matches 1 run schedule foobar1:foobar2/func2 1s /execute if score $loop_count loop_count matches 2 run schedule foobar1:foobar2/func2 1s ``` 當 $loop_count 的 loop_count 為2時,會先符合第三行,真正想起作用的第四行就會因為第一行被執行而被跳過 所以我現在全部的程式碼變成這樣: ```mcfunction= /scoreboard objectoves add dummy loop_count /scoreboard players add $loop_count loop_count 1 /execute if $loop_count loop_count matches 1 run schedule foobar1:foobar2/func2 1s /execute if score $loop_count loop_count matches 2 run schedule foobar1:foobar2/func2 1s ``` 再把想要執行的其他指令插入(比如 say1 跟 say2 ,同樣加上 execute 來控制要在第幾次執行) ```mcfunction= /execute if $loop_count loop_count matches 1 run schedule foobar1:foobar2/func2 1s ``` 跟 ```mcfunction= /execute if score $loop_count loop_count matches 2 run schedule foobar1:foobar2/func2 1s ``` 之間 像這樣 ```mcfunction= /scoreboard objectoves add dummy loop_count /scoreboard players add $loop_count loop_count 1 /execute if $loop_count loop_count matches 1 run say 1 /execute if $loop_count loop_count matches 1 run schedule foobar1:foobar2/func2 1s /execute if score $loop_count loop_count matches 2 run say 2 /execute if score $loop_count loop_count matches 2 run schedule foobar1:foobar2/func2 1s ``` PS: 1. 建議把不同的迴圈次數用換行分開 2. 呼叫自己是要放在一個區塊的最後、以免要執行的指令被洗掉 3. 之後可以 match 3,4,5 ..... 一直持續下去 4. 可以到[github](https://github.com/Skywind5487/MRH_datapack)上康康我的成品 感恩各位的觀看,我們 下次債建 # 後記 打一篇教學真滴累 感謝每一個願意在巴哈上打心得/教學文的大大們 寫一篇教學文實在是費心費力 中間想翻譯與解釋我的想法也是一段嚴酷的旅程...... 還要找維基百科等等的 在寫完這篇文後,讓自己表達想法的技能也更進一步 再次感恩看我吐苦水、耐心看完的U秀的你  
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up