软件工程师用打造斯诺克桌球俱乐部设计案例.docx_第1页
软件工程师用打造斯诺克桌球俱乐部设计案例.docx_第2页
软件工程师用打造斯诺克桌球俱乐部设计案例.docx_第3页
软件工程师用打造斯诺克桌球俱乐部设计案例.docx_第4页
软件工程师用打造斯诺克桌球俱乐部设计案例.docx_第5页
已阅读5页,还剩29页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

星期八职场经验网()【现成经验助你快速完成工作】用HTML5打造斯诺克桌球俱乐部设计案例本文介绍了如何利用HTML5技术来打造一款非常酷的斯诺克桌球游戏,文章中详细地列出了开发的全过程,并解说了实现这个游戏的几个关键点。在文章末尾我向大家提供了游戏的在线实例页面和源码下载链接,如果你只是想玩玩(需要使用支持HTML5的浏览器,建议使用Chrome 12, Internet Explorer 9 或者 Fire Fox 5及其以上版本),那你可以跳过正文拉到页面最底端去玩玩那个游戏或者下载源码,但我建议你好好看看实现过程,对我们学习HTML5非常有帮助。 毫无疑问,我们已经目睹了HTML5背后的那场伟大的Web开发革命。经过那么多年HTML4的统治,一场全新的运动即将完全改变现在的Web世界。正是他释放出来的现代化气息和丰富的用户体验,让它很快地成为了一个独特的插件运行在类似Flash和Silverlight的框架之上。如果你是一个非常年轻的开发者,也许你是刚刚在开始学习HTML5,所以可能你并没有注意到他有太大的变化。在任何时候,我希望这篇文章能够帮助到你,当然,也希望像我一样的老手能从中学到一些新的花样。你的点评对我来说非常重要,所以我很期待你的来信。当然能让我更兴奋的是当你在那个游戏画面上右击时暗暗地说一句“Hey,这居然不是Flash!也不是Silverlight!”系统要求想要使用本文提供的HTML5桌球应用,你必须安装下面的这些浏览器:Chrome 12, Internet Explorer 9 or Fire Fox 5游戏规则也许你已经知道这是一个什么样的游戏了,是的,这是“英式斯诺克”,实际上更确切的说是“简易版英式斯诺克”,因为没有实现所有的斯诺克游戏规则。你的目标是按顺序将目标球灌入袋中,从而比其他选手得到更多的分数。轮到你的时候,你就要出杆了:根据提示,你必须先打进一个红色球得到1分,如果打进了,你就可以继续打其他的球 - 但是这次你只能打彩色球了(也就是除红色球以外的球)。如果成功打进,你将会得到各自彩球对应的分数。然后被打进的彩球会回到球桌上,你可以继续击打其他的红球。这样周而复始,直到你失败为止。当你把所有的红球都打完以后,球桌上就只剩下6个彩球了,你的目标是将这6个彩球按以下顺序依次打入袋中:黄(2分)、绿(3分)、棕(4分)、蓝(5分)、粉(6分)、黑(7分)。如果一个球不是按上面顺序打进的,那它将会回到球桌上,否则,它最终会留在袋里。当所有球都打完后,游戏结束,得分最多的人胜出。犯规处理为了处罚你的犯规,其他选手将会得到你的罚分: 白球掉入袋中罚4分 白球第一次击中的球是错误的话罚第一个球的分值 第一个错误的球掉入袋中罚第一个球的分值 处罚的分数至少是4下面的这段代码展示了我是如何来计算犯规的:1. varstrokenBallsCount=0; 2. console.log(strokenBalls.length:+strokenBalls.length); 3. for(vari=0;istrokenBalls.length;i+) 4. varball=strokenBallsi; 5. /causingthecueballtofirsthitaballotherthantheballon 6. if(strokenBallsCount=0) 7. if(ball.Points!=teamsplayingTeamID-1.BallOn.Points) 8. if(ball.Points=1|teamsplayingTeamID-1.BallOn.Points=1| 9. fallenRedCount=redCount) 10. if(teamsplayingTeamID-1.BallOn.Points4) 11. teamsplayingTeamID-1.FoulListteamsplayingTeamID-1 12. .FoulList.length=4; 13. $(#gameEvents).append( 14. Foul4points:Expected+ 15. teamsplayingTeamID-1.BallOn.Points+,buthit+ball.Points); 16. 17. else 18. teamsplayingTeamID-1.FoulListteamsplayingTeamID-1 19. .FoulList.length=teamsplayingTeamID-1.BallOn.Points; 20. $(#gameEvents).append( 21. Foul+teamsplayingTeamID-1 22. .BallOn.Points+points:Expected+teamsplayingTeamID-1 23. .BallOn.Points+,buthit+ball.Points); 24. 25. break; 26. 27. 28. 29. 30. strokenBallsCount+; 31. 32. 33. /Foul:causingthecueballtomissallobjectballs 34. if(strokenBallsCount=0) 35. teamsplayingTeamID-1.FoulListteamsplayingTeamID-1.FoulList.length=4; 36. $(#gameEvents).append( 37. Foul4points:causingthecueball 38. tomissallobjectballs); 39. 40. 41. for(vari=0;ipottedBalls.length;i+) 42. varball=pottedBallsi; 43. /causingthecueballtoenterapocket 44. if(ball.Points=0) 45. teamsplayingTeamID-1.FoulListteamsplayingTeamID-1.FoulList.length=4; 46. $(#gameEvents).append( 47. Foul4points:causingthecueball 48. toenterapocket); 49. 50. else 51. /causingaballdifferentthanthetargetballtoenterapocket 52. if(ball.Points!=teamsplayingTeamID-1.BallOn.Points) 53. if(ball.Points=1|teamsplayingTeamID-1.BallOn.Points=1 54. |fallenRedCount=redCount) 55. if(teamsplayingTeamID-1.BallOn.Points4) 56. teamsplayingTeamID-1.FoulListteamsplayingTeamID-1 57. .FoulList.length=4; 58. $(#gameEvents).append( 59. Foul4points: 60. +ball.Points+waspotted,while+teamsplayingTeamID-1 61. .BallOn.Points+wasexpected); 62. $(#gameEvents).append( 63. ball.Points:+ball.Points); 64. $(#gameEvents).append( 65. teamsplayingTeamID-1 66. .BallOn.Points:+teamsplayingTeamID-1.BallOn.Points); 67. $(#gameEvents).append( 68. fallenRedCount:+fallenRedCount); 69. $(#gameEvents).append( 70. redCount:+redCount); 71. 72. else 73. teamsplayingTeamID-1.FoulListteamsplayingTeamID-1 74. .FoulList.length=teamsplayingTeamID-1.BallOn.Points; 75. $(#gameEvents).append( 76. Foul+teamsplayingTeamID-1 77. .BallOn.Points+points:+ball.Points+waspotted,while 78. +teamsplayingTeamID-1.BallOn.Points+wasexpected); 79. 80. 81. 82. 83. 得分我们根据下面的规则来计算得分:红(1分)、黄(2分)、绿(3分)、棕(4分)、蓝(5分)、粉(6分)、黑(7分)。代码如下:1. if(teamsplayingTeamID-1.FoulList.length=0) 2. for(vari=0;ipottedBalls.length;i+) 3. varball=pottedBallsi; 4. /legallypottingredsorcolors 5. wonPoints+=ball.Points; 6. $(#gameEvents).append( 7. Potted+ball.Points+points.); 8. 9. 10. else 11. teamsplayingTeamID-1.FoulList.sort(); 12. lostPoints=teamsplayingTeamID-1.FoulListteamsplayingTeamID-1.FoulList.length-1; 13. $(#gameEvents).append( 14. Lost+lostPoints+points.); 15. 16. teamsplayingTeamID-1.Points+=wonPoints; 17. teamsawaitingTeamID-1.Points+=lostPoints;选手的闪动动画头像游戏是有两位选手参与的,每一位选手都有自己的昵称和头像,选手的昵称我们就简单地以“player 1”和“player 2”来命名了(也许让用户自己输入会更漂亮)。每位选手的头像是一只正在打桌球的可爱小狗。当轮到其中一位选手时,他的头像就会有一闪一闪的动画效果,同时对手的头像会停止闪动。这个效果我们是通过改变img元素的CSS3属性opacity的值来实现的:我们使用jquery的animatio函数让opacity的值在0-1.0之间变化。1. functionanimateCurrentPlayerImage() 2. varotherPlayerImageId=0; 3. if(playingTeamID=1) 4. otherPlayerImageId=player2Image; 5. else 6. otherPlayerImageId=player1Image; 7. varplayerImageId=player+playingTeamID+Image; 8. $(#+playerImageId).animate( 9. opacity:1.0 10. ,500,function() 11. $(#+playerImageId).animate( 12. opacity:0.0 13. ,500,function() 14. $(#+playerImageId).animate( 15. opacity:1.0 16. ,500,function() 17. ); 18. ); 19. ); 20. 21. $(#+otherPlayerImageId).animate( 22. opacity:0.25 23. ,1500,function() 24. ); 25. 力量控制条一个优秀的斯诺克选手都能很好地把握住每一杆的力度.不同的技巧需要不同的击球方式:直接的,间接的,或者利用边角的等等。不同方向和不同力度的组合可以构造成千上万种可能的路径。幸运的是,这个游戏提供了一个非常漂亮的力度控制条,可以帮助选手在击球前调整他们的球杆。为了达到这一点,我们使用了HTML5的meter元素标签,它可以完成测量距离的工作。meter标签最好在知道这次测量的最小值和最大值的情况下使用。在我们的这个例子中,这个值在0到100之间,因为IE9不支持meter,所以我用了一张背景图来替代,这样效果也是一样的。1. #strengthBarposition:absolute;margin:375px00139px; 2. width:150px;color:lime;background-color:orange; 3. z-index:5;当你点击了力度条后,你实际上是选择了一个新的力度。一开始你可能不是很熟练,但在真实世界中,这是需要时间来训练自己的能力的。点击力度条的代码如下:1. $(#strengthBar).click(function(e) 2. varleft=$(#strengthBar).css(margin-left).replace(px,); 3. varx=e.pageX-left; 4. strength=(x/150.0); 5. $(#strengthBar).val(strength*100); 6. ); 在当前选手的头像框里面,你会注意到有一个小球,我叫他“ball on”,就是当前选手在规定时间内应该要击打的那个球。如果这个球消失了,那选手将失去4分。同样如果选手第一次击中的球不是框内显示的球,那他也将失去4分。这个“ball on”是直接将canvas元素覆盖在用户头像上的,所以你在头像上看到的那个球,他看起来像是在标准的div上盖了一个img元素,但是这个球并不是img实现的。当然我们也不能直接在div上画圆弧和直线,这就是为什么我要将canvas覆盖到头像上的原因了。看看代码吧:1. 2. 3. 1. varplayer1BallOnContext=player1BallOnCanvas.getContext(2d); 2. varplayer2BallOnContext=player2BallOnCanvas.getContext(2d); 3. . 4. . 5. . 6. functionrenderBallOn() 7. player1BallOnContext.clearRect(0,0,500,500); 8. player2BallOnContext.clearRect(0,0,500,500); 9. if(playingTeamID=1) 10. if(teams0.BallOn!=null) 11. drawBall(player1BallOnContext,teams0.BallOn,newVector2D(30,120),20); 12. 13. else 14. if(teams1.BallOn!=null) 15. drawBall(player2BallOnContext,teams1.BallOn,newVector2D(30,120),20); 16. player1BallOnContext.clearRect(0,0,133,70); 17. 18. 旋转屋顶上的电风扇在这个游戏中这把电风扇纯属拿来玩玩有趣一把的。那为什么这里要放一把电风扇?是这样的,这个游戏的名字叫HTML5斯诺克俱乐部,放一把电风扇就有俱乐部的气氛了,当然,我也是为了说明如何实现CSS3的旋转。实现这个非常简单:首先我们需要一张PNG格式的电扇图片。只是我们并没有用电扇本身的图片,我们用他的投影。通过显示风扇在球桌上的投影,让我们觉得它在屋顶上旋转,这样就达到了我们目的:1. #roofFanposition:absolute;left:600px;top:-100px;width:500px;height:500px; 2. border:2pxsolidtransparent;background-image:url(/Content/Images/roofFan.png); 3. background-size:100%;opacity:0.3;z-index:2; 4. . 5. . 6. . 7. 为了获得更为逼真的气氛,我用Paint.Net软件将电扇图片平滑化了,现在你再也看不到电扇的边缘了。我觉得这是达到如此酷的效果最为简单的办法。除了用了这图像处理的把戏,我们仅仅使用了一个带背景图的普通的div元素,这并没有什么特别。既然我们已经得到了电扇图片,我们就要让它开始旋转了。这里我们使用CSS3的rotate属性来实现这一切。球杆动画球杆的动画对于这个游戏也不是必需的,但是这的确为此添加了不少乐趣。当你开始用鼠标在球桌上移动时,你会注意到球杆的确是跟着你的鼠标在转动。这就是说球杆会一直保持跟随鼠标的移动,就像你身临其境一般真实。因为选手只能用他的眼睛来瞄准,所以这个效果也会对选手有所帮助。球杆是单独一张PNG图片,图片本身不直接以img的形式展现,也不以背景的形式展现,相反,它是直接展现在一个专门的canvas上的。当然我们也可以用div和css3来达到同样的效果,但我觉得这样能更好的说明如何在canvas上展现图片。首先,canvas元素会占据几乎整个页面的宽度。请注意这个特别的canvas有一个很大的z-index值,这样球杆就可以一直在每个球的上方而不会被球遮盖。当你在球桌上移动鼠标时,目标点会实时更新,这时候球杆图片会进行2次转换:首先,通过计算得到母球的位置,其次翻转母球周围的球杆,通过这2步我们就得到了鼠标所在点和母球的中心点。1. #cueposition:absolute; 2. . 3. . 4. . 5. if(drawingtopCanvas.getContext) 6. varcueContext=drawingtopCanvas.getContext(2d); 7. 8. . 9. . 10. . 11. varcueCenter=15,-4; 12. varcue=newImage; 13. cue.src=; 14. 15. varshadowCue=newImage; 16. shadowCue.src=; 17. cueContext.clearRect(0,0,topCanvasWidth,topCanvasHeight); 18. 19. if(isReady) 20. cueContext.save(); 21. cueContext.translate(cueBall.position.x+351,cueBall.position.y+145); 22. cueContext.rotate(shadowRotationAngle-Math.PI/2); 23. cueContext.drawImage(shadowCue,cueCenter0+cueDistance,cueCenter1); 24. cueContext.restore(); 25. cueContext.save(); 26. cueContext.translate(cueBall.position.x+351,cueBall.position.y+140); 27. cueContext.rotate(angle-Math.PI/2); 28. cueContext.drawImage(cue,cueCenter0+cueDistance,cueCenter1); 29. cueContext.restore(); 30. 为了让球杆变得更真实我们为球杆添加了投影,并且我们故意让球杆投影的旋转角度和球杆的角度不一样,我们这样做是为了让球杆有3D的效果。最终的效果实在是太酷了。推拉球杆这个球杆动画模仿了真实人类的特征:你是否看到过斯诺克选手在瞄准的时候会推拉球杆?我们通过HTML5改变母球和球杆的距离实现了这一效果。当达到一个极点是球杆会被拉回来,然后到达另一个极点时又会被向前推。这样周而复始,知道选手停止移动鼠标。1. varcueDistance=0; 2. varcuePulling=true; 3. . 4. . 5. . 6. functionrender() 7. . 8. . 9. . 10. 11. if(cuePulling) 12. if(lastMouseX=mouseX| 13. lastMouseY=mouseY) 14. cueDistance+=1; 15. 16. else 17. cuePulling=false; 18. getMouseXY(); 19. 20. 21. else 22. 23. cueDistance-=1; 24. 25. 26. if(cueDistance40) 27. cueDistance=40; 28. cuePulling=false; 29. 30. elseif(cueDistance=0.1) 14. vardashLength=dashArraydashIndex+%dashCount; 15. if(dashLengthdistRemaining)dashLength=distRemaining; 16. varxStep=Math.sqrt(dashLength*dashLength/(1+slope*slope); 17. 18. varsignal=(x2x?1:-1); 19. 20. x+=xStep*signal; 21. y+=slope*xStep*signal; 22. thisdraw?lineTo:moveTo(x,y); 23. distRemaining-=dashLength; 24. draw=!draw; 25. 26. 27. 显示跟踪路径当选手击打母球后,母球会在球桌上留下一条跟踪线,用来标明其上一个点的位置。创建这个跟踪路径比前面提到的目标路径复杂一点。首先我必须去实例化一个Queue对象,这个项目中的Queue对象原型由Stephen Morley提供。1. vartracingQueue=newQueue();一旦球开始运动,我们就将母球的实时位置压入这个Queue中:1. if(renderStep%2=0) 2. draw(); 3. enqueuePosition(newVector2D(cueBall.position.x,cueBall.position.y); 4. enqueuePosition函数确保了我们只保存前20个点的位置,这也就是为什么我们只让显示最近的母球的运动路径的原因。1. functionenqueuePosition(position) 2. tracingQueue.enqueue(position); 3. varlen=tracingQueue.getLength(); 4. 5. if(len20) 6. tracingQueue.dequeue(); 7. 8. 接下来,我们要遍历Queue中的数据,从而来创建这条跟踪路径:1. /drawingthetracingline 2. varlastPosX=cueBall.position.x; 3. varlastPosY=cueBall.position.y; 4. 5. vararr=tracingQueue.getArray(); 6. 7. if(!cueBall.pocketIndex) 8. context.strokeStyle=#363; 9. context.lineWidth=8; 10. context.lineCap=round; 11. 12. context.beginPath(); 13. vari=arr.length; 14. while(-i-1) 15. varposX=arri.x; 16. varposY=arri.y; 17. context.dashedLine(lastPosX,lastPosY,posX,posY,10,200,10,20); 18. lastPosX=posX; 19. lastPosY=posY; 20. 21. 22. context.closePath(); 23. context.stroke(); 24. 绘制小球小球和他们的投影都是呈现在一个特殊的canvas上(在球杆canvas下方)。在呈现小球时,我们先要呈现其投影,这样做主要是为了模拟3D的环境。每一个小球必须有投影,我们对每个小球的投影位置都会有一点细微的不同,这些细微差别表明了小球是在不同方向被投射的,也说明了光源所在的位置。每个小球是由一个公共函数来画的,函数有两个参数:1)canvas context;2)小球对象。函数先画出一个完整的圆弧然后根据小球对象提供的颜色将这个圆弧线性填充。每一个小球对象有3中颜色:光亮色、中色和暗色,这些颜色就是用来创建线性渐变颜色的,3D效果也是这样做出来的。1. functiondrawBall(context,ball,newPosition,newSize) 2. varposition=ball.position; 3. varsize=ball.size; 4. 5. if(newPosition!=null) 6. position=newPosition; 7. 8. if(newSize!=null) 9. size=newSize; 10. 11. /maincircle 12. context.beginPath(); 13. context.fillStyle=ball.color; 14. context.arc(position.x,position.y,size,0,Math.PI*2,true); 15. 16. vargradient=context.createRadialGradient( 17. position.x-size/2,position.y-size/2,0,position.x, 18. position.y,size); 19. 20. /brightspot 21. gradient.addColorStop(0,ball.color); 22. gradient.addColorStop(1,ball.darkColor); 23. context.fillStyle=gradient; 24. context.fill(); 25. context.closePath(); 26. 27. context.beginPath(); 28. context.arc(position.x,position.y,size*0.85,(Math.PI/180)*270, 29. (Math.PI/180)*200,true); 30. context.lineTo(ball.x,ball.y); 31. vargradient=context.createRadialGradient( 32. position.x-size*.5,position.y-size*.5, 33. 0,position.x,position.y,size); 34. 35. gradient.addColorStop(0,ball.lightColor); 36. gradient.addColorStop(0.5,transparent); 37. context.fillStyle=gradient; 38. context.fill(); 39. 40. 41. functiondrawBallShadow(context,ball) 42. /maincircle 43. context.beginPath(); 44. context.arc(ball.position.x+ball.size*.25,ball.position.y+ball.size*.25

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论