群体行为(不要碰到对方)

书名:代码本色:用编程模拟自然系统
作者:Daniel Shiffman
译者:周晗彬
ISBN:978-7-115-36947-5
第6章目录

6.11 群/体行为(不要碰到对方)

1、ArrayList

  • 在粒子系统类中,我们用ArrayList存放粒子的列表。我们会在本例中做同样的事情:把一组Vehicle对象存放到ArrayList中。
ArrayList<Vehicle> vehicles;   声明由小车对象组成的ArrayList
void 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&lt;Vehicle> vehicles;
void setup() {
    size(320,240);
    vehicles = new ArrayList&lt;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-33
  • 但如果同时有多辆小车的距离都很近,这时候该怎么做?在这种情况下,我们可以对所有远离小车的向量求平均值,用平均向量计算分离行为的转向力。


    图6-34

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&lt;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、运行结果

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,039评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,426评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,417评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,868评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,892评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,692评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,416评论 3 419
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,326评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,782评论 1 316
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,957评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,102评论 1 350
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,790评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,442评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,996评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,113评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,332评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,044评论 2 355

推荐阅读更多精彩内容