用React 來寫OOXX小遊戲

Joe Chang
Coding Hot Pot
Published in
Jan 20, 2021
photo by @pawel_czerwinski

OOXX小遊戲也可以說是很熱門的練習題,所以今天就用React 來寫一個OOXX小遊戲吧!

初始化先設定一個長度為9的陣列來記錄玩家下的位置,這邊用了array.from({length}*9) 就會製造出一個長度為9的陣列

const [record, setRecord] = useState(Array.from({ length: 9 }))

但因為我沒有另外寫callback function去設定 ,所以實際上這個陣列就會是 [undefined,undefined,…undefined]

樣式的部分就寫在module.scss, 托flex的福 ,可以很快的解決九宮格css的部分,切完版之後,我需要抓到玩家點擊的是第幾個格子,所以我這樣寫 ,看玩家點擊哪一個格子就把那個格子index帶入,看起來一切都很合理…

<div onClick={handclick(index)} ></div>

然後畫面就掛掉了。

紅字錯誤顯示無限迴圈 ,我沒料到這樣寫handclick(index)就會馬上執行 ,因為vue這樣寫都沒事(喂),因為handclick function 改變了record的陣列,然後我的九宮格格子是依靠record陣列繪製出來的,再次render變成無窮迴圈,所以這邊記得要改成arrow function,就可以正常運作

接者列出贏家的畫線有幾種可能, 這邊用九宮格的位置來記錄,第一格index為0…最後一格為8

const arr = [[0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 4, 8], [2, 4, 6]];

設定玩家a為圈圈,用1來代表,玩家b則為叉叉,用-1來代表,由玩家a先開始,所以初始值設定為1,玩家a下完之後,如果沒有勝負,則輪到玩家b

const [player, setPlayer] = useState(1)

每當玩家點擊格子, 該格子就會依照是哪位玩家 ,就放入對應的數字 1 or -1 ,並且馬上比對剛剛列出八種勝出方式的陣列,並將格子內的ox轉換成對應的數字加總並且取絕對值 ,如果為3就是有人勝出了, 中斷迴圈開始準備畫線

ex 如果record 陣列這三個index (0,1,2)的數字加起來為3 or -3 就代表有人勝出了!(可以畫出最上排的水平線)

用canvas來畫線,在一開始就已經建立好畫布,只是先隱藏起來,這邊用偷懶的方式, 設定一個x、y的陣列,大概抓個格子的中線位置,所以畫的線不會很精準,先取出獲勝的組合假設是1、4、7好了,抓陣列第一個數字為起點,陣列最後一個數字為終點,再利用除法和取餘數,拿到這兩個格子是位在第幾列的第幾行

但是,這時又碰到一個小小的問題

利用useRef來抓dom裡的canvas,殊不知一直抓不到 !! 一直是undefined ,這時候推斷是雖然我即時將canvas更換為顯示的狀態,但是因為非同步的關係,下一行的canvasRef.current此時還是undefined狀態

一開始用了一個比較蠢的解法是 setTimeout(() => {})的確可以運作,但這是這樣寫用膝蓋想也知道不合理,後來查到比較正確的方式是在useEffect階段來取得useRef,不過也失敗了,為什麼?因為我的canvas預設是隱藏,所以useRef抓到的就會是null,問題好像越變越複雜,仔細想想,其實canvas也沒必要隱藏,只要可以讓使用者點擊到被canvas蓋住的格子就可以了,所以就在canvas身上設定css穿透屬性:pointer-events: none;就解決了!

完整程式碼

最終成品!

--

--

Joe Chang
Coding Hot Pot

前端工程師,唯有非常努力,才能看起來毫不費力