书名:代码本色:用编程模拟自然系统
作者:Daniel Shiffman
译者:周晗彬
ISBN:978-7-115-36947-5
第6章目录
6.12 结合
- 只通过一种行为规则无法模拟出复杂系统的自发行为。
- 开发一种实现来自各种转向力的混合和匹配这种行为的机制
1、回顾
- 力的结合,在第2章做过这样的事。
PVector wind = new PVector(0.001,0);
PVector gravity = new PVector(0,0.1);
mover.applyForce(wind);
mover.applyForce(gravity);
- 在以上代码中,mover对象同时受两个力的作用。该程序能正常工作,因为Mover类支持力的累加。
2、转向力结合
在本章中,力源自对象(小车)自身的意愿,我们希望这些意愿也可以累加。
让我们从以下场景开始,假设系统中的小车有两个意愿:
- 寻找鼠标所在的位置;
- 和距离过近的其他小车分离。
对此,我们可能会在Vehicle类中加入一个applyBehaviors()函数,用于管理小车的所有行为。
void applyBehaviors(ArrayList<Vehicle> vehicles) {
separate(vehicles);
seek(new PVector(mouseX,mouseY));
}
- applyBehavior()函数调用了separate()函数和seek()函数,这两个函数分别对小车施加不同的转向力。
- 我们想调整这两种转向力的强度,但现在的实现无法做到这一点。
- separate()函数和seek()函数最好能返回转向力向量,如此一来,我们就可以调整转向力强度,最后用调整后的转向力影响小车的加速度。
void applyBehaviors(ArrayList<Vehicle> vehicles) {
PVector separate = separate(vehicles);
PVector seek = seek(new PVector(mouseX,mouseY));
applyForce(separate); 我们必须在这里施加转向力,因为seek()函数和separate()函数不再做这件
applyForce(seek);
}
- 看看seek()函数的变化:
PVector seek(PVector target) {
PVector desired = PVector.sub(target,loc);
desired.normalize();
desired.mult(maxspeed);
PVector steer = PVector.sub(desired,vel);
steer.limit(maxforce);
applyForce(steer); 不再施加转向力,而是返回转向力向量
return steer;
}
- 这是一个细微的变化,但对我们来说非常重要:它使我们能集中改变多种转向力的强度。
3、示例
示例代码6-8 转向行为结合:寻找和分离
ArrayList<Vehicle> vehicles;
void setup() {
size(640,360);
// We are now making random vehicles and storing them in an ArrayList
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) {
// Path following and separation are worked on in this function
v.applyBehaviors(vehicles);
// Call the generic run method (update, borders, display, etc.)
v.update();
v.display();
}
// Instructions
fill(0);
text("Drag the mouse to generate new vehicles.",10,height-16);
}
void mouseDragged() {
vehicles.add(new Vehicle(mouseX,mouseY));
}
Vehicle.pde
class Vehicle {
// All the usual stuff
PVector position;
PVector velocity;
PVector acceleration;
float r;
float maxforce; // Maximum steering force
float maxspeed; // Maximum speed
// Constructor initialize all values
Vehicle(float x, float y) {
position = new PVector(x, y);
r = 12;
maxspeed = 3;
maxforce = 0.2;
acceleration = new PVector(0, 0);
velocity = new PVector(0, 0);
}
void applyForce(PVector force) {
// We could add mass here if we want A = F / M
acceleration.add(force);
}
void applyBehaviors(ArrayList<Vehicle> vehicles) {
PVector separateForce = separate(vehicles);
PVector seekForce = seek(new PVector(mouseX,mouseY));
separateForce.mult(2);
seekForce.mult(1);
applyForce(separateForce);
applyForce(seekForce);
}
// A method that calculates a steering force towards a target
// STEER = DESIRED MINUS VELOCITY
PVector seek(PVector target) {
PVector desired = PVector.sub(target,position); // A vector pointing from the position to the target
// Normalize desired and scale to maximum speed
desired.normalize();
desired.mult(maxspeed);
// Steering = Desired minus velocity
PVector steer = PVector.sub(desired,velocity);
steer.limit(maxforce); // Limit to maximum steering force
return steer;
}
// Separation
// Method checks for nearby vehicles and steers away
PVector separate (ArrayList<Vehicle> vehicles) {
float desiredseparation = r*2;
PVector sum = new PVector();
int count = 0;
// For every boid in the system, check if it's too close
for (Vehicle other : vehicles) {
float d = PVector.dist(position, other.position);
// If the distance is greater than 0 and less than an arbitrary amount (0 when you are yourself)
if ((d > 0) && (d < desiredseparation)) {
// Calculate vector pointing away from neighbor
PVector diff = PVector.sub(position, other.position);
diff.normalize();
diff.div(d); // Weight by distance
sum.add(diff);
count++; // Keep track of how many
}
}
// Average -- divide by how many
if (count > 0) {
sum.div(count);
// Our desired vector is the average scaled to maximum speed
sum.normalize();
sum.mult(maxspeed);
// Implement Reynolds: Steering = Desired - Velocity
sum.sub(velocity);
sum.limit(maxforce);
}
return sum;
}
// Method to update position
void update() {
// Update velocity
velocity.add(acceleration);
// Limit speed
velocity.limit(maxspeed);
position.add(velocity);
// Reset accelertion to 0 each cycle
acceleration.mult(0);
}
void display() {
fill(175);
stroke(0);
pushMatrix();
translate(position.x, position.y);
ellipse(0, 0, r, r);
popMatrix();
}
}
网友评论