14、《每周一点canvas动画》——碰撞检测(1)
2018-01-15 17:22
387 查看
每周一点canvas动画代码文件
在前面的几章中我介绍了一些动画效果,这些动画效果都相对基础。但是通过这些基础的动画形式和概念,你可以设计出更复杂的动画。本章将介绍在动画中相对来说比较难的物理概念——碰撞检测,当然,我并不是说这个概念在理解上有多难,而是其实现的过程,以及实现的方式上,比较考验你的脑洞。言归正传,本章主要内容:
碰撞检测的方法
几何体碰撞检测方法
基于距离的碰撞检测
回顾我们讲过的动画形式,边界检测是物体与边界的碰撞,用户交互其实也可以说是鼠标与物体的碰撞。这两种动画中使用到的碰撞检测技术,与我们物体与物体的碰撞其实是有异曲同工之处。
方法一:判断物体与物体之间是否有重叠,这里使用物体的外接矩形边界来确定
方法二:判断物体与物体之间的距离,当距离小于某个值时,满足碰撞条件,物体产生碰撞效果
这是我们常用的两种方法,至于更高级的,我会在后面做补充说明。
现在如果给你两个物体,每个物体上都有
记得我们以前添加过一个方法叫做
我们的方法中传入两个对象rectA和rectB,仔细对比图来理解代码的实现。当碰上的时候返回true,也就是我们的两个物体发生了交叠,我们就可以在我们代码中这样处理:
然而,世界上的事情总是这样,简单容易的往往都得不到最好的结果,要想得到最好的结果,那么事情的复杂度就自然而然的会提升许多。看看下面的图片:
所以,上面的方法虽然简单,但是其精确度却远远不够。不过这要看你的选择,如果要求不高,在可以接受的范围内,也可以选择用这种方法来做碰撞检测。
讲了这么多理论的东西,现在我们就来实验一下上面所说的是否有效果?先看效果图
这里我们新建了一个类
代码的原理很简单,这里的box有两种状态,正在下落的activeBox和已经下落到底部的box。当我们的画布中出现第一个box时,此时的activeBox和box都是它,在drawBox中不会进行碰撞检测。当此第一个落到底部后,就会创建第二个box,此时的activeBox就为第二个box,在它下落的过程中就要与数组boxes中的已有的矩形做碰撞检测,然后以此循环。
。基于距离的碰撞检测方法,最好的展示例子就是球体。如何计算两个球体之间的距离相信你已经轻车熟路了
现在有了距离,我们就可以通过它来判断两个球体之间是否发生了碰撞。
所以只要两个球体之间的距离小于半径就算发生了碰撞。赶快上代码吧!
这里我们实现了水平方向的两个球体的碰撞效果。代码很简单,这里就不列出了。
具体代码如下
虽然代码看起来有点长,但基本上都是我们以前讲过的,唯一新一点的就是在碰撞检测部分,我们在发生碰撞后对球体的速度处理上我们运用了弹性动画的效果,不过这也是在弹性动画那一节讲过的。这里就不做过多解释了,注意动态图中,当小球与centerBall发生碰撞时,他并不是立即返回,而是有一部分进入centerBall,这里是因为弹性动画改变的是加速度,不要认为是错误的。
下一节,我们介绍多物体碰撞检测策略和更高级的碰撞检测方法!敬请期待!
在前面的几章中我介绍了一些动画效果,这些动画效果都相对基础。但是通过这些基础的动画形式和概念,你可以设计出更复杂的动画。本章将介绍在动画中相对来说比较难的物理概念——碰撞检测,当然,我并不是说这个概念在理解上有多难,而是其实现的过程,以及实现的方式上,比较考验你的脑洞。言归正传,本章主要内容:
碰撞检测的方法
几何体碰撞检测方法
基于距离的碰撞检测
1.碰撞检测的方法
我记得在前面的章节《每周一点canvas动画》——边界检测与摩擦力这一章中我们介绍了边界检测的方法,忘记的可以去看一看。而我们今天要介绍的碰撞检测是物体与物体之间的,也就是说除了物体与边界的碰撞之外,在我们的动画中我们还要实现物体与物体的碰撞。回顾我们讲过的动画形式,边界检测是物体与边界的碰撞,用户交互其实也可以说是鼠标与物体的碰撞。这两种动画中使用到的碰撞检测技术,与我们物体与物体的碰撞其实是有异曲同工之处。
方法一:判断物体与物体之间是否有重叠,这里使用物体的外接矩形边界来确定
方法二:判断物体与物体之间的距离,当距离小于某个值时,满足碰撞条件,物体产生碰撞效果
这是我们常用的两种方法,至于更高级的,我会在后面做补充说明。
2.几何体碰撞检测方法
查看代码源文件,我们在前面的章节中在Ball.js类上增加了一个方法
getBounds。如果忘记了,打开看看,它的返回值是什么。
Ball.prototype.getBounds = function(){ return { x: this.x - this.radius, y: this.y - this.radius, width: this.radius*2, height: this.radius*2 }; }
现在如果给你两个物体,每个物体上都有
getBounds方法.但是,你仍旧不能判断两个物体是否碰上。这里我们需要在
utils.js工具函数文件中定义一个新的方法:
utils.intersects = function(rectA, rectB){ return !(rectA.x + rectA.width < rectB.x || rectB.x + rectB.width < rectA.x || rectA.y + rectA.height < rectB.y || rectB.y + rectB.height < rectA.y); }
记得我们以前添加过一个方法叫做
utils.containsPoint,这个方法我们用于判断鼠标是否点击在了物体上。同样,上面的这个方法,也返回一个布尔值,用于判断两个物体是否有交叠。
我们的方法中传入两个对象rectA和rectB,仔细对比图来理解代码的实现。当碰上的时候返回true,也就是我们的两个物体发生了交叠,我们就可以在我们代码中这样处理:
if(utils.intersects(rectA, rectB)){ //碰撞了 //dosomething }
然而,世界上的事情总是这样,简单容易的往往都得不到最好的结果,要想得到最好的结果,那么事情的复杂度就自然而然的会提升许多。看看下面的图片:
所以,上面的方法虽然简单,但是其精确度却远远不够。不过这要看你的选择,如果要求不高,在可以接受的范围内,也可以选择用这种方法来做碰撞检测。
讲了这么多理论的东西,现在我们就来实验一下上面所说的是否有效果?先看效果图
这里我们新建了一个类
box.js文件,其实就是矩形文件,跟
ball.js文件,没有多大的区别,只是一个是球体,一个是方块。如果用的是方块,那getBound方法就可以省了。在这我就不列出box的代码了,直接看效果代码:
<canvas id="canvas" width="500" height="400" style="background:#000;"> your browser not support canva cf78 s! </canvas> <script src="../js/utils.js"></script> <script src="../js/box.js"></script> //引入box文件 <script> window.onload = function(){ var canvas = document.getElementById('canvas'), context = canvas.getContext('2d'), boxes = [], activeBox = createBox(), gravity = 0.02; //创建一个新的box function createBox(){ var color = Math.random()*(0xffffff); var box = new Box(Math.random()*40+10, Math.random()*40+10, color); box.x = Math.random()*canvas.width; boxes.push(box); return box; } //画boxes function drawBox(box){ //判断box是否是activeBox,如果不是做碰撞检测,碰上了,创建新的box if(activeBox !== box && utils.intersects(activeBox, box)){ activeBox.y = box.y - activeBox.height; activeBox = createBox(); } box.draw(context); } //监听事件keydown window.addEventListener('keydown', function(event){ switch (event.keyCode){ case 37: activeBox.x -= 5; break; case 39: activeBox.x += 5; break; case 40: gravity = 2; break; } }, false) //监听事件keyup window.addEventListener("keyup", function(event){ gravity = 0.02; }, false); //动画循环 (function drawFrame(){ window.requestAnimationFrame(drawFrame, canvas); context.clearRect(0, 0, canvas.width, canvas.height); activeBox.vy += gravity; activeBox.y += activeBox.vy; //如果到达底部就创建一个新的box if(activeBox.y + activeBox.height > canvas.height){ activeBox.y = canvas.height - activeBox.height; activeBox = createBox(); } //范围限制 if(activeBox.x < 0 ){ activeBox.x = 0; } if( activeBox.x + activeBox.width > canvas.width){ activeBox.x = canvas.width -activeBox.width; } //绘制 boxes.forEach(drawBox); }()); } </script>
代码的原理很简单,这里的box有两种状态,正在下落的activeBox和已经下落到底部的box。当我们的画布中出现第一个box时,此时的activeBox和box都是它,在drawBox中不会进行碰撞检测。当此第一个落到底部后,就会创建第二个box,此时的activeBox就为第二个box,在它下落的过程中就要与数组boxes中的已有的矩形做碰撞检测,然后以此循环。
3. 基于距离的碰撞检测
这一部分,我们摒弃上节中提到的外接矩形几何体碰撞检测的方法。采用一种新的方法——基于距离的碰撞检测。基于距离的碰撞检测方法,最好的展示例子就是球体。如何计算两个球体之间的距离相信你已经轻车熟路了
var dx = ballB.x - ballA.x, dy = ballB.y - ballA.y, dist = Math.aqrt(dx * dx + dy * dy);
现在有了距离,我们就可以通过它来判断两个球体之间是否发生了碰撞。
所以只要两个球体之间的距离小于半径就算发生了碰撞。赶快上代码吧!
这里我们实现了水平方向的两个球体的碰撞效果。代码很简单,这里就不列出了。
3.1 附带弹性效果的碰撞检测
最后,在这里介绍一个多物体附带弹性效果的碰撞检测,弹性动画上一节我们介绍过,这里我们就小运用一下,为下一节多物体的碰撞做铺垫。整个动画的构思是:在画布中间放置了一个静态的球体centerBall,然后又画了10个小球,小球在画布中自由运动,与centerBall发生碰撞效果,不过当发生碰撞之后,小球的运动状态是一个弹性动画的效果。具体代码如下
window.onload = function(){ var canvas = document.getElementById('canvas'), context = canvas.getContext('2d'), centerBall = new Ball(60, "#00d49e"), balls = [], numBalls = 10, spring = 0.03, bounce = -1; centerBall.x = canvas.width/2; centerBall.y = canvas.height/2; for(var i=0; i<numBalls; i++){ var ball = new Ball(Math.random()*30 + 5, Math.random()*0xffffff); ball.x = Math.random()*canvas.width; ball.y = Math.random()*canvas.height; ball.vx = Math.random()*6 - 3; ball.vy = Math.random()*6 - 3; balls.push(ball); } //边界碰撞检测 function move(ball){ ball.x += ball.vx; ball.y += ball.vy; if(ball.x + ball.radius > canvas.width){ ball.x = canvas.width - ball.radius; ball.vx *= bounce; } if(ball.x - ball.radius < 0){ ball.x = ball.radius; ball.vx *= bounce; } if(ball.y + ball.radius > canvas.height){ ball.y = canvas.height - ball.radius; ball.vy *= bounce; } if(ball.y - ball.radius < 0){ ball.y = ball.radius; ball.vy *= bounce; } } //碰撞检测 function draw(ball){ var dx = ball.x - centerBall.x; var dy = ball.y - centerBall.y; var dist = Math.sqrt(dx*dx+dy*dy); var min_dist = centerBall.radius + ball.radius; if(dist < min_dist){ var angle = Math.atan2(dy, dx); var targetX = centerBall.x + Math.cos(angle)*min_dist, targetY = centerBall.y + Math.sin(angle)*min_dist; ball.vx += (targetX - ball.x)*spring; ball.vy += (targetY - ball.y)*spring; } ball.draw(context); } (function drawFrame(){ window.requestAnimationFrame(drawFrame, canvas); context.clearRect(0, 0, canvas.width, canvas.height); balls.forEach(move); balls.forEach(draw); centerBall.draw(context); }()); }
虽然代码看起来有点长,但基本上都是我们以前讲过的,唯一新一点的就是在碰撞检测部分,我们在发生碰撞后对球体的速度处理上我们运用了弹性动画的效果,不过这也是在弹性动画那一节讲过的。这里就不做过多解释了,注意动态图中,当小球与centerBall发生碰撞时,他并不是立即返回,而是有一部分进入centerBall,这里是因为弹性动画改变的是加速度,不要认为是错误的。
下一节,我们介绍多物体碰撞检测策略和更高级的碰撞检测方法!敬请期待!
相关文章推荐
- 7、《每周一点canvas动画》——边界检测与摩擦力(1)
- 8、《每周一点canvas动画》——边界检测与摩擦力(2)
- 4.每周一点canvas动画--波形运动
- 4.1、每周一点canvas动画--圆周运动
- 【canvas】基于坐标的碰撞检测 / 基本的动画 / 多物体动画
- 11、《每周一点canvas动画》——缓动动画
- 11、《每周一点canvas动画》 —— 弹性动画
- 13、《每周一点canvas动画》—— 文字粒子
- 16、《每周一点canvas动画》——坐标旋转
- 17、《每周一点canvas动画》——星球守护
- Canvas绘制小球运动,检测碰撞变色
- 2.每天一点canvas动画--用户交互
- 基于 HTML5 Canvas 的 3D 碰撞检测
- 【cocos2d-x】cocostudio::ColliderDetector 简单介绍 骨骼动画绑定碰撞区域进行碰撞检测
- JavaScript动画-碰撞检测
- html5 canvas创建弹性碰撞动画
- VUE+WebPack游戏设计:实现碰撞检测和动画精灵
- 基于 HTML5 Canvas 的 3D 碰撞检测
- canvas碰撞检测2
- canvas中的碰撞检测笔记