HTML5版的String Avoider小游戏
HTML5版的String Avoider小游戏 http://www.newgrounds.com/portal/view/300760 蛮简单也蛮考验耐心,从游戏起始点移动鼠标到终点位置,鼠标移动过程绘制出移动轨迹的String平滑曲线,整个过程不能碰撞到边界,从技术角度来说其核心就是根据鼠标移动位置生成String线的算法,该游戏是ActionScript写的Flash版,这里将其改造成HTML5版的JavaScript实现,并增加可自定义关卡功能的一种设计思路。
String连线我是缓存了300个点信息的数组,鼠标移动时不断调整300个点的新位置信息,每次更新时先将新鼠标点设置给第一个元素,后续更新x点时,计算其与x-1点的角度,在此方向长度为1的位置更新坐标,这样就达到了平滑曲线的效果。
除了绘制String线外还有个技术点就是监测碰撞,该Flash游戏的边界都是线段,因此第一想到的监测方式就是线线相交的思路,算法可参考 http://en.wikipedia.org/wiki/Line%E2%80%93line_intersection ,如果以LineLine的相交思路只需要遍历所有point间的线段,判断是否与游戏关卡定义的边界线相交,但这种方式对不规则边界就比较麻烦,监测性能也不高。
考虑到我们还需要提供用户可DIY自定义游戏关卡的功能,我们将采用监测颜色透明度信息的方式,由于正常游戏时场景无需用户动态修改,因此边界的信息可提前缓存到ImageData内存中,并且我们300个点的距离都是1,监测只需根据点进行就可以。
整个程序采用HT for Web的GraphView拓扑图组件,再其上通过addTopPainter添加顶层画笔绘制曲线,当曲线碰到Node图元时绘制成红色,否则绘制成黄色,监听GraphView拓扑图的interaction事件,在该事件中设置dirty的脏标志,在绘制时根据dirty的标志进行更新,采用这样的方式可将多次变换最终聚合成一次更新,这也是图形刷新绘制常用的基本技巧。同时通过GraphView.setEditable(true)开启了拓扑图的可视化编辑功能,用户可随时改变界面图元位置和旋转等形状信息,相当于自定义关卡的效果。
所有代码和运行效果如下:http://v.youku.com/v_show/id_XODU4NzY5MzQ0.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
function init(){ dataModel = new ht.DataModel(); graphView = new ht.graph.GraphView(dataModel); graphView.handleScroll = function(){}; graphView.setEditable( true ); graphView.setPannable( false ) view = graphView.getView(); view.style.left = '10px' ; view.style.top = '10px' ; view.style.width = '600px' ; view.style.height = '400px' ; view.style.background = 'black' ; document.body.appendChild(view); createNode( 20 , 20 , 80 , 40 , 'rect' ); createNode( 200 , 300 , 80 , 40 , 'star' ); createNode( 400 , 100 , 80 , 40 , 'oval' ); createShape(); length = 1 ; count = 300 ; points = []; for (var i= 0 ; i<count; i++){= "" points.push({x:= "" 0 ,= "" y:= "" 0 });= "" }= "" view.addeventlistener(&# 39 ;mousemove&# 39 ;,= "" function(e){= "" var= "" point= "graphView.lp(e);" points[ 0 ].x= "point.x;" points[ 0 ].y= "point.y;" for = "" (var= "" i= "1;" <= "" count= "" -= "" 1 ;= "" i++)= "" {= "" angle= "Math.atan2(points[i].y" points[i= "" 1 ].y,= "" points[i].x= "" 1 ].x);= "" 1 ].x= "" += "" length= "" *= "" math.cos(angle);= "" points[i].y= "points[i" 1 ].y= "" math.sin(angle);= "" if (imagedata){= "" hit= "false;" count;= "" x= "Math.floor(points[i].x);" y= "Math.floor(points[i].y);" index= "(y" graphview.getwidth()= "" x)= "" 4 ;= "" if (imagedata.data[index+ 3 ]= "" != "=" 0 ){= "" break ;= "" graphview.redraw();= "" });= "" dirty= "true;" imagedata= "null;" graphview.mi(function(e){= "" graphview.addtoppainter(function(g){= "" if (dirty){= "" graphview.getwidth(),= "" graphview.getheight());= "" ht. default .calllater(graphview.redraw,= "" graphview);= "" } else {= "" g.beginpath();= "" g.linewidth= "3;" g.strokestyle= "hit" ?= "" &# 39 ;red&# 39 ;= "" := "" &# 39 ;yellow&# 39 ;;= "" g.moveto(points[ 0 ].x,= "" points[ 0 ].y);= "" g.lineto(points[i].x,= "" points[i].y);= "" g.stroke();= "" function= "" createnode(x,= "" y,= "" w,= "" h,= "" shape){= "" node= "new" ht.node();= "" node.setrect(x,= "" h);= "" node.s({= "" &# 39 ;shape&# 39 ;:= "" shape,= "" &# 39 ;select.width&# 39 ;:= "" 0 = "" datamodel.add(node);= "" return = "" node;= "" }<= "" pre= "" ><br> <br> <p class = "" ></p> </count;> |