书名:代码本色:用编程模拟自然体系
作者:Daniel Shiffman
译者:周晗彬
ISBN:978-7-115-36947-5
第6章目次
6.11 群/体活动(不要遇到对方)
1、ArrayList
- 在粒子体系类中,我们用ArrayList存放粒子的列表。我们会在本例中做同样的变乱:把一组Vehicle对象存放到ArrayList中。
ArrayList<Vehicle> vehicles; 声明由小车对象构成的ArrayListvoid setup() { vehicles = new ArrayList<Vehicle>; 用一系列小车对象添补ArrayList for (int i = 0; i < 100; i++) { vehicles.add(new Vehicle(random(width),random(height))); }}2、draw()函数
- 假如要在draw()函数中处理处罚全部小车对象,只需遍历这个ArrayList,并在对象上调用相应的方法。
void draw(){ for (Vehicle v : vehicles) { v.update(); v.display(); }}3、添加活动
- 我们要为小车添加一种活动。比如让小车探求鼠标地点的目标位置:
v.seek(mouseX, mouseY);
- 但这只是个体的活动,前面我们一直在研究个体的活动,现在要研究群体活动。让我们从分离(separate)活动开始。分离活动等同于以下下令:“请不要和你的邻人发生碰撞!”
v.separate();
- 这个函数另有些题目,我们还少了一些东西。分离指的是“从其他个体上分开”,其他个体指的是列表中的其他小车。
v.separate(vehicles);4、群体活动的实现
- 相比于粒子体系,本例有很大差别。在粒子体系中,个体(粒子或小车)单独运作;
但在本例中,我们会告诉个体:“现在轮到你操纵了,你必要思量体系中的每个个体,所以我要向你传入一个ArrayList,内里存放了全部其他个体。”
- 为了实现群体活动,我们用以下代码实现setup()函数和draw()函数。
ArrayList<Vehicle> vehicles;void setup() { size(320,240); vehicles = new ArrayList<Vehicle>(); for (int i = 0; i < 100; i++) { vehicles.add(new Vehicle(random(width),random(height))); }}void draw() { background(255); for (Vehicle v : vehicles) { v.separate(vehicles); 这是本节参加的新东西,小车在盘算分离转向力时必要查抄其他全部对 v.update(); v.display(); }}5、转向力
- 这只是一个开头,真正的操纵在separate()函数中实现。我们先思考这个函数的实现方式。Reynolds提到:“用转向克制拥堵”,也就是说,假如某辆小车和你的间隔太近,你应该转向以阔别它。对此你是否以为很熟悉?“探求活动”指的是朝着目标转向,将“探求活动”的转向力反转,就能得到躲避活动的转向力。
- 但假犹如时有多辆小车的间隔都很近,这时间该怎么做?在这种情况下,我们可以对全部阔别小车的向量求匀称值,用匀称向量盘算分离活动的转向力。
6、separate()函数的实现
- 下面我们开始实现separate()函数,它的参数是一个ArrayList对象,内里存放了全部小车对象。
- 在这个函数中,我们要遍历全部的小车,查抄它们是否过于靠近。
float desiredseparation = 20; 这个变量指定最短间隔 for (Vehicle other : vehicles) { float d = PVector.dist(location, other.location); 当前小车和其他小车之间的间隔 if ((d > 0) && (d < desiredseparation)) { 假如小车的间隔小于20像素,这里的代码就会实验 } }注意:在上面的代码中,我们不但查抄间隔是否小于desiredseparation(过于靠近!),还要查抄间隔是否大于0。这么做是为了确保小车不会心图和自因素离。全部小车对象都在ArrayList中,一不警惕你就会让一辆小车与自身发生比力。
- 一旦发现和某辆小车过于靠近,我们就应该记录阔别这辆小车的向量。
if ((d > 0) && (d < desiredseparation)) { PVector diff = PVector.sub(location, other.location); 一个指向阔别其他小车方向的向量 diff.normalize();}
- 有了这个diff向量还不敷,我们还要针对每辆靠近的小车盘算diff向量,再盘算它们的匀称向量。将全部向量加在一起,再除以总数,就可以得到匀称向量!
PVector sum = new PVector(); 从一个空向量开始int count = 0;for (Vehicle other : vehicles) { 我们还要记录有多少辆小车的间隔过近 float d = PVector.dist(location, other.location); if ((d > 0) && (d < desiredseparation)) { PVector diff = PVector.sub(location, other.location); diff.normalize(); sum.add(diff); 将全部向量加在一起,并递增计数器 count++; }}if (count > 0) { 必须确保至少找到一辆间隔过近的小车,然后才实验除法操纵(克制除零的情况!) sum.div(count);}
- 有了匀称向量(sum向量)之后,我们将它延伸至最大速率,就可以得到所需速率——盼望小车以最大速率朝着这个方向活动!一旦有了所需速率,我们就可以根据Reynolds的公式盘算转向力:
转向力 = 所需速率 - 当前速率。
if (count > 0) { sum.div(count); sum.setMag(maxspeed); 延伸至最大速率(使其成为所需速率) PVector steer = PVector.sub(sum,vel); Reynolds的转向力公式 steer.limit(maxforce); applyForce(steer); 将力转化为小车的加快度}7、示例
示例代码6-7 聚集活动:分离
void separate (ArrayList<Vehicle> vehicles) { float desiredseparation = r*2; 分离的间隔取决于小车的尺寸 PVector sum = new PVector(); int count = 0; for (Vehicle other : vehicles) { float d = PVector.dist(location, other.location); if ((d > 0) && (d < desiredseparation)) { PVector diff = PVector.sub(location, other.location); diff.normalize(); diff.div(d); 盘算小车和其他小车之间的间隔:间隔越近,分离的幅度越大;间隔越远,分离sum.add(diff); count++; } } if (count > 0) { sum.div(count); sum.normalize(); sum.mult(maxspeed); PVector steer = PVector.sub(sum, vel); steer.limit(maxforce); applyForce(steer); }}8、运行效果
|