首页 > 开发 > 前端 > 正文

用Html的canvas做小游戏(爱心鱼)

2016-07-06 17:01:50  来源:慕课网
  名称:爱心鱼
爱心鱼主要使用了html5中canvas来进行制作。游戏规则是鱼麻麻要去吃海藻上生长出来的果实喂养鱼宝宝,果实分为两种,普通的橙色果实和可以让分值加倍的蓝色果实。
1、首先,这个游戏是在海洋中,海水里有海葵,尘埃,果实,大鱼,小鱼。搭建好HTML的网页结构后,因为场景内容比较多,在这里使用了两个画布叠加,要使用 can1=document.getElementById("canvas1")来获取canvas,使用can1.getContext('2d');获取canvas的场景(也就是画笔)。在canvas1中我们绘制了鱼麻麻和鱼宝宝,尘埃,以及得分显示,碰撞特效;canvas2中主要是背景和海葵、果实。
2、由于整个游戏场景是在海洋中,海葵和dust会随着海水运动,所以这里需要经过一段时间刷新海葵和dust。在这里有个重要的概念就是window.requestAnimationFrame(),这个方法原理其实也就跟setInterval差不多,通过递归调用同一方法来不断更新画面以达到动起来的效果,但它优于setTimeout/setInterval的地方在于它是由浏览器专门为动画提供的API,在运行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下的话,动画会自动暂停,有效节省了CPU开销。在爱心鱼这个游戏中我们采用window.requestAnimationFrame(gameloop);让图片成为动画,两帧之间的时间间隔通过在加载页面时获得 lastTime = Date.now(); deltaTime = 0; gameloop();
和gameloop()中的 var now = Date.now(); deltaTime = now-lastTime; lastTime = now;这样就可以让场景中的海藻,果实,尘埃在一次次绘制中通过改变角度,位置达到动画效果。并且在gameloop()中还需要检测鱼麻麻是否吃到果实,是否喂了鱼宝宝(毕竟这是个游戏,得分才是王道!)。
3、在画鱼麻麻的过程中我们用到了ctx1.save()和ctx1.restore()这一对API来限制自定义的context属性有效区域;ctx1.translate(this.x,this.y)使ctx1从定义的鱼麻麻位置(画布中央)开始画;ctx1.rotate(this.angle)使画布旋转,有利于后面改变鱼麻麻的角度。在游戏中使用鼠标控制游戏,通过给canvas1添加鼠标移动事件can1.addEventListener('mousemove',onMouseMove,false);通过mx = e.offsetX ==undefined ? e.layerX: e.offsetX来获取鼠标的x坐标,同理得y。鱼麻麻随着鼠标移动时有角度差和位移差,通过两个lerp函数实现角度、位移变化 function lerpDistance(aim, cur, ratio) { var delta = cur - aim; return aim + delta * ratio; } function lerpAngle(a, b, t) { var d = b - a; if (d > Math.PI) d = d - 2 * Math.PI; if (d < -Math.PI) d = d + 2 * Math.PI; return a + d * t; }这里被当做基准的角度a是通过 var deltaY = my - this.y; var deltaX = mx - this.x; var beta = Math.atan2(deltaY,deltaX) + Math.PI;得到的,atan2(y,x)函数返回的是从X 轴正向逆时针旋转到点 (x,y) 时经过的角度,但是它返回的角度在-PI到PI之间,但是鱼麻麻旋转的是一个2PI区域,所以要加上一个PI使其返回值在0到2PI之间(一个圆)。鱼宝宝随鱼麻麻移动同理。
在鱼的动画中,眼睛的动画略有不同,因为只有两种状态(两张图片),所以可以设置固定时间换下一张,这样也可以控制眼睛睁着和眨眼的时间。
4、 在这个动画里还有一个重要概念就是pool——物体池,在绘制果实和特效圈时都用到了这个概念。也就是说通过先定义一个池子,里面放上我们所需要的全部个体,首先拿出一部分,当它结束它的显示周期时返回pool,当我们需要更多的个体时,就可以一个个判断pool里的个体状态如果是false,改变状态显示此个体,循环改变该果实的draw位置(主要是减小y坐标使其向上飘动,减小到某个值时改变状态让其消失在屏幕上)。在gameloop中添加屏幕监查方法,当果实少于某个值时,再改变果实状态重新生长。特效光圈也是同理,设置光圈物体池,改变半径及透明度使其逐渐变大。
5、海藻的晃动是通过绘制二次贝塞尔曲线及正弦函数实现的。ctx2.quadraticCurveTo(this.rootx[i],canHeight-100,this.headx[i],this.heady[i]);二次贝塞尔曲线需要两个点。第一个点是用于二次贝塞尔计算中的控制点,第二个点是曲线的结束点。曲线的开始点是当前路径中最后一个点。如果路径不存在,那么请使用 beginPath() 和 moveTo() 方法来定义开始点。

  我们用海葵底部做控制点,在海葵本身高度上定一个位置为开始点,其头部为结束点。但是这样的绘制值不能使海葵左右摇摆,所以这里用到了sin()函数,它的返回值l在-1到1之间,通过this.headx[i] = this.rootx[i] + l * this.amp[i];实现左右摇摆(如果没有这个-1和振幅amp[i]相乘,海藻只能向右摆动)。把这个点作为结束点就达到了使海葵摆动的目的。
6、canvas中几个常用的API ctx1 = canvas.getContext('2d'):
ctx1.save();
ctx1.shadowBlur = "15";//shadowBlur 属性设置或返回阴影的模糊级数
ctx1.shadowColor = "white";//阴影颜色
ctx1.fillStyle = "white";/
ctx1.font = "30px Verdana";
ctx1.textAlign = "center";
ctx1.fillText("SCORE: " + this.score,w 0.5,h - 30);
ctx1.restore();
ctx1.strokeStyle = "rgba(203,91,0,"+ alpha +")";//用于笔触的颜色、渐变或模式
ctx1.arc(this.x[i],this.y[i],this.r[i],0,Math.PI 2);//创建弧/曲线(用于创建圆形或部分圆)。
结束!