Try   HackMD

嗨,哈囉,大家好,我是流星若風

今天發文主要是想講述datapack的三個觀念

  1. Function (函式)
  2. loop (迴圈)
  3. variable (變數)
  4. delay (延遲)

提醒:一定要按照教學全部做完再執行function,不然絕對是災難,最重要的是,看完他!!!

撰文動機

在製作我的專案(翻轉歷史)時,遇到對話的指令方塊太冗長、datapack也沒有好用的方法,因此在想出後與各位巴友分享

名詞解釋

function

是一個大型程式中的某部份程式碼,由一個或多個語句塊組成。它負責完成某項特定工作,而且相較於其他程式碼,具備相對的獨立性。
——引述自 維基百科

簡單來說,function(函式)就是一段能重複使用的程式,在minecraft中 一個.mcfunction檔案就是一個function

loop(迴圈)

迴圈是一段在程式中只出現一次,但可能會連續執行多次的程式碼。迴圈中的程式碼會執行特定的次數,或者是執行到特定條件成立時結束迴圈,或者是針對某一集合中的所有項目都執行一次。
—— 引述自 維基百科

簡單來說,Loop(迴圈)就是能夠重複執行自身的一段程式

那function跟Loop的差別是什麼呢?
我認為,其實loop就是一種會重複執行(呼叫)自己的function

variable(變數)

變數可以指在電腦記憶體裏存在值的被命名的儲存空間。
—— 引述自 維基百科

delay(延遲)

指做出觸發動作與得到回應之間的時間間隔
——引述自 維基百科

簡單來說,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(特殊用途)
  1. 自訂名稱資料夾1(以下以"foobar1"代替資料夾名稱)

function

  1. func1.mcfunction
  1. 自定名稱資料夾2(以下以"foobar2"代替資料夾名稱)

func2.mcfunction

(這已經是稍微簡化的分法了,詳細請看阿猴大文章)
也就是說,所有的 .mcfunction 檔案有兩個可存放的地方,分別是"datapack"資料夾 下的

  1. foobar1/function/func1.mcfunction
  2. foobar1/function/foobar2/func2.mcfunction

理論上,你甚至可以在foobar2底下新增foobar3/foobar4 更多資料夾

PS:

  1. 沒有附檔名即為資料夾
  2. Foobar在程式中是指無意義,在此作為資料夾名稱
  3. func為function之縮寫

詳見阿猴大文章

指令中的函數路徑

簡單來說,就是 " foobar1:foobar2/func2 "

先打 "data" 底下的資料夾名稱(foobar1),
跳過一層function資料夾,
然後一層一層(foobar2/foobar3 )找到需要的 .mcfunction 檔案

ps:

  1. /指令 <必填參數> [選填參數]
  2. 另外還有像 {NBT標籤} 這樣的用法
  3. 懶得輸入全部語法的可以善用tab跟上下鍵,打/fu就可以看到選單了,再用上下鍵選則要的指令然後按tab就能自動輸入了,根本懶人福音
  4. 只有在聊天欄(按t)才要加"/",指令方塊家族跟 /execute run <要執行的指令>,都不用加"/"
  5. 詳情請見Minecraft wiki(點我)

當函式名稱為"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(點我)

這個指令的語法也十分簡單,只有:

/function <函數路徑>  

中文直翻:

/函數 <函數路徑>

那啥是函數路徑(wiki寫的是"<name>")?

只要寫成這樣子:

/function foobar1:foobar2/func2

就能讓function自攻自受自己呼叫自己了

因為它會呼叫自己、再呼叫自己循環往復
所以他一開始就不會停下來
也達到Loop的效果

variable

可是他這樣就會鬼畜的一直呼叫自己、一直呼叫自己
停都停不下來

我不要

什麼?你問那我想讓他重複幾次?
Hmmm 先來個10次如何?

所以,我們要讓程式數數(ㄕㄨˇ ㄕㄨˋ )
也就是用到variable(變數)
在minecraft中則是使用scorebroad(記分版)

/scoreboard

Minecraft wiki(點我)
語法 (只寫會用到的)

/scoreboard objectoves add <準則> <項分板名稱>
/scorrboard players add <玩家> <記分項名稱>

中文直翻

/記分板 計分項 增加 <準則> <記分項名稱> 
/記分板 玩家 增加 <玩家> <準則> <記分項名稱>

準則?

計分項的準則(criterion,複數為 criteria,遊戲內叫「條件」)決定了其行為,也就是要跟蹤什麼。下方的表格列舉了所有的準則。

簡單來說,就是這個計分項(變數)要跟什麼事情掛勾?

但在這裡,內建的準則只有符合我們的需求,我們必須使用 dummy(虛擬型) 這個準則,也就是不和其他東西掛勾,只用指令來調整

實作

先增加一個幫我記loop次數的計分項(變數),並命名為loop_count

/scoreboard objectoves add dummy loop_count

但有了計分項,還需要有個目標承載計分項
所以我在假目標(

loop_count)身上的loop_count 增加1,讓假目標身上增加loop_count這個計分項

然後讓 $loop_count 的 loop_count 都增加1

/scoreboard players add $loop_count loop_count 1

所以我現在全部的程式碼變成這樣:

/scoreboard objectoves add dummy loop_count /scoreboard players add $loop_count loop_count 1 /function foobar1:foobar2/func2

由於minecraft的datapack是由上往下執行,所以

/function foobar1:foobar2/func2

要放在最後才能讓前面的指令執行

這樣就能讓每 loop 一次就讓 loop_count 增加1了

要查看目前 loop_count 的數值,可以用

/scoreboard players get $loop_count loop_count 

可是,讓程式會數數以後,我還是沒辦法讓他停下來啊
所以要用到下個指令

/execute

Minecraft wiki(點我)
語法 (只寫會用到的)

/execute unless score <目標> <計分項> matches <數值> run function <函數路徑> 
/execute if score <目標> <計分項> matches <數值> run function <函數路徑> 

中文直翻:

/執行 如果後面的條件不成立則 [分數 <目標> <計分項> 符合 <數值>]([]是條件的部分) 執行 迴圈 <函數路徑> 
/執行 如果後面的條件成立則 [分數 <目標> <計分項> 符合 <數值>]([]是條件的部分) 執行 函數 <函數路徑> 

這又是一個大指令詳細去看wiki吧

現在我只想讓他執行十次,那就「判斷是否迴圈執行到第十次,是則結束迴圈」這樣如何?
Too young too simple
mcfunction這種垃圾指令才不會給你 returnbreak

所以要反向思考,「判斷是否迴圈執行到第十次,否則繼續迴圈(呼叫Function本身)」
所以我現在全部的程式碼變成這樣:

/execute unless score $loop_count loop_count matches 10 run function foobar1:foobar2/func2

所以目前的指令統合後會像這樣:

/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

語法

/schedule <函數路徑> <數值><時間單位>

中文直翻:

/計畫 <函數路徑> <數值><時間單位>

這個函式的功能就是在<數值><時間單位>後呼叫函式
那麼,我們只要把 run 後面的 function(指令名稱) 改成 schedule就好了啊

所以我現在全部的程式碼變成這樣:

/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可是,我專案中的對話需要不同的延遲時間ㄟ

那麼用unless就不太合適了
unless是某值以外的所有值
if是只有某值,方便分別每次的
所以我們需要使用if,來針對不同迴圈次數調整不同的的延遲時間

當有一個 if 就不能用 unless 了
不然,先遇到 unless 後面的 if 就沒了
e.g.:

/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時,會先符合第三行,真正想起作用的第四行就會因為第一行被執行而被跳過

所以我現在全部的程式碼變成這樣:

/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 來控制要在第幾次執行)

/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

之間

像這樣

/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上康康我的成品

感恩各位的觀看,我們 下次債建

後記

打一篇教學真滴累
感謝每一個願意在巴哈上打心得/教學文的大大們
寫一篇教學文實在是費心費力
中間想翻譯與解釋我的想法也是一段嚴酷的旅程
還要找維基百科等等的
在寫完這篇文後,讓自己表達想法的技能也更進一步

再次感恩看我吐苦水、耐心看完的U秀的你