If you're seeing this message, it means we're having trouble loading external resources on our website.

如果你被网页过滤器挡住,请确保域名*.kastatic.org*.kasandbox.org 没有被阻止.

主要内容

矢量运动

所有这些矢量数学的东西听起来都像我们应该知道的东西,但这是为什么?它将如何帮我们写代码?事实上,我们需要有一定的耐心。使用 PVector 类所能达到的的神奇效用还需要时间来显现。
在第一次学习新的数据结构时,这实际上是一种常见的情况。例如,当你第一次了解数组时,使用数组似乎比仅仅用变量代替多个事物要复杂得多。但当你需要处理成百上千个东西时,情况就不一样了。
PVector 也是如此。现在看起来更多的工作会在以后得到回报,而且回报相当丰盛。你不必着急,因为你的奖励会在下一章到来。

速度

然而,目前我们要把重点放在简单的东西上。使用向量对运动进行编程是什么意思?我们已经在 弹跳球的例子中看到了这一切的初级阶段。屏幕上的对象有一个位置(即它在任何给定的时刻在哪里)以及一个速度(关于它应该如何从一个时刻移动到下一个时刻的说明)。将速度添加到位置:
position.add(velocity);
然后我们在那个位置绘制对象:
ellipse(position.x, position.y, 16, 16);
这是绘制运动物体的新手教程:
  • 将速度添加到位置
  • 在位置绘制对象
在弹跳球示例中, 所有这些代码都发生在 ProcessingJS 的 draw 函数中。我们现在要做的是将所有的运动逻辑封装在一个 object 内。这样,我们就可以在所有 ProcessingJS 程序中为移动对象编程创建基础。
在这种情况下,我们将创建一个通用的 Mover 对象,它将描述在屏幕上移动的物体。因此,我们必须考虑以下两个问题:
  • Mover 有哪些数据?
  • Mover有哪些功能?
我们的运动新手教程算法可以告诉我们这些问题的答案。Mover 对象有两个数据:positionvelocity。这两个数据都是 PVector 对象。我们可以从编写构造函数开始,该函数可以将这些属性初始化为适当的随机值:
var Mover = function() {
  this.position = new PVector(random(width), random(height));
  this.velocity = new PVector(random(-2, 2), random(-2, 2));
};
它的功能也差不多很简单。Mover 需要移动,也需要被看到。我们将这些需求实现为名为 update()display()的方法。我们将把所有的运动逻辑代码放在 update() 中,并在 display() 中绘制对象。
Mover.prototype.update = function() {
  this.position.add(this.velocity);
};

Mover.prototype.display = function() {
  stroke(0);
  strokeWeight(2);
  fill(127);
  ellipse(this.position.x, this.position.y, 48, 48);
};
如果面向对象编程对你来说是全新的,那么这里可能会显得有些混乱。毕竟,我们在本章的开头讨论了PVectorPVector 对象是用于创建位置对象和速度对象的模板。那么,他们在另一个对象,Mover 中做了什么呢?事实上, 这是非常一般的一件事。对象只是保存数据(和功能)的东西。这些数据可以是数字、字符串、数组或其他对象!在本课程中,我们将一遍又一遍地看到这一点。例如,在粒子教程中,我们将编写一个对象来描述粒子系统(ParticleSystem)。该 ParticleSystem 对象将有一个数组的 Particle 对象...并且每一个 Particle 对象都有一些 PVector 对象!
让我们通过合并一个函数来完成 Mover 对象,以确定对象到达窗口边缘时应该做什么。 现在让我们做一些简单的事情,让它环绕边缘:
Mover.prototype.checkEdges = function() {

  if (this.position.x > width) {
    this.position.x = 0;
  } 
  else if (this.position.x < 0) {
    this.position.x = width;
  }

  if (this.position.y > height) {
    this.position.y = 0;
  } 
  else if (this.position.y < 0) {
    this.position.y = height;
  }
};
既然 Mover 对象已经完成,我们可以看一下我们在主程序中需要做什么。 我们首先声明并初始化新的 Mover 实例:
var mover = new Mover();
然后我们在 draw 中调用相应的函数:
draw = function() {
  background(255, 255, 255);

  mover.update();
  mover.checkEdges();
  mover.display(); 
};
这是完整的运行示例。 尝试改变数字,或者注释掉几行代码,看看会发生什么:

加速度

好。 现在,我们应该有信心说明:(1)什么是PVector;(2)我们如何在物体内部使用PVector来跟踪它的位置和运动。 这是很好的第一步,我们可以给自己鼓鼓掌。 然而,在起立鼓掌和欢呼之前,我们需要再向前迈出一步。 毕竟,观看运动菜鸟教程的例子相当无聊—圆永远不会加速,永不减速,永不拐弯。 对于更有趣的运动,对于出现在我们周围的现实世界中的运动,我们需要在我们的 Mover 对象加速中再添加一个 PVector
我们在这里使用的加速度的严格定义是:速度的变化率。让我们想想这个定义。这是一个新概念吗?并不是。速度被定义为位置的变化率。实质上,我们正在开发一种 "滴流" 效应。加速度会影响速度,进而影响位置 (说起来,当我们看到力如何影响加速度,从而影响位置时,这一点将变得更加关键)。在代码中,这将表现为:
velocity.add(acceleration);
position.add(velocity);
作为一项练习,从现在开始,让我们为自己制定一个规则。 让我们在其余的这些教程中完成每个示例,而不要触及速度和位置的具体值(初始化它们除外)。 换句话说,我们现在对编写运动物体的目标是:提出一个算法来计算加速度,让涓滴效应发挥其神奇作用。 (事实上,你会找到打破这个规则的理由,但重要的是要说明我们的运动算法背后的原理。)因此我们需要提出一些计算加速度的方法:
  1. 恒定的加速度
  2. 完全随机的加速度
  3. 向鼠标方向的加速度
算法 #1,恒定的加速度,并不是特别有趣,但它是最简单的,它将帮助我们开始将加速度结合到我们的代码中。
我们需要做的第一件事是在 Mover 构造函数中添加另一个 PVector 属性来表示加速度。 我们将它初始化为 (0.001,0.01) 并一直保持该值,因为我们当前的算法是 constant acceleration。 你可能会想,“天哪,这些值看起来非常小!”这是对的,它们非常小。 重要的是要意识到我们的加速度值(以像素为单位)将随时间在速度中累积,大约每秒30次,具体取决于我们的草图的帧速率。 因此,为了将速度矢量的大小保持在合理的范围内,我们的加速度值应该设置并保持在一个很小值。
var Mover = function() {
  this.position = new PVector(width/2,height/2);
  this.velocity = new PVector(0, 0);
  this.acceleration = new PVector(-0.001, 0.01);
};
注意上面我们将初始速度设置为0 - 因为我们知道在程序运行时我们会逐渐加速它,这要归功于加速度。 我们将在update()方法中执行此操作:
Mover.prototype.update = function() {
  this.velocity.add(this.acceleration);
  this.position.add(this.velocity);  
};
由于我们不断提高速度,如果我们让程序运行得足够长,我们就会冒着速度值变得非常大的风险。 我们希望速度有一个最大限制。 我们可以使用 PVector limit 方法来做到这一点,它会将向量限制在给定的大小。
Mover.prototype.update = function() {
  this.velocity.add(this.acceleration);
  this.velocity.limit(10);
  this.position.add(this.velocity);  
};
这转化为以下内容:
速度的大小是多少? 如果不到10,不用担心; 只是保持原样。 但是,如果它超过10,则将其减少到10!
让我们来看看Mover对象的变化,完成accelerationlimit()
现在转到算法#2,随机的加速度。 在这种情况下,我们不是在对象的构造函数中初始化加速,而是希望在每个循环中选择一个新的加速,即每次调用update()
Mover.prototype.update = function() {
  this.acceleration = PVector.random2D();
  this.velocity.add(this.acceleration);
  this.velocity.limit(10);
  this.position.add(this.velocity);  
};
因为随机向量是归一化的,我们可以尝试用两种不同的方式来缩放它:
  1. 将加速度用恒定值缩放:
    acceleration = PVector.random2D();
    acceleration.mult(0.5);
    
  1. 将加速度用随机值缩放:
    acceleration = PVector.random2D();
    acceleration.mult(random(2));
    
虽然这看起来很明显,但重要的是理解加速度不仅仅指的是移动中物体的 速度增加速度减少 ,而是指在速度或方向上的任何速度变化。 加速用于引导对象,我们将在以后的章节中一次又一次地看到这一点,因为我们开始编写对象,以决定如何在屏幕上移动。

“自然模拟”系列课程是由 Daniel Shiffman 的 "编程的本质" 衍生而来,基于 知识共享 著名-非商用性 3.0 本地化许可协议

想加入讨论吗?

尚无帖子。
你会英语吗?单击此处查看更多可汗学院英文版的讨论.