為了學習Canvas,寫了這個小游戲貪吃蛇供自己和大家學習
Github: https://github.com/zhiyishou/Gsnake
Play On: http://zhiyishou.github.io/Gsnake
游戲截圖:
前言:
為了方便加載轉移,我把整個js都寫在了html里,為了方便閱讀,將函數結構在html里清晰地分開,
並在代碼里有足夠注釋。
函數結構如下:
|----script |----Definations |----Global Snake variables |----Global Canvas variables |----Global Panel variables |----Global Stage variables |----Global Game status variables |----Init Functions |----initPanel |----initButtons |----initStage |----initCanvas |----initMaps |----SnakeNode |----initSnake |----produceSingle |----init |----Draw Funcitons |----drawScore |----drawButton |----drawButtons |----drawSnake |----drawSingle |----drawStage |----draw |----Action Functions |----moveSnake |----main |----Event Functions |----KeyDown |----getOffsetPosition |----determineButton |----MouseMove |----ClickButton |----debounce |----bind |----Pause |----Start |----ReStart |----Died |----ROCK and ROLL |----init() |----main()
其中碰到的問題與解決:
一、鼠標事件問題
Canvas 中無法實現內部事件的添加和刪除,准確的來說,在Canvas就是一張單純的畫布,整個Canvas才能做Dom中的事件操作
如果想在Canvas中實現內部click或mousemove等事件,有兩種方案來實現:
1、用四周邊界來確定:
- 基於:
- 對Canvas綁定事件后,每當有事件發生,則計算當前鼠標相對於Canvas的坐標值;
- 在每個要綁定事件的對象中設定其四邊邊距坐標,並將其放在一個數組;
- 觸發:事件發生時則遍歷整個數組,來根據坐標來判斷是否在哪一個對象的邊界范圍內,來確定鼠標現在所在的對象。
- 缺點:這個做的話要求對象只能是形狀規整的直角四邊形,對復雜圖形的處理沒有可實施性。
2、使用CanvasAPI中的isPointInPath方法:
- 基於:
- (同上)對Canvas綁定事件后,每當有事件發生,則計算當前鼠標相對於Canvas的坐標值;
- 將要綁定事件的對象儲存在數組里;
- isPonitInPath或isPointInStroke方法針對context上下文來判斷一個坐標值是否在其Path中或上;
- 觸發:事件發生時則遍歷整個數組,並重繪數組里的對象,即改變context,每繪制一個對象則context改變一次,當前的context來使用isPointInPath或isPointInStroke方法,將Offset坐標傳入,來判斷鼠標是否在其路徑上,確定現在focus或click的對象。(canvas中的context會在closePath方法后重新設置)
- 優點:可實現復雜的圖形事件。
二、繪制問題
在我原先的版本中我是將整個對象操作和對象繪制設置成一個Interval來實現在,在后來的編寫中就發現這樣做會很死板,如果想添加或改動一些功能,則要對整個代碼進行修改甚至在這種模型下無法實現。
最后還是將繪制和操作分離開來:
- 對繪制設置Interval,如:
setInterval(function(){ draw(); },1000/60) //每秒重繪60次
- 而將繪制對象屬性的改變綁定在事件上,如:
該游戲中的Event Functions
原理:對象事件來改變對象的屬性,而繪制則是用對象屬性來繪制,兩個邏輯各司其職,互不干預。
優點:整體程序邏輯會更清晰,更方便后續功能的新增和修改。
The End