用原生Canvas寫貪吃蛇及問題解決


為了學習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、用四周邊界來確定:

  • 基於:
    1. 對Canvas綁定事件后,每當有事件發生,則計算當前鼠標相對於Canvas的坐標值;
    2. 在每個要綁定事件的對象中設定其四邊邊距坐標,並將其放在一個數組;
  • 觸發:事件發生時則遍歷整個數組,來根據坐標來判斷是否在哪一個對象的邊界范圍內,來確定鼠標現在所在的對象。
  • 缺點:這個做的話要求對象只能是形狀規整的直角四邊形,對復雜圖形的處理沒有可實施性。

 

2、使用CanvasAPI中的isPointInPath方法:

  • 基於:
    1. (同上)對Canvas綁定事件后,每當有事件發生,則計算當前鼠標相對於Canvas的坐標值;
    2. 將要綁定事件的對象儲存在數組里;
    3. 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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM