主要内容
指向运动
我们回到最初的例子之一,一个
Mover
对象向着光标加速的例子。你可能注意到了,目前为止我们绘制的几乎所有形状都是圆形。这样很方便,因为可以不用考虑旋转问题。一个圆形转和不转都一个样。然而,我们作为程序员总有时候会想在屏幕上绘制一些指向运动方向的东西。有可能要画蚂蚁,或者汽车,或者太空飞船。我们说的“指向运动方向”其实是“根据速度向量旋转”。速度是一个向量,具有x和y分量,但要在 ProcessingJS 中实现旋转,我们需要的是角度。把速度向量放进三角图中进行分析:
好的, 我们知道正切(tangent)的定义是:
问题在于我们知道速度,但不知道角度,我们需要把角度计算出来。在这里我们引入一个概念叫做反正切函数,记作 arctan 或者 tan-1。(同样还有反正弦和反余弦。)
如果 a 的正切等于 b,那么 b 的反正切等于 a。即:
如果 | |
那么 |
看到它是如何反过来的吗?这样我们就可以算出角度:
如果 | |
那么 |
现在我们有了公式,来看看如何在 Mover 的
display()
函数中实现。注意在 ProcessingJS 中,反正切函数为 atan()
。JavaScript 中也有原始版本的反正切函数 Math.atan()
(以及其他基本的三角函数),但我们在这里还是采用 ProcessingJS 提供的函数。Mover.prototype.display = function () {
var angle = atan(this.velocity.y / this.velocity.x);
stroke(0, 0, 0);
fill(127, 127, 127);
pushMatrix();
rectMode(CENTER);
translate(this.position.x, this.position.y);
rotate(angle);
rect(0, 0, 30, 10);
popMatrix();
};
现在程序差不多快完成,也快能用了。但我们还有一个大问题没有解决。考虑下图所示的两个速度向量:
尽管看上去相似,但这两个向量的方向可是大不相同——实际上是正好相反!然而,如果用我们刚才的公式来算一下每个向量的角度……
V1 ⇒ angle = atan(3/-4) = atan(-0.75) = -0.644 弧度 = -57 角度
V2 ⇒ angle = atan(-3/4) = atan(-0.75) = -0.644 弧度 = -57 角度
V2 ⇒ angle = atan(-3/4) = atan(-0.75) = -0.644 弧度 = -57 角度
……计算的结果是两个向量有相同的角度。这不可能啊,因为这两个向量是指向相反方向的!这是一个在计算机图形领域很常见的问题。与其用
atan()
函数再加上判断各分量正负关系的一大串判断语句,不如直接选择 ProcessingJS (包括 JavaScript 和很多其他编程环境) 中已有的 atan2()
函数,一步到位地解决问题。Mover.prototype.display = function () {
var angle = atan2(this.velocity.y, this.velocity.x);
stroke(0, 0, 0);
fill(127, 127, 127);
pushMatrix();
rectMode(CENTER);
translate(this.position.x, this.position.y);
rotate(angle);
rect(0, 0, 30, 10);
popMatrix();
};
还能进一步简化,
PVector
对象本身有一个函数 heading()
,可以替你自动调用 atan2()
函数,你可以直接得到任何一个 PVector
对象在二维平面上的方向(单位是弧度)。最终我们的程序完成了,你可以移动光标看看它是怎么旋转的。
“自然模拟”系列课程是由 Daniel Shiffman 的 "编程的本质" 衍生而来,基于 知识共享 著名-非商用性 3.0 本地化许可协议。