vsTASKER #5 – Using Events : Flock of birds

This tutorial deals with a simulation of a flock of 3000 birds, sharing the same simple Logic.
The behavior of each bird is given by the Logic according to 3 rules:

  • The first one is to get attracted by one of the blue birds, without going too far;
  • The second rule is to avoid getting close to one of the red birds;
  • The last rule is to go back to the initial position when there is nothing in sight.
Some code is provided below the video to help you build this demo in vsTASKER.
The database can also be downloaded.

Flock Logic:

Avoid Group: Avoid Task Runtime
if (L:threat) {
     if (E:pos.distanceTo(L:threat->pos) > L:threshold) {
        E:setSpeed(10);
        return DONE;
     }
     else {
        float hdg = E:pos.azimuthTo(L:threat->pos);
        hdg += DEG2RAD(180 + RANDOM(-20,20));
        E:setHeading(hdg);
        E:setSpeed(L:threat->getSpeed()*2);
     }
     return AGAIN;
  }
Avoid Group: Return Task Runtime
    E:dyn->setHeading(L:init_pos);
    E:dyn->setSpeed(20);

    if (E:pos.distanceTo(L:init_pos) < 50) {
       E:setSpeed(0);
       return DONE;
    }
Follow Group: Return Home Check
if (L:leader->pos.distanceTo(L:init_pos) > L:threshold * 2) return YES;
else return NO;
Follow Group: Follow Task Runtime
  if (L:leader) {
     if (E:pos.distanceTo(L:init_pos) > L:threshold * 2) {
        return DONE;
     }
     else {
        float hdg = E:pos.azimuthTo(L:leader->pos);
        hdg += DEG2RAD(frand(-disp,disp));
        disp += RANDOM(1,10);
        if (disp > 90) disp = 0;
        E:setHeading(hdg);
        E:setSpeed(RANDOM(30,50));
     }
  }
Eyes Task Declaration
private:
    Array threats;
    Array leaders;
Eyes Task Initialization
case RESET: {
      for (int i=0; i<S:entities.count(); i++) { 
         Vt_Entity* e=S:entities[i];
         if (e->getStatus()->isForce(_Red)) threats.addElem(e, UNIQ);
      }
      for (int i=0; i<S:entities.count(); i++) { 
         Vt_Entity* e=S:entities[i]; 
         if (e->getStatus()->isForce(_Blue)) leaders.addElem(e, UNIQ);
      }
  } break;
Eyes Task Runtime
    float closest = L:threshold;
    Vt_Entity* threat = NULL;

    for (int i=0; i<threats.count(); i++) { 
        Vt_Entity* e=threats[i];
        if (E:pos.distanceto(e->pos) < closest) {
          closest = E:pos.distanceTo(e->pos);
          threat = e;
       }
    }
    if (threat) {
       L:leader = NULL;
       L:threat = threat;
       L:raiseEvent("Detect!");
       return AGAIN;
    }

    closest = L:threshold;
    Vt_Entity* leader = NULL;

    for (int i=0; i<leaders.count(); i++) { 
       Vt_Entity* e=leaders[i];
          if (E:pos.distanceto(e->pos) < closest) {
          closest = E:pos.distanceTo(e->pos);
          leader = e;
       }
    }
    if (leader) {
       L:threat = NULL;
       if (L:leader != leader) {
          L:leader = leader;
          L:raiseEvent("Follow!");
       }
    }

Pulser Task:

Declaration
private:
    Vt_Entity* e;
    int wait, cpt, threat;
Initialization
  case RESET: {
      for (int i=0; i<S:entities.count(); i++) { 
         e=S:entities[i];
         if (e->getStatus()->isForce(_Blue)) break;
      }
      cpt = 0;
      wait = 0;
      threat = 0;
  } break;
Runtime
    if (++cpt > wait) {
       if (!threat) {
          threat = true;
          e->setColor(clRed);
          wait = RANDOM(1,5);
       }
       else {
          threat = false;
          e->setColor(clBlue);
          wait = RANDOM(20,40);
       }
       cpt = 0;
    }
    if (threat) S:raiseEvent("Detect!", T_Entity, e);
    else S:raiseEvent("Follow!", T_Entity, e);
Download the database (vsTASKER v6)
Download the zip database.
Unzip it in Data/Db/OpenGL
Load it with vsTASKER GUI v6 or newer