用原生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

 

posted @ 2015-03-05 17:13 纸异兽 阅读(...) 评论(...) 编辑 收藏