iT邦幫忙

2021 iThome 鐵人賽

DAY 30
0
Software Development

在麥塊的農場裡寫 Lua系列 第 30

Day30 小烏龜應用整合篇

今天來一起看看各種小烏龜一起工作吧
Mining Turtle 我已經很熟了,可以挖礦直達地底
那拿鑽石斧的 Felling Turtle 呢?
我本以為它只能砍樹,實測發現它也能挖礦
而 Mining Turtle 也能砍樹
所以我不太確定兩者有什麼差異 @@

令我比較訝異的是,拿鑽石鏟的 Digging Turtle
我本以為它至少能鏟土和沙子
實測卻發現連鏟土都無法,只能鏟沙,感覺非常不實用 ><

鋤頭龜的作業流程

至於拿鑽石鋤頭的 Farming Turtle
可以在其儲物箱先備好植物種子和骨粉
整個種植過程的 API 呼叫如下:

  1. turtle.dig() 翻土除草
  2. turtle.place() 撒種子
  3. turtle.select(n) n 是骨粉的位置
  4. turtle.place() 撒骨粉
    這裡可以用回圈判斷 place 回傳值,true 代表可以繼續灑骨粉,false 代表作物已經長成、或是不能再用骨粉加速生長了
  5. turtle.dig() 收割作物

接下來我試著把之前學到的東西一起複習並應用

挖出樓梯通道的小烏龜

之前已介紹過,挖礦龜可以掘井挖隧道
但如果玩家想要到達地底,總不能朝著深井跳下去吧 .....
所以必須改寫 /rom/programs/turtle/excavate.lua 讓這支程式可以挖樓梯出來!
部分程式碼如下,因為有點長,我就直接在程式碼註解說明每一段的大概

在這之前,我先設置好 GPS 基地台,請參考之前的文章

-- 讓玩家指定挖掘的深度,例如 mining 5 那就只會挖出深度 5 的樓梯,然後折返
local depthLimit = tonumber(tArgs[1])
if depthLimit < 1 then
    print("Depth must be positive")
    return
end

-- 目前挖掘的深度
local depth = 0
-- 吐出的東西總數
local unloaded = 0
-- 收集的東西總數
local collected = 0
local protocol = 'mining'   --  這是傳訊時讓監控中心分辨用的,如果是鏟子龜,protocol 就是 digging,砍樹龜則是 'felling'

peripheral.find("modem", rednet.open) -- 找到身上的所有 modem 後打開它
rednet.host(protocol, 'yoshi')

-- 印出訊息,同時傳送到網路上給監控中心。這裡用的是廣播,只要有監聽 protocol 都可收到
local function printThenSend(message)
    print(message)
    rednet.broadcast(message, protocol)
end

-- 把東西丟出來放到地上,這是挖掘完畢後回到地上的作業
local function unload()
    printThenSend("Unloading")
    for n = 1, 16 do
        local nCount = turtle.getItemCount(n)
        local data = turtle.getItemDetail(n)
        -- 不要把火把丟掉,可能還會再用
        if nCount > 0 and data.name ~= 'minecraft:torch' then
            turtle.select(n)
            turtle.drop()
            unloaded = unloaded + nCount
        end
    end
    collected = 0
    turtle.select(1)
end

local function collect()
    -- 收集物品。略,這部分跟原本 excavate.lua 大同小異
end


-- 從儲物箱內尋找火把,然後放置在左方,放完後再轉回正前方。這麼做是為了照耀通道,好讓主人走下去 XD
local function placeTorch()
    for n = 1, 16 do
        local nCount = turtle.getItemCount(n)
        if nCount > 0 then
            turtle.select(n)
            local data = turtle.getItemDetail()
            if data.name == 'minecraft:torch' then
                turtle.place()
                break
            end
        end
    end
end

-- 因為我已經架設好 GPS 基地台,所以每隻小烏龜只要有 Ender Modem 都可以隨時取得 GPS 座標並回報
local function reportPosition(currentDepth)
    if math.fmod(currentDepth, 10) == 0 then
        local x, y, z = gps.locate(2, false)
        local msg = string.format("Depth: %s  GPS: %d %d %d", currentDepth, x, y, z)
        printThenSend(msg)
    end
end

-- @tparam boolean dig 是否邊移動邊挖掘? 如果 dig = false, 小烏龜只會嘗試移動,不挖任何東西
local function tryForwards(dig)
    while not turtle.forward() do
        if turtle.detect() then
            if dig and turtle.dig() then
                collect()
            else
                return false
            end
        elseif turtle.attack() then
            collect()
        else
            sleep(0.5)
        end
    end

    if dig then
        -- 往前多挖一格,這樣玩家才能走下去
        turtle.dig()
        -- 往左邊挖一格,讓走道寬一點
        turtle.turnLeft()
        turtle.dig()
        turtle.turnRight()
    else
        turtle.forward()
    end
    
    return true
end

-- @tparam boolean dig 是否邊移動邊挖掘? 如果 dig = false, 小烏龜只會嘗試移動,不挖任何東西
local function tryDown(dig)
    while not turtle.down() do
        if turtle.detectDown() then
            if dig and turtle.digDown() then
                collect()
            else
                return false
            end
        elseif turtle.attackDown() then
            collect()
        else
            sleep(0.5)
        end
    end

    -- 挖寬兩格
    if dig then
        turtle.turnLeft()
        turtle.dig()
        -- 深度每 6 格放一支火把
        if dig and (depth % 6 == 0) then
            placeTorch()
        end
        turtle.turnRight()
    end

    depth = depth + 1
    reportPosition(depth)

    return true
end

-- @tparam boolean dig 是否邊移動邊挖掘? 如果 dig = false, 小烏龜只會嘗試移動,不挖任何東西
local function tryUp(dig)
    while not turtle.up() do
        if turtle.detectUp() then
            if dig and turtle.digUp() then
                collect()
            else
                return false
            end
        elseif turtle.attackUp() then
            collect()
        else
            sleep(0.5)
        end
    end

    depth = depth - 1
    reportPosition(depth)

    return true
end


local done = false
-- 先往下開挖,直到指定深度
while not done do
    if not tryForwards(true) or not tryDown(true) then
        done = true
        break
    end
    if depth >= depthLimit then
        break
    end

end

printThenSend("Returning")

-- 挖到指定深度後,開始回程。迴轉後往上走
turtle.turnLeft()
turtle.turnLeft()
done = false
while not done do
    if not tryUp(false) or not tryForwards(false) then
        done = true
        break
    end
    if depth == 0 then
        break
    end
end

unload()

上述的程式碼,大約指定深度 50 ,就可以挖出這樣的樓梯通道
雖然形狀有點怪,還需要再優化 XD
CC: Tweaked Mining Turtle Stairs

回到地面上後,把東西都一股腦丟出來了
CC: Tweaked Mining Turtle unload

而挖掘過程中,小烏龜的螢幕顯示就像這樣
CC: Tweaked Mining Turtle monitor

不過,我們不可能就這樣跟著小烏龜下去,它挖到哪,我們走到哪
所以這時候就需要 Ender Modem 把小烏龜狀態傳達給監控中心了!

設置監控小烏龜作業的中心電腦

首先我有一台有 Ender Modem 的電腦,並接上三台大螢幕
分別監控三種 protocol 訊息:mining、digging、felling
也就是挖礦龜、鏟沙龜、砍樹龜的作業
程式碼如下

local centerMonitor = peripheral.wrap("monitor_5")
local leftMonitor = peripheral.wrap("monitor_4")
local rightMonitor = peripheral.wrap("monitor_2")

rednet.open('right')
rednet.host('monitor', 'center')    --  這台電腦的 protocol 是 monitor、名稱是 center

parallel.waitForAny(
    function ()
        while true do
            local senderID, message, protocol = rednet.receive('mining')
            term.redirect(centerMonitor)
            print("Turtle ID: " .. senderID)
            print("message: " .. message)
        end
    end,
    function ()
        while true do
            local senderID, message, protocol = rednet.receive('digging')
            term.redirect(leftMonitor)
            print("Turtle ID: " .. senderID)
            print("message: " .. message)
        end
    end,
    function ()
        while true do
            local senderID, message, protocol = rednet.receive('felling')
            term.redirect(rightMonitor)
            print("Turtle ID: " .. senderID)
            print("message: " .. message)
        end
    end
)

rednet.close()

我在三個 function 間持續不斷地切斷,只要有收到小烏龜訊息,就把 ID 和訊息發送到螢幕上顯示
這樣一來,監控中心可以根據不同的 protocol 來決定顯示方式
例如我把 mining 的訊息顯示到正中央大螢幕
digging 訊息顯示在左方螢幕,felling 訊息顯示在右方螢幕
執行過程大概的畫面如下
CC: Tweaked Turtles Monitor and Rednet

以上就是今天的分享了~

30 天鐵人賽感言

其實在這 30 天,沒辦法充分花時間在遊戲體驗
前期花了許多時間在熟悉 Lua 語言特性和語法
後期則一直在自己探索 CC: Tweaked 模組的使用方式
所以我的程式碼和範例應用其實都滿簡單的XD
我相信 CC: Tweaked 可以應用得非常有趣,讓很多事情都全自動化了
(那主人還要幹嘛 .....)

在國內幾乎沒有人把這模組介紹得非常詳細(應該是沒有)
只知道有些幼童程式語言學習的課程似乎有玩到,但沒有玩得很深入
說來說去就是 Turtle,並沒有從 Computer 開始的基礎介紹
所以只能找國外各種範例和文章,或是自己嘗試了~

感謝這三十天有追蹤和閱讀我文章的網友們
如果你也有玩麥塊,試試看,會很有趣~


上一篇
Day29 自動合成物品的小烏龜與指令
系列文
在麥塊的農場裡寫 Lua30

1 則留言

0
熟女卡卡
iT邦新手 5 級 ‧ 2021-10-19 16:40:26

恭喜恭喜帶著小烏龜完賽了~~~ /images/emoticon/emoticon08.gif

蛋踢球 iT邦新手 4 級 ‧ 2021-10-22 08:46:10 檢舉

/images/emoticon/emoticon01.gif
也恭喜卡卡完賽~

我要留言

立即登入留言