iT邦幫忙

2022 iThome 鐵人賽

DAY 9
3

第二篇:油漆桶遊戲-實作篇

大綱

  • 遊戲內容
  • 製造地圖
  • 選擇數字
  • 遊戲運作
  • 在arcade運作遇到的限制

成果預覽


選擇數字後 可以切換到相對應的顏色
按下A鍵後從箭頭處開始變色 想辦法讓畫面變成相同顏色
遊戲連結

製造地圖

我們用上回講過的地圖生成函數改寫一下。
這邊要提醒一下arcade的特性,
在宣告陣列的時候不能是空陣列
所以我先在裡面塞東西。

// 地圖初始化
let map = [[0],[0]];
map = []
for (let i = 0; i < map_width; i++) {
    map.push([]);
    for (let j = 0; j < map_height; j++) {
        map[i].push(0);
    }
}

然後執行地圖生成,這些都是上回講過的,這邊就不細講了

// 生成斑點
function make_area(x:number, y:number, area_color:number, count:number) {
    let now_x = x; // 生成斑點的x座標
    let now_y = y; // 生成斑點的y座標
    for (let i = 0; i < count; i++) { // 嘗試生成count次
        if (map[now_x][now_y] == area_color) { // 如果已經是目標顏色,則不再生成
            i + 1;
        }
        map[now_x][now_y] = area_color; // 生成斑點
        let direction = Math.floor(Math.random() * 4);  // 隨機製造下次生成的方向
        if (direction == 0 && now_x > 0) {
            now_x -= 1;
        } else if (direction == 1 && now_x < map.length - 1) {
            now_x += 1;
        } else if (direction == 2 && now_y > 0) {
            now_y -= 1;
        } else if (direction == 3 && now_y < map.length - 1) {
            now_y += 1;
        }
    }
}
// 地圖生成
function create_map() {
    for (let i = 0; i < map.length; i++) {
        for (let j = 0; j < map.length; j++) {
            if (Math.random() > dispersion * 0.95) { // 有機率生成斑點
                make_area(i, j, Math.floor(Math.random() * color_number-1)+1, Math.floor((Math.random() + 1) * 5) * dispersion);
            }
        }
    }
}

最後輸出地圖。
這個輸出的部份我們不像在console一樣是用文字輸出。
所以我們先建立地圖物件

let map_object = sprites.create(img`
    ...
`, SpriteKind.Player)
map_object.setScale(8, ScaleAnchor.Middle)
map_object.setPosition(115, 100)

map_object.setScale(8, ScaleAnchor.Middle)設定物件像素比例
map_object.setPosition(115, 100)設定地圖位置
然後是改變地圖圖片

function print_map() {
    let create_map = image.create(map_width, map_height)
    for (let i = 0; i < map.length; i++) { 
        for (let j = 0; j < map.length; j++) {
            if(map[i][j] == 0){
                create_map.setPixel(i, j, 10)
            }else{
                create_map.setPixel(i, j, map[i][j])
            }
        }
    }
    map_object.setImage(create_map)
}

創建一個圖片
let creat_map = image.create(map_width, map_height)
設定像素顏色
create_map.setPixel(i, j, color)
下面這段的部分是因為arcade的0顏色是代表透明色 所以我將它改為代碼10的顏色

if(map[i][j] == 0){
    create_map.setPixel(i, j, 10)
}else{
    create_map.setPixel(i, j, map[i][j])
}

最後再讓地圖物件的圖片改為剛剛的create_map就完成地圖的顯示了

改變數字

構思
偵測按鍵按壓事件 > 改變數字 > 在畫面中視覺化
有了構思就可以開始實作了

controller.right.onEvent(ControllerButtonEvent.Pressed, function () {
   ...
})
controller.left.onEvent(ControllerButtonEvent.Pressed, function () {
    ...
})

上面的函式是取得右鍵的按鍵事件,
如果左鍵被按下就會執行裡面的調用函式。
下面則是左鍵。
接下來要在裡面做一些事

可以看到上圖,數字從零開始往右遞增。
所以我們宣告一個變數用來存取現在的數字。

let now_number = 0

然後往右+1往左-1。
再來,我們的數字只有0到9我們不能讓數字超過,
所以我們利用取餘的方式替代if判斷式。

蝦你說用if會更簡單喔? 我單純想秀不行嗎XD

controller.right.onEvent(ControllerButtonEvent.Pressed, function () {
    now_number = (now_number + 1) % 10
})
controller.left.onEvent(ControllerButtonEvent.Pressed, function () {
    now_number = (10+(now_number-1))%10
})

接下來就是顯示出來啦
創立一個陣列儲存圖片

let chose_number_img = [
    img`
        ...
    `,
    img`
        ...
    `,
    ...
    ]

然後在處理完數字加減後變更圖片
這邊用到object.setImage(Img)更換物件圖片

controller.right.onEvent(ControllerButtonEvent.Pressed, function () {
    now_number = (now_number + 1) % 10
    chose_number.setImage(chose_number_img[now_number])
})
controller.left.onEvent(ControllerButtonEvent.Pressed, function () {
    now_number = (now_number + 1) % 10
    chose_number.setImage(chose_number_img[now_number])
})

這樣選擇的部分就告一段落了。

遊戲運作

//main
create_map(); // 生成地圖
print_map(); // 輸出地圖

開始時先呼叫函數
再來是實際玩的部分
一樣,先做構想
按下A鍵後 > 傳入now_number > 執行flood fill演算法 > 改變圖片

controller.A.onEvent(ControllerButtonEvent.Pressed, function () {
    flood_fill(map[0][0], now_number, 0, 0)
    print_map()
    if (check_end()) { // 檢查是否結束
        console.log("you win");

        game.over(true)
    }
})

所我們每次按下A鍵的時候執行flood fill演算法改變map陣列
再來執行print_map將結果印出來
最後看看是否都是同一個顏色了,結束了就結束遊戲。
檢查函數就沒甚麼好說的了,各位看看應該就懂了。

// 檢查是否結束
function check_end() {
    let end = true;
    let color = map[0][0]; // 設定初始顏色
    for (let i = 0; i < map.length; i++) {
        for (let j = 0; j < map.length; j++) {
            if (map[i][j] != color) { // 如果有不同的顏色,則還沒結束
                end = false;
                return end;
            }
        }

    }
    return end;
}

恭喜各位看到這裡,基本上遊戲就做完啦!
不過有人想說這地圖有點小,我可以增加難度讓地圖變大嗎?

在arcade運作遇到的限制

各位還記的上回我說過flood fill是怎麼運作的嗎?
沒錯是用遞迴的方式。
所以有學過遞迴的現在應該想到一件事,遞迴有層數限制
但在python或是javascript本身理論上可以跑到九百多層。
不過各位的arcade機是用單片機運作的,效能跟電腦差了好幾倍。
說到這邊大家應該理解了。沒錯!arcade的遞迴層數沒有很多,具體有多少我沒有實測。
不過你如果地圖太大會讓搜尋的時候遞迴太多層,導致程式直接被kill掉。
目前這個大小算式在可以邊緣,有可能會被kill不過大部分時間是可以完成遊戲的。


下期預告

各種棋篇
>_一篇文章教你製作連線相關的棋類
黑白棋 立體四子棋 圈圈叉叉

作者:鍾佳龍

上一篇
Arcade再進化-橫向捲軸遊戲(1)
下一篇
Arcade再進化-空島跳躍者(2)
系列文
玩game學打code。街機程式設計再進化。微軟Arcade30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

我要留言

立即登入留言