Loading...
Logo
Processing Forum
Hey guys!

Once again I need your help! I have included the source code below. It's a simple sketch that generates some wandering autonomous agents on a 2D canvas. At this point, there is 1 superclass called "Capsules"and 2 subclasses called MW1 and FW1.

I have succeeded in implementing inheritance and polymorphism (at least I think so) and I generate the agents using this part: 

agents = new ArrayList<Capsule>();
 
  for (int i = 0; i < 40; i++) {
    if (i < 20) agents.add(new MW1(random(width),random(height),color(255,0,0),i));
    else        agents.add(new FW1(random(width),random(height),color(0,255,0),i));
}

and inside draw() I am able to call some functions included in the superclass. My question is: how can I apply a function (like arrive() for example) only for the MW1 agents and NOT all of them(?) Cause right now, any method I call, is being applied to all of them, no matter how many agents I create, through this:

for( Capsule agent : agents) {  // instancing - creating objects
      agent.Default();  // default flocking behavior for ALL the agents
      agent.separate(agents);  // global separation that affects ALL the agents
  }

here is the entire code, any kind of help would be much appreciated!


StopWatchTimer sw;  // a visual timer-clock implementation on canvas
int time;  // a counter for time triggering events
int wait = 1000;  // 1 second

Facilities work;  // wondering agent -- represents (W)
Facilities retail;  // wondering agent -- represents (R)
Facilities school;  // wondering agent -- represents (S)
Facilities leisure;  // wondering agent -- represents (L)

ArrayList<Capsule> agents;

float d = 25;

int counter=0;  // counter inception
int temp=0;

void setup() {
  
  size(300,300);  //generates the basic composition canvas
  time = millis();  //store the current time
  
  smooth();  //setup of visual time clock
  println (millis());
  sw = new StopWatchTimer();
  sw.start(); 
  
  // creating the facilities
  work = new Facilities(random(width),random(height));  //generates a hypothetic invisible agent (W)
  retail = new Facilities(random(width),random(height));  //generates a hypothetic invisible agent (R)
  school = new Facilities(random(width),random(height));  //generates a hypothetic invisible agent (S)
  leisure = new Facilities(random(width),random(height));  //generates a hypothetic invisible agent (L)
  
  // creating the agents
 agents = new ArrayList<Capsule>();
 
  for (int i = 0; i < 40; i++) {
    if (i < 20) agents.add(new MW1(random(width),random(height),color(255,0,0),i));
    else        agents.add(new FW1(random(width),random(height),color(0,255,0),i));
}
}

//--------------configuring the canvas & the counter-------------

void draw() {
  background(255);
  time();  //reference to the visual timer
  
  //check the difference between now and the previously stored time is greater than the wait interval
  if(millis() - time >= wait){
    println(counter);//if it is, do something
      temp=counter;
      time = millis();//also update the stored time
      counter++; // counter augmentation
      
      if(counter >180){  // check whether time exceeds 180 sec. aka 24:00 hours
      counter=0; 
    }
  }
    
  //-------------------------------------------------------------
  
  work.Default();  //main-basic functions of (W) facility
  retail.Default();  //main-basic functions of (R) facility
  school.Default();  //main-basic functions of (S) facility
  leisure.Default();  //main-basic functions of (L) facility
  
  PVector vectorOfwork = new PVector(work.location.x, work.location.y);  //creates a vector that connects all individual agents with the (W) facility
  PVector vectorOfretail = new PVector(retail.location.x, work.location.y);  //creates a vector that connects all individual agents with the (R) facility
  PVector vectorOfschool = new PVector(school.location.x, school.location.y);  //creates a vector that connects all individual agents with the (S) facility
  PVector vectorOfleisure = new PVector(leisure.location.x, leisure.location.y);  //creates a vector that connects all individual agents with the (L) facility
  
    
  for( Capsule agent : agents) {  // instancing - creating objects
      agent.Default();  // default flocking behavior for ALL the agents
      agent.separate(agents);  // global separation that affects ALL the agents
    }
}

  //-------------------------------------------------------------

void time() {  //visual configuration for the timer-clock
  background(#FFFFFF);  
  fill(#000000);
  textAlign(CENTER);
  text(nf(sw.hour(), 2)+":"+nf(sw.minute(), 2)+":"+nf(sw.second(), 2)+":"+nf(sw.hundrensec(), 2), 260, 290);
}


//---------------------------------------------superclass----------------------------------------------------//

class Capsule {  //the super-class that defines the chararacteristics of the agents

  PVector location;
  PVector velocity;
  PVector acceleration;
  float r;  //the radius of the circles-spheres/ agents
  float wandertheta;
  float maxforce;    // Maximum steering force
  float maxspeed;    // Maximum speed
  color fillColor;  //differentiation of agent types are based on complementary coloring
  int name;  // gives an ID - number to each different agent
  
  Capsule(float x, float y, color c, int nam) {  //the class constructor
    acceleration = new PVector(0,0);
    velocity = new PVector(0,0);
    location = new PVector(x,y);
    r = 6;
    wandertheta = 0;
    maxspeed = 0.5;
    maxforce = 0.05;
    fillColor = c;
    name = nam;
  }
  
  void Default() {
    boundaries();
    update();
    display();
    wander();
  }
 
  void update() {  // Method to update location
    velocity.add(acceleration);  // Update velocity
    velocity.limit(maxspeed);  // Limit speed
    location.add(velocity);  // Reset accelertion to 0 each cycle
    acceleration.mult(0);
  }

  void wander() {  // Method that makes the agent float into 2D space
    float wanderR = 25;  // Radius for our "wander circle"
    float wanderD = 200;  // Distance for our "wander circle"
    float change = 0.05;
    wandertheta += random(-change,change);  // Randomly change wander theta

    // calculating the new location to steer towards on the wander circle
    PVector circleloc = velocity.get();    // Start with velocity
    circleloc.normalize();   // Normalize to get heading
    circleloc.mult(wanderD);  // Multiply by distance
    circleloc.add(location);  // Make it relative to boid's location
    
    float h = velocity.heading2D();  // We need to know the heading to offset wandertheta

    PVector circleOffSet = new PVector(wanderR*cos(wandertheta+h),wanderR*sin(wandertheta+h));
    PVector target = PVector.add(circleloc,circleOffSet);
    seek(target);
  }  

  void applyForce(PVector force) {  // Newton's second law; We could add mass here if we want A = F / M
    acceleration.add(force);
  }


  // A method that calculates and applies a steering force towards a target
  // STEER = DESIRED MINUS VELOCITY
  void seek(PVector target) {
    PVector desired = PVector.sub(target,location);  // A vector pointing from the location to the target
    desired.normalize();  // Normalize desired and scale to maximum speed
    desired.mult(maxspeed);
    PVector steer = PVector.sub(desired,velocity);  // Steering = Desired minus Velocity
    steer.limit(maxforce);  // Limit to maximum steering force
    applyForce(steer);
  }
  
  // Method that lowers the speed of the agent by the time he reaches the target - see: "buffer zone"
  void arrive(PVector target) {
    PVector desired = PVector.sub(target,location);  // A vector pointing from the location to the target
    float d = desired.mag();
    desired.normalize();  // Normalize desired and scale with arbitrary damping within 100 pixels
    if (d < 6) {
      float m = map(d,0,15,0,maxspeed);
      desired.mult(m);
    } else {
      desired.mult(maxspeed);
    }

    PVector steer = PVector.sub(desired,velocity);  // Steering = Desired minus Velocity
    steer.limit(5*maxforce);  // Limit to maximum steering force
    applyForce(steer);
  }
  
  void display() {  // Draws an ellipse that represents an agent-capsule
    fill(fillColor);
    stroke(0);
    pushMatrix();
    translate(location.x, location.y);
    ellipse(0, 0, r, r);
    popMatrix();
  }
  
  //Method that checkes the maximum floating range of the agents
  void boundaries() {

    PVector desired = null;

    if (location.x < d) {
      desired = new PVector(maxspeed, velocity.y);
    } 
    else if (location.x > width -d) {
      desired = new PVector(-maxspeed, velocity.y);
    } 

    if (location.y < d) {
      desired = new PVector(velocity.x, maxspeed);
    } 
    else if (location.y > height-d) {
      desired = new PVector(velocity.x, -maxspeed);
    } 

    if (desired != null) {
      desired.normalize();
      desired.mult(maxspeed);
      PVector steer = PVector.sub(desired, velocity);
      steer.limit(2*maxforce);
      applyForce(steer);
    }
  }
  
  // Method that separates the agents from one-another -- see: "buffer zone"
  void separate (ArrayList<Capsule> agent) {
    float desiredseparation = r*4;  //4*r = separation ONLY for wandering
    PVector sum = new PVector();
    int count = 0;
    
    // For every agent in the system, check if it's too close
    for (Capsule v : agent) {
      float d = PVector.dist(location, v.location);
      
      // 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(location, v.location);
        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);
      sum.normalize();  // Our desired vector is the average scaled to maximum speed
      sum.mult(maxspeed);
      
      // Implement Reynolds: Steering = Desired - Velocity
      PVector steer = PVector.sub(sum, velocity);
      steer.limit(maxforce);
      applyForce(steer);
    }
  }  
  
  // Method that separates the agents from one-another -- see: "buffer zone"
  void separateSpecial (ArrayList<Capsule> agent) {
    float desiredseparation = r;  //
    PVector sum = new PVector();
    int count = 0;
    
    // For every agent in the system, check if it's too close
    for (Capsule v : agent) {
      float d = PVector.dist(location, v.location);
      
      // 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(location, v.location);
        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);
      sum.normalize();  // Our desired vector is the average scaled to maximum speed
      sum.mult(maxspeed);
      
      // Implement Reynolds: Steering = Desired - Velocity
      PVector steer = PVector.sub(sum, velocity);
      steer.limit(maxforce);
      applyForce(steer);
    }
  }
  
  // Alignment
  // For every nearby agent in the system, calculate the average velocity
  PVector align (ArrayList<Capsule> agent) {
    float neighbordist = 50;
    PVector sum = new PVector(0,0);
    int count = 0;
    for (Capsule v : agent) {
      float d = PVector.dist(location,v.location);
      if ((d > 0) && (d < neighbordist)) {
        sum.add(v.velocity);
        count++;
      }
    }
    if (count > 0) {
      sum.div((float)count);
      sum.normalize();
      sum.mult(maxspeed);
      PVector steer = PVector.sub(sum,velocity);
      steer.limit(maxforce);
      return steer;
    } else {
      return new PVector(0,0);
    }
  }
}


//--------------------------------------------------------subclasses-----------------------------------------------------------------------//


class MW1 extends Capsule {  // subclass: male_worker_single

  MW1(float x_mw1, float y_mw1, color c_mw1, int nam_mw1) {
    super(x_mw1,y_mw1,c_mw1,nam_mw1);
  }
  
  void display() {
    fill(255,0,0);
    stroke(0);
    pushMatrix();
    translate(location.x, location.y);
    ellipse(0, 0, r, r);
    popMatrix();
  }
  

}


class FW1 extends Capsule { // subclass: female_worker_single

  FW1(float x_fw1, float y_fw1, color c_fw1, int nam_fw1) {
    super(x_fw1,y_fw1,c_fw1,nam_fw1);
  }
  
  void display() {
    fill(0,255,0);
    stroke(0);
    pushMatrix();
    translate(location.x, location.y);
    ellipse(0, 0, r, r);
    popMatrix();
  }
}


//----------------------------------------------rest parts------------------------------------------------------//

class Facilities {  //the class that defines the chararacteristics of institutions
  
  PVector location;
  PVector velocity;
  PVector acceleration;
  float r;  //the radius of the circles-spheres/ facilities
  float wandertheta;
  float maxforce;    // Maximum steering force
  float maxspeed;    // Maximum speed
  
  Facilities(float x, float y) {  //the class constructor
    acceleration = new PVector(0,0);
    velocity = new PVector(0,0);
    location = new PVector(x,y);
    r = 6;
    wandertheta = 0;
    maxspeed = 1;
    maxforce = 0.05;
  }
  
  void Default() {  // the basic, default functions for the facility
    wander();
    run();
    boundaries();
  }
  
  void run() {
    update();
    display();
  }

  
  void update() {  // Method to update location
   
    velocity.add(acceleration);   // Update velocity
    velocity.limit(maxspeed);  // Limit speed
    location.add(velocity);
    acceleration.mult(0);   // Reset accelertion to 0 each cycle
  }

  void wander() {  // Method that makes the agent float into 2D space
    float wanderR = 25;  // Radius for our "wander circle"
    float wanderD = 30;  // Distance for our "wander circle"
    float change = 2;
    wandertheta += random(-change,change);  // Randomly change wander theta

    // calculating the new location to steer towards on the wander circle
    PVector circleloc = velocity.get();  // Start with velocity
    circleloc.normalize();  // Normalize to get heading
    circleloc.mult(wanderD);  // Multiply by distance
    circleloc.add(location);  // Make it relative to boid's location
    
    float h = velocity.heading2D();  // We need to know the heading to offset wandertheta

    PVector circleOffSet = new PVector(wanderR*cos(wandertheta+h),wanderR*sin(wandertheta+h));
    PVector target = PVector.add(circleloc,circleOffSet);
    seek(target);
  }  

  void applyForce(PVector force) {   // We could add mass here if we want A = F / M
    acceleration.add(force);
  }


  // A method that calculates and applies a steering force towards a target
  // STEER = DESIRED MINUS VELOCITY
  void seek(PVector target) {
    PVector desired = PVector.sub(target,location);  // A vector pointing from the location to the target
    desired.normalize();  // Normalize desired and scale to maximum speed
    desired.mult(maxspeed);
    // Steering = Desired minus Velocity
    PVector steer = PVector.sub(desired,velocity);
    steer.limit(maxforce);  // Limit to maximum steering force

    applyForce(steer);
  }

  void display() {  // Draw an ellipse that represents a facility-capsule
    fill(255);
    stroke(255);
    pushMatrix();
    translate(location.x, location.y);
    ellipse(0, 0, r, r);
    popMatrix();
  }
 
  //Method that checkes the maximum floating range of the facilities
  void boundaries() {

    PVector desired = null;

    if (location.x < d) {
      desired = new PVector(maxspeed, velocity.y);
    } 
    else if (location.x > width -d) {
      desired = new PVector(-maxspeed, velocity.y);
    } 

    if (location.y < d) {
      desired = new PVector(velocity.x, maxspeed);
    } 
    else if (location.y > height-d) {
      desired = new PVector(velocity.x, -maxspeed);
    } 

    if (desired != null) {
      desired.normalize();
      desired.mult(maxspeed);
      PVector steer = PVector.sub(desired, velocity);
      steer.limit(2*maxforce);
      applyForce(steer);
    }
  }  
}


// class timer -- the visual counting clock on the canvas

class StopWatchTimer {
  int startTime = 0, stopTime = 0;
  boolean running = false; 
  void start() {
    startTime = millis();
    running = true;
  }
  void stop() {
    stopTime = millis();
    running = false;
  }
  int getElapsedTime() {
    int elapsed;
    if (running) {
      elapsed = (millis() - startTime);
    }
    else {
      elapsed = (stopTime - startTime);
    }
    return elapsed;
  }
  
 int hundrensec()  // And add in the class  before  int second() this :
  {
 
  return (getElapsedTime() / 10) % 100; 
  } 
  int second() {
    return (getElapsedTime() / 1000) % 60;
  }
  int minute() {
    return (getElapsedTime() / (1000*60)) % 60;
  }
  int hour() {
    return (getElapsedTime() / (1000*60*60)) % 24;
  }
}

Replies(19)

Have you already thought on overriding method arrive() within class MW1 ?
Or even make arrive() exclusive to that sub-class?

Cause right now, any method I call, is being applied to all of them, no matter how many agents I create
Only overridden method I see inside those 2 sub-classes is update() !
There's no more unique implementations between MW1 & FW1 besides that method!!!
In fact, only fill() color is different!  @_@

I'm still somewhat new to Processing/Java.
But I guess if you need to treat each sub-class as different entities, you would have to instantiate them apart from its parent class:

List<MW1> agentMW = new ArrayList();
List<FW1> agentFW = new ArrayList();


Indeed that was the first thing I tried to do!

I erased the arrive() from the superclass and I added in the subclass MW1. When I tried to call to it like this:

or( Capsule agent : agents) {  // instancing - creating objects
      agent.Default();  // default flocking behavior for ALL the agents
      agent.separate(agents);  // global separation that affects ALL the agents
       agent.arrive(vectorOfwork);
  }

I get the bug: the function arrive(PVector) does not exist... it works though for all the agents...
Sorry, I was lazy to write all of my reply fast enough! 

Indeed, if method arrive() is exclusive to a sub-class, parent class can't see it.

Like I posted later in my previous reply, you need to instantiate the sub-classes, not their parent.
Which is more like a template for them rather than to be used by itself!
If you never instantiate Capsule, you can make it abstract, and make arrive() abstract too:
abstract class Capsule
{
// [...]
  abstract void arrive(PVector target);
// [...]
}
In FW1, you have to define it, but it can be empty:
void arrive(PVector target) {}
and in MW1, it really does something.

Thus, you can call arrive() whatever the real class is.

In FW1, you have to define it, but it can be empty:
void arrive(PVector target) {}
Isn't defining arrive() as empty some kind of overriding anyways?


the "abstruct" in front of the class is similar to the "public"?

cause in this way it doesn't either last I checked. Also it somehow eliminates the whole superclass - subclass configuration, doesn't it?
Never used those exotic types.
Nevertheless, here's a link for it -> http://docs.oracle.com/javase/tutorial/java/IandI/abstract.html
Similar, yes, in the sense they refine the declaration of the class.
Here, the effect is to allow to define abstract methods, and to prohibit to make instances of this class.

" cause in this way it doesn't either last I checked"
It doesn't what?

" it somehow eliminates the whole superclass - subclass configuration"
Not sure what is a "configuration" here.

An abstract class defines a kind of blueprint for the sub-classes, indicating what methods all of them will have, without necessarily providing them (abstract methods), although it can (default implementation). It is an intermediary step between interfaces (only made of abstract methods, in a sense), and concrete classes.
" cause in this way it doesn't work either last time I checked " sorry for the bad english ...

" it somehow eliminates the whole superclass - subclass configuration " = if I use the abstract method for the entire superclass, then should I "abstract" the methods (like "arrive()") inside the subclass too? Or there is no need for subclasses?

Sorry for the questions but I am new to the program and whatever I google in search of an answer gives me too general info. So whenever I try to apply similar things to my code, they do not really work.

Here is an example of usage:
Copy code
  1. // Abstract because a "shape" is too generic, cannot be displayed for example
  2. abstract class Shape
  3. {
  4.   int x, y; // Position
  5.   // A concrete method
  6.   void move(int _x, int _y)
  7.   {
  8.     x = _x;
  9.     y = _y;
  10.   }
  11.   // An abstract method, because it depends on the kind of shape
  12.   abstract void display();
  13. }
  14.  
  15. class Circle extends Shape
  16. {
  17.   int diam; // Additional field
  18.   Circle(int d)
  19.   {
  20.     diam = d;
  21.   }
  22.   void display()
  23.   {
  24.     ellipse(x, y, diam, diam);
  25.   }
  26. }
  27.  
  28. class Square extends Shape
  29. {
  30.   int size;
  31.   Square(int s)
  32.   {
  33.     size = s;
  34.   }
  35.   void display()
  36.   {
  37.     rect(x, y, size, size);
  38.   }
  39. }
  40.  
  41. void setup()
  42. {
  43.   size(400, 400);
  44.   ArrayList<Shape> shapes = new ArrayList<Shape>();
  45.   Square s = new Square(10); s.x = 50; s.y = 100;
  46.   shapes.add(s);
  47.   Circle c = new Circle(20); c.x = 100; c.y = 50;
  48.   shapes.add(c);
  49.   s = new Square(30); s.x = 150; s.y = 150;
  50.   shapes.add(s);
  51.  
  52.   for (Shape sh : shapes)
  53.   {
  54.     sh.display();
  55.   }
  56. }
As you can see, it works.
You cannot abstract the methods in concrete classes, you have to implement the abstract methods you inherit from. As I wrote, implementation can be empty if the method isn't relevant for your sub-class.

I hope I made things a bit clearer.
yes this is all right but I have already done it. The problem is exactly at the point where you display the shapes:

Copy code
  1. for (Shape sh : shapes)
  2.   {
  3.     sh.display();
  4.   }
using that u display ALL the shapes. How can you make it work, only for 1 of the subclasses for example? Display only the square or something.
Shape is an abstract class which is extended by both classes Circle & Square.

You can either make separate ArrayLists for Circle & Square or you can modify Square's display() method implementation.

Using separate ArrayLists , you can decide which one you wanna use, using separate for loops for each.

For the other option, you could create a boolean flag variable, let's say, isActive.
And inside display(), have a test for it.
If true, draw object; otherwise do nothing.
Copy code
    class Square extends Shape {
    
        int size;
        boolean isActive = true;
    
        Square(int s) {
            size = s;
        }
    
        void display() {
            if (isActive)   rect(x, y, size, size);
        }
    }
    
Never tried it, would this be an approach? How to better implement?



code adapted:

Copy code
  1. // Abstract because a "shape" is too generic, cannot be displayed for example
  2. abstract class Shape
  3. {
  4.   int x, y; // Position
  5.   color c;
  6.   // A concrete method
  7.   void move(int _x, int _y)
  8.   {
  9.     x = _x;
  10.     y = _y;
  11.   }
  12.   void setColor(color _c)
  13.   {
  14.     c = _c;
  15.   }
  16.   // An abstract method, because it depends on the kind of shape
  17.   abstract void display();
  18. }
  19.  
  20. class Circle extends Shape
  21. {
  22.   int diam; // Additional field
  23.   Circle(int d)
  24.   {
  25.     diam = d;
  26.   }
  27.   void display()
  28.   {
  29.     fill(c);
  30.     ellipse(x, y, diam, diam);
  31.   }
  32. }
  33.  
  34. class Square extends Shape
  35. {
  36.   int size;
  37.   Square(int s)
  38.   {
  39.     size = s;
  40.   }
  41.   void display()
  42.   {
  43.     fill(c);
  44.     rect(x, y, size, size);
  45.   }
  46. }

  47. void setup()
  48. {
  49.   size(400, 400);
  50.   ArrayList<Shape> shapes = new ArrayList<Shape>();
  51.   Square s = new Square(10); s.x = 50; s.y = 100;

  52.   shapes.add(s);
  53.   Circle c = new Circle(20); c.x = 100; c.y = 50;
  54.   shapes.add(c);
  55.   s = new Square(30); s.x = 150; s.y = 150;
  56.   shapes.add(s);
  57.   
  58.   for (Shape sh : shapes)
  59.   {
  60.     Class sqr = s.getClass();// how to assign direct?
  61.     Class cir = c.getClass();  
  62.     if (sh.getClass().equals(cir)) 
  63.     sh.setColor(255);
  64.     else
  65.     sh.setColor(color(255, 0, 0));
  66.     sh.display();
  67.   }
  68. //println(s.getClass().getName());// or better use string compare?
  69. }

Found a better 3rd way!
This thing called instanceof:
    for (Shape sh: shapes)
        if ( sh instanceof Square )  sh.display();
That tweak above will only call display() if current ArrayList element ( Shape sh) belongs to class Square!
So, it will skip Circle one!!!  

I believe this is the most easier and elegant solution for your needs! 
Hey guys!

Thanks so much for your time, interest and you answers. 
The 3rd way seems to function for now :) it seems really good.

GoToLoop you also mentioned ArrayLists; Well, I used to deal with ArrayLists before I try to solve it with inheritance. Here is an example of the previews configuration with ArrayLists:

http://mibpaste.com/naGV58 ( I hope you can open the link ) --this code btw works fine thanks to the help of this forum :)

The only problem is that agents get even more and behaviors change over time so I had to come up with a better way to deal with it.

Now I have 4 different subclasses.
When I try to use the instanceof modifier that you proposed some issues are solved indeed :)

But when I try to arrange them as in the example (see the link) I write for example:

if (agent instanceof FnW2)      // this attaches each of the FnW2 agent to an equal MW2 agent 
       agent.arrive(MW2.location);

and I get the common bug: cannot make a static reference to the non-static field MW2.location

Does this mean I have to go back to ArrayLists once again, and stop with the inheritance? Or is it probably a syntax error?
Seems like you did not declare location static. So instead of passing WM2.loaction you would ether pass an object
Copy code
  1. Wm2 wm2 = new WM2(...);
  2. agent.arrive(wm2.location);
Or declare location static in the WM2 class (but I think that's not what you want:
Copy code
  1. class WM2{
  2.       static PVector location;
  3. }
  4. ...
  5. now you call call
  6. WM2.location;
So the variable is always there without having an object, but there's only one location for all WM2-objects.



The instanceof operator can be useful, but it is generally avoided by OOP purists.
The idea is to make all classes of same category to have a similar behavior, so you precisely don't have to distinguish them when needing to do something: instead, you let the classes themselves to decide if they need to do what is requested or not, and how to do it.

" I have already done it"
I thought you wrote it didn't work...

" How can you make it work, only for 1 of the subclasses for example?"
Re-read the start of the thread. If you don't want to display the circles, in my example, just make the display() method to do nothing.
Did not know instanceof was so bad. Here is a small article on how to do it better: 
I didn't wrote it was bad, it is very useful in some cases, particularly legacy code, code from libraries, some specific cases, etc.

But here, it is easy to avoid, so I tried to point out the more "orthodox" method. The code I show is less clumsy (imagine a dozen of instanceof checks for many different shapes), perhaps easier to understand (arguably), certainly easier to extend (if you add a new class, you have to add a new instanceof, and if your code is in a library, it might be not possible, or you might need to repeat all the cascade of tests in a sub-class; with the method above, only the new class is responsible of its behavior), and perhaps even marginally faster.