--- tags: 2022 iThome 鐵人賽 title: 9/24 Arcade也能學演算法Ouo!? 演算法遊戲(2) AUTHOR: 鍾佳龍 --- # Arcade再進化 ## Arcade也能學演算法Ouo!? 演算法遊戲!(2) ### **第二篇:油漆桶遊戲-實作篇** > ### 大綱 > * 遊戲內容 > * 製造地圖 > * 選擇數字 > * 遊戲運作 > * 在arcade運作遇到的限制 > ## 成果預覽 ![](https://i.imgur.com/LkofOqF.gif ) [遊戲連結](https://makecode.com/_9yfiWkC6y5zv) 選擇數字後 可以切換到相對應的顏色 按下A鍵後從箭頭處開始變色 想辦法讓畫面變成相同顏色 > ## 製造地圖 我們用上回講過的地圖生成函數改寫一下。 這邊要提醒一下arcade的特性, 在宣告陣列的時候不能是空陣列 所以我先在裡面塞東西。 ```javascript= // 地圖初始化 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); } } ``` 然後執行地圖生成,這些都是上回講過的,這邊就不細講了 ```javascript= // 生成斑點 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一樣是用文字輸出。 所以我們先建立地圖物件 ```javascript= 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)`設定地圖位置 然後是改變地圖圖片 ```javascript= 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的顏色 ```javascript= if(map[i][j] == 0){ create_map.setPixel(i, j, 10) }else{ create_map.setPixel(i, j, map[i][j]) } ``` 最後再讓地圖物件的圖片改為剛剛的`create_map`就完成地圖的顯示了 > ## 改變數字 **構思** **偵測按鍵按壓事件 > 改變數字 > 在畫面中視覺化** 有了構思就可以開始實作了 ```javascript= controller.right.onEvent(ControllerButtonEvent.Pressed, function () { ... }) controller.left.onEvent(ControllerButtonEvent.Pressed, function () { ... }) ``` 上面的函式是取得右鍵的按鍵事件, 如果左鍵被按下就會執行裡面的調用函式。 下面則是左鍵。 接下來要在裡面做一些事 ![](https://i.imgur.com/9IpeuL5.png) 可以看到上圖,數字從零開始往右遞增。 所以我們宣告一個變數用來存取現在的數字。 ```javascript= let now_number = 0 ``` 然後往右+1往左-1。 再來,我們的數字只有0到9我們不能讓數字超過, 所以我們利用取餘的方式替代if判斷式。 ![](https://i.imgur.com/cv2agWp.jpg) *蝦你說用if會更簡單喔? ~~我單純想秀不行嗎~~XD* ```javascript= 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 }) ``` 接下來就是顯示出來啦 創立一個陣列儲存圖片 ```javascript= let chose_number_img = [ img` ... `, img` ... `, ... ] ``` 然後在處理完數字加減後變更圖片 這邊用到`object.setImage(Img)`更換物件圖片 ```javascript= 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]) }) ``` 這樣選擇的部分就告一段落了。 > ## 遊戲運作 ```javascript= //main create_map(); // 生成地圖 print_map(); // 輸出地圖 ``` 開始時先呼叫函數 再來是實際玩的部分 **一樣,先做構想** **按下A鍵後 > 傳入now_number > 執行flood fill演算法 > 改變圖片** ```javascript 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`將結果印出來 最後看看是否都是同一個顏色了,結束了就結束遊戲。 檢查函數就沒甚麼好說的了,各位看看應該就懂了。 ```javascript= // 檢查是否結束 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不過大部分時間是可以完成遊戲的。 --- > ## 下期預告 > **各種棋篇** > \>\_一篇文章教你製作連線相關的棋類 > *黑白棋 立體四子棋 圈圈叉叉* ###### 作者:鍾佳龍