26、《每周一点canvas动画》——3D旋转与碰撞

各位同学实在不好意思,最近忙着面试找工作,耽搁了一个星期。由于前一篇文章的关注的量比较多,让我决定以后的文章尽量多加一些高质量的DEMO和配图。可能这比较耗费时间,但质量才是王道,希望大家给点时间。

上一节我们介绍了重力和屏幕环绕在三维环境下的物理效果。其实三维环境中物体的运动状态,基本上与二维环境一样,主要的运动状态无非也就是那么几个:匀速运动,加速运动,碰撞,旋转等。再往后如果想要制作更加复杂有规律的运动效果,你可能需要了解一些抽象的物理概念和数学知识:布朗运动,正态分布,矩阵变换等。本节的主要内容分为两个部分,第一部分介绍三维环境下的旋转。第二部分介绍碰撞检测。

1、坐标旋转

在二维环境下,我们要让物体做圆周运动有两种方法。第一种,需要的条件比较多:

    // centerX, centerY :旋转中心
    // angle: 角度
    // radius: 旋转半径
    ball.x = centerX + Math.sin(angle)*radius;
    ball.y = centerY + Math.cos(angle)*radius;

    //每一帧角度增加形成圆周运动
    angle += speed;

如果忘记了可以看看《每周一点canvas动画》——三角函数(2)圆周运动部分的内容。随着内容的深入,我们通过简单的三角函数变换得到一种更高级的旋转方式,它所用的条件很少:

//x1,y1是球体坐标相对旋转中心的距离
 newX = x1*cos(angle) - y1*sin(angle);
 newY = y1*cos(angle) + x1*sin(angle);

通过上面的这个公式我们同样可以做出圆周运动的效果,但所需的条件就只有一个:物体每一帧的旋转角度angle。

如果忘了,可以查看《每周一点canvas动画》——坐标旋转高级坐标旋转部分的内容。

回到三维的环境下,与二维环境下不同的是我们多了一个维度。除了x轴,y轴,还有z轴。

也就是说我们可以得出三个旋转公式:

//绕Z轴
newX = x * cos(angleZ) - y * sin(angleZ);
newY = y * cos(angleZ) + x * sin(angleZ);

//绕X轴
newY = y * cos(angleX) - z * sin(angleX);
newZ = z * cos(angleX) - y * sin(angleX);

//绕Y轴
newX = x * cos(angleY) - z * sin(angleY);
newZ = z * cos(angleY) + x * sin(angleY);

上面的公式可能相对麻烦,但是你仔细观察就会发现,绕某个轴旋转那么物体的轨迹变动就在另外两个轴形成的面上。简单来说就是,绕X轴,物体的Y,Z坐标发生变化,绕Z轴,物体的X,Y坐标发生变化,同理Y轴。这样一下是不是就好记很多。

下面我们看一看实际的效果图

在上图中,物体的旋转并不是某一个轴,而是同时绕着X轴和Y轴做旋转。代码很简单,具体查看rotate-xy.html

2、碰撞检测

在二维的平面环境中,对物体进行碰撞检测有很多方法,比如:

  1. 外接几何体碰撞检测(外接矩形,外接圆)

  2. 基于距离的碰撞检测方法

  3. 光线投射法

还有些其他的更高级的碰撞检测原理,比如分离轴定理等,大家有空可以自己去研究一下,在《HTML5 canvas核心技术》这一书中有详细的介绍。

相比于二维的环境,三维的环境更加复杂,在这里我们很难使用外接几何体和光线投射法去碰断物体之间是否发生碰撞。那么,只剩基于距离的碰撞检测了,似乎现在这是唯一的方法,但万事无绝对,总归会有更加精准,先进的方法。因为,我本身并不是做游戏开发的,所以了解有限。如果你身边有做游戏开发,尤其3D游戏开发的同学,可以向他们请教下,麻烦分享给大家。

我们知道平面上两点之间的距离是这样计算的:

dx = point1.x - point2.x;
dy = point1.y - point2.y;
distance = Math.sqrt(dx * dx + dy * dy);

同理,三维环境下两点之间的距离遵循如下公式:

dx = point1.x - point2.x;
dy = point1.y - point2.y;
dz = point1.z - point2.z;
distance = Math.sqrt(dx * dx + dy * dy + dz * dz);

下面我们就运用上面的公式做个小的DEMO。具体效果为,当物体发生碰撞后颜色变为蓝色。

核心代码如下:

。。。
 function checkCollision (ballA, i) {
        for (var ballB, dx, dy, dz, dist, j = i + 1; j < numBalls; j++) {
          ballB = balls[j];
          dx = ballA.xpos - ballB.xpos;
          dy = ballA.ypos - ballB.ypos;
          dz = ballA.zpos - ballB.zpos;
          dist = Math.sqrt(dx * dx + dy * dy + dz * dz); //距离计算
          if (dist < ballA.radius + ballB.radius) {      //检测
            ballA.color = "#0000ff";
            ballB.color = "#0000ff";
          }
        }
      }
。。。

详细代码请查看collision.html

到这里,这一章的内容就结束了。我们几乎模拟了所有在二维环境下物体的运动效果。其中最为重要的就是三维环境的搭建和物体的排序。这两段代码,让我们尽可能的实现物体的三维效果。其他的所有效果都是基于这两段代码之上。

你可能发现我们大部分做示例的物体,其实都是由原生canvas API提供的。如:圆,矩形,椭圆等。而并没有原生的API,告诉你如何创建一个三维的物体。后面的章节,我们就学习点、线、面的绘制,并以此为基础创建更加复杂的三维物体。

后面的内容可能更新会稍微慢一点.但是绝对保证质量,尽可能每一篇文章里都写一个高质量的DEMO,希望大家谅解。

以上就是对《每周一点canvas动画》——3D旋转与碰撞的相关介绍,希望对您学习javascript有所帮助,感谢您关注织梦者!

阅读更多

没有更多推荐了,返回首页