HTML5实验室
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

第2章 物理实验

实验8 质点运动与反射

简介

物理实验属于物理引擎的范畴,物理引擎是计算机图像学与物理学之间的一座桥梁。物理引擎通过为刚性物体赋予真实物理属性的方式来计算运动、旋转和碰撞反应。

物理引擎使用对象属性(动量、扭矩或者弹性)来模拟刚体行为,这不仅可以得到更加真实的结果,对于开发人员来说也比编写行为脚本要更加容易掌握。好的物理引擎允许有复杂的机械装置,像球形关节、轮子、汽缸或者铰链。有些也支持非刚性体的物理属性,如流体。物理引擎可以从其他厂商购买,而一些游戏开发系统具备完整的物理引擎。还要注意,虽然有的系统在其特性列表中说它们有物理引擎,但其实是一些简单的加速和碰撞检测属性而已。物理引擎可以实现粒子效果、流体效果、软体效果等,本实验先从最简单的匀速直线运动谈起。

匀速直线运动

物体在一条直线上运动,且在任意相等的时间间隔内位移相等,这种运动称为匀速直线运动(Uniform Rectilinear Motion),如图2-1所示。位移是速度对时间的累积,写成公式就是:s=∫vdt。而计算机无法模拟无穷小的时间间隔,但是只要时间间隔足够小,积分运算误差就不大。时间间隔为(1/60)s,也就是60帧/秒。

图2-1 匀速直线运动

在实验2台球中,按照如下方式定义小球:

        var ball={
                  position: { x: 100, y: 100 },
                  r: 15
                };

其中包含了小球的位置和半径两个属性,因为要用小球来做物理实验,所以为小球增加两个属性vx和vy,分别代表小球沿X轴方向和沿Y轴方向上的速度。这在物理学上叫做运动独立性原理,即:一个物体同时参与几种运动,各分运动都可看成独立进行的,互不影响,物体的合运动则视为几个相互独立分运动叠加的结果。分运动和合运动之间具有:独立性、等时性、矢量性、同体性。如下所示:

        var ball={
                  position: { x: 100, y: 100 },
                  r: 15,
                  vx: 190,
                  vy: 110
                };

代码中假定了小球沿X轴方向的速度为190,沿Y轴方向上的速度为110。

定义好了小球的速度,如何把它的运动状态体现在Canvas中呢?在实验3中,已经用到了Jscex来实现画圆动画。这里继续使用Jscex来实现小球的运动。

在使用Jscex之前依然要先引用Jscex压缩后的库函数:

        <scriptsrc="jscex.min.js"type="text/javascript"></script>

这样就可以使用Jscex了。

        <canvasid="myCanvas"width="600"height="500">Your browser does not support the canvas element.
        </canvas>
        <scripttype="text/javascript">
        var canvas=document.getElementById("myCanvas");
        var cxt=canvas.getContext("2d");
        var ball={
                position: { x: 100, y: 100 },
                r: 15,
                vx: 190,
                vy: 110
            };
        var cyc=10;
        var moveAsync=eval(Jscex.compile("async", function () {
        while (true) {
                cxt.fillStyle="rgba(0, 0, 0, .3)";    \注:实现了残影效果。\
                cxt.fillRect(0, 0, canvas.width, canvas.height);
                cxt.fillStyle="#fff";
                cxt.beginPath();
                cxt.arc(ball.position.x, ball.position.y, ball.r, 0, Math.PI * 2, true);
                cxt.closePath();
                cxt.fill();
                ball.position.x +=ball.vx * cyc / 1000;
                ball.position.y +=ball.vy * cyc / 1000;    \注:小球的x 坐标和 y坐标每隔一个周期(cyc)就发生变化。\
                $await(Jscex.Async.sleep(cyc));
                }
            }))
            moveAsync().start();
        </script>

也可以让小球往返不停地运动:

        var moveAsync=eval(Jscex.compile("async", function () {
        while (true) {
        while (ball.position.x < 500) {
                        cxt.fillStyle="rgba(0, 0, 0, .3)";
                        cxt.fillRect(0, 0, canvas.width, canvas.height);
                        cxt.fillStyle="#fff";
                        cxt.beginPath();
                        cxt.arc(ball.position.x, ball.position.y, ball.r, 0, Math.PI * 2, true);
                        cxt.closePath();
                        cxt.fill();
                        ball.position.x +=ball.vx * cyc / 1000;
                        ball.position.y +=ball.vy * cyc / 1000;
                        $await(Jscex.Async.sleep(cyc));
                      }
        while (ball.position.x > 100) {
                        cxt.fillStyle="rgba(0, 0, 0, .3)";
                        cxt.fillRect(0, 0, canvas.width, canvas.height);
                        cxt.fillStyle="#fff";
                        cxt.beginPath();
                        cxt.arc(ball.position.x, ball.position.y, 15, 0, Math.PI * 2, true);
                        cxt.closePath();
                        cxt.fill();
                        ball.position.x -=ball.vx * cyc / 1000;
                        ball.position.y -=ball.vy * cyc / 1000;
                        $await(Jscex.Async.sleep(cyc));
                      }
                  }
                }))
                moveAsync().start();

可以看到上面这段代码共有3个while,从上到下依次无限循环执行下去,效果如图2-2所示。

图2-2 Canvas中模拟匀速运动的小球

倘若小球和四壁碰撞,就会发生反弹,反弹符合反射定律,如图2-3所示。

图2-3 反射定律

其中,入射角i与反射角r是相等的。所以在Canvas中模拟的结果就是:当小球与上下壁相撞时,Y轴速度方向变成其反方向,大小不变,X轴速度不变;当小球与左右两壁碰撞时,X轴速度方向变成其反方向,大小不变,Y轴速度大小及方向都不变。可以得出:

        var canvas=document.getElementById("myCanvas");
        var cxt=canvas.getContext("2d");
        var ball={
                  x: 100,
                  y: 100,
                  r: 15,
                  vx: 190,
                  vy: 110
                };
        var cyc=10;
        var moveAsync=eval(Jscex.compile("async", function () {
        while (true) {
                        cxt.fillStyle="rgba(0, 0, 0, .3)";
                        cxt.fillRect(0, 0, canvas.width, canvas.height);
                        cxt.fillStyle="#fff";
                        cxt.beginPath();
                        cxt.arc(ball.x, ball.y, ball.r, 0, Math.PI * 2, true);
                        cxt.closePath();
                        cxt.fill();
        //与左右两壁碰撞。
        if (ball.r+ball.x > canvas.width || ball.x < ball.r) ball.vx *=-1;
        //与上下两壁碰撞。
        if (ball.r+ball.y > canvas.height || ball.y < ball.r) ball.vy *=-1;
                        ball.x +=ball.vx * cyc / 1000;
                        ball.y +=ball.vy * cyc / 1000;
                        $await(Jscex.Async.sleep(cyc));
                  }
                }))
                moveAsync().start();

这样小球就永远逃不出Canvas画布的范围了,当然这是碰撞的一种特殊情况,因为碰撞面分别与X轴和Y轴平行,所以处理起来非常方便。如果遇到任意碰撞面,就不能这么简单地把方向设反,而要通过向量计算速度的变化,在后面的实验中将解决这一问题。