Trouble with Array and Multiple Classes

Goal: I am trying to create an array of amoebae (Amies) drawn with beginShape() inside their class (Amy). I am also trying to feed only one Amy within the array.

Problem 1: I can create an array of Amies with food functions inside the Amy class, but then however many Amies there are in the array that same number of food is dropped, which means all the Amies are fed. My professor told me that the food must be in its own class.

Problem 2: When I put the food into its own class and put Amy into an array, either: I get a massive null void exception error and the program opens but fails to run necessitating a force close; or, I get an array of Amies that ignore the food altogether.

Problem 3: When I managed to make two Amies without an array, they both eat the one piece of food that’s been dropped. I need to use constrain (or distance?) somehow to stop all but the first Amy from eating the food, but honestly my brain is completely soggy right now.

Semi-Working Code for Two Amies Eating One Food (see problem 3)

Amy amy1;
Amy amy2;
Food food;
color petriDish = #DDDCDC;

void setup(){
  size(600,600);
  rectMode(CENTER);
  amy1 = new Amy();
  amy2 = new Amy(width*.5, height*.5);
  food = new Food();
}

void draw(){
  background(petriDish);
  food.petriDish();
  food.dropFood();
  food.eatFood();
  amy1.amyShow();
  amy1.amyFace();
  amy1.moveAmy();
  amy2.amyShow();
  amy2.amyFace();
  amy2.moveAmy();
  food.chaseFood();
}

void mouseClicked(){
  food.foodColour = color(random(0,255),random(0,255),random(0,255));
  food.foodDropped = true;
  food.xFood = mouseX;
  food.yFood = mouseY;
}

Amy Class

class Amy {
 
 //amy size & movement variables
 float amyX, amyY;
 float xSpeed, ySpeed;
 float easing;
 color amyColour;
 
 Amy() {
  color petriDish = #DDDCDC;
 //amy size & movement variables
  amyX = width/1.4; 
  amyY = height/1.6;
  xSpeed = random(.3,1.5); 
  ySpeed = random(.2,1.5);
  easing = 0.05;
  amyColour = color(0,150,213);
 }
 
  Amy(float tempAmyX, float tempAmyY) {
  color petriDish = #DDDCDC;
 //amy size & movement variables
  amyX = tempAmyX; 
  amyY = tempAmyY;
  xSpeed = random(.3,1.5); 
  ySpeed = random(.2,1.5);
  easing = 0.05;
  amyColour = color(0,150,213);
 }
 
 void amyShow(){
    beginShape();
    fill(amyColour,99);
    stroke(amyColour*2,99);
    strokeWeight(5);  
    curveVertex(amyX*.95,amyY+48);
    curveVertex(amyX-5,amyY+6);
    curveVertex(amyX+16,amyY-29);
    curveVertex(amyX+54,amyY-61);
    curveVertex(amyX+71,amyY-37);
    curveVertex(amyX+109,amyY-40);
    curveVertex(amyX+132,amyY+4);
    curveVertex(amyX+102,amyY+56);
    curveVertex(amyX+82,amyY+85);
    curveVertex(amyX+57,amyY+56);
    curveVertex(amyX+9,amyY+48);
    curveVertex(amyX+9,amyY+20);
    curveVertex(amyX-2,amyY+15);
    curveVertex(amyX-5,amyY+6);
    curveVertex(amyX-5,amyY+6);
    endShape(CLOSE);
}

void amyFace(){
  fill(0);
  noStroke();
  if(food.foodDropped){
    ellipse(amyX+55,amyY,15,7);
    ellipse(amyX+75,amyY,15,7);
    ellipse(amyX+60,amyY+15,20,15);
    //arc(amyX+60,amyY+10,15,5,0,PI);
  } else {
      ellipse(amyX+55,amyY,10,15);
      ellipse(amyX+75,amyY,10,15);
      arc(amyX+60,amyY+10,15,5,0,PI);
  }
}

void moveAmy(){
  amyX = amyX + xSpeed; 
    if((amyX >= width-width/100) || (amyX <= width-width*1.4)) {
    xSpeed = xSpeed *-1;
  }
  amyY = amyY + ySpeed;
    if((amyY >= height-height/100) || (amyY <= height-height*1.2)) {
    ySpeed = ySpeed *-1;
    }
}

void dropFood(){
  fill(food.foodColour);
  if(food.foodDropped){
    rect(food.xFood,food.yFood,25,25);
  }
}

}

Food Class

class Food {
 //food variables
 float xFood, yFood;
 boolean foodDropped;
 int foodColour;
  
 Food() {
  foodDropped = false;
  xFood = mouseX;
  yFood = mouseY;
 }
 
 void dropFood(){
  fill(foodColour);
  if(foodDropped){
    rect(xFood,yFood,25,25,9);
  }
}

void eatFood(){
  if((int(amy1.amyX+60)>=xFood-3 && int(amy1.amyX+60)<=xFood+3) && 
     (int(amy1.amyY+22)>=yFood-3 && int(amy1.amyY+22)<=yFood+3))
  
  if((int(amy2.amyX+60)>=xFood-3 && int(amy2.amyX+60)<=xFood+3) && 
     (int(amy2.amyY+22)>=yFood-3 && int(amy2.amyY+22)<=yFood+3))
  
  {
    foodDropped=false;
    amy1.amyColour = foodColour;
    foodDropped=false;
    amy2.amyColour = foodColour;
  }
}

void chaseFood(){
  if(foodDropped){
    amy1.amyX = amy1.amyX + amy1.xSpeed*1.2;
    amy1.xSpeed = amy1.xSpeed *-1;
    amy1.amyY = amy1.amyY + amy1.ySpeed*1.2;
    amy1.ySpeed = amy1.ySpeed *-1;
    amy1.amyX = (1-amy1.easing) * amy1.amyX + amy1.easing * xFood-3;
    amy1.amyY = (1-amy1.easing) * amy1.amyY + amy1.easing * yFood-1;  
  }
  if(foodDropped){
    amy2.amyX = amy2.amyX + amy2.xSpeed*1.2;
    amy2.xSpeed = amy2.xSpeed *-1;
    amy2.amyY = amy2.amyY + amy2.ySpeed*1.2;
    amy2.ySpeed = amy2.ySpeed *-1;
    amy2.amyX = (1-amy2.easing) * amy2.amyX + amy2.easing * xFood-3;
    amy2.amyY = (1-amy2.easing) * amy2.amyY + amy2.easing * yFood-1;  
  }
}

void petriDish(){
  textSize(25);
  fill(petriDish*500);
  text("Petri Dish 42:",10,30);
  text("Amy the Amoeba",10,65);
}

}

Semi-Working Code with Amy Array Ignoring Food (see problem 2)

Amy[] amy = new Amy[3];
Food food;
color petriDish = #DDDCDC;

void setup(){
  size(500,500);
  rectMode(CENTER);
  for (int i = 0; i <amy.length; i++){
    amy[i] = new Amy();
  }
  food = new Food();
}

void draw(){
  background(petriDish);
  for (int i = 0; i < amy.length; i++){
    amy[i].petriDishLabel();
    food.dropFood();
    food.eatFood();
    amy[i].drawAmy();
    amy[i].moveAmy();
    food.dropFood();
    food.chaseFood();
    food.eatFood();
  }
}

void mouseClicked(){ 
    food = new Food();
    food.foodDropped = true;
    food.xFood = mouseX;
    food.yFood = mouseY;
}

Amy Class

class Amy{
Food food = new Food();
  color petriDish = #DDDCDC;
  float amyX, amyY;
  color amyColour = #0098D0;
  float xSpeed, ySpeed;
  float easing = 0.05;
  int direction;
  
  Amy(){ //constructor
    xSpeed = random(.3,1.5);
    ySpeed = random(.2,1.5);
    amyX = random(width);
    amyY = random(height);
    if(amyX >= width/2 && amyY >= height/2){
      xSpeed = xSpeed*-1;
      ySpeed = ySpeed*-1;
    }

  }
  
  void drawAmy(){
    //body
    fill(amyColour,99);
    stroke(amyColour*2,99);
    strokeWeight(5);  
    beginShape();
    curveVertex(amyX*.95,amyY+48);
    curveVertex(amyX-5,amyY+6);
    curveVertex(amyX+16,amyY-29);
    curveVertex(amyX+54,amyY-61);
    curveVertex(amyX+71,amyY-37);
    curveVertex(amyX+109,amyY-40);
    curveVertex(amyX+132,amyY+4);
    curveVertex(amyX+102,amyY+56);
    curveVertex(amyX+82,amyY+85);
    curveVertex(amyX+57,amyY+56);
    curveVertex(amyX+9,amyY+48);
    curveVertex(amyX+9,amyY+20);
    curveVertex(amyX-2,amyY+15);
    curveVertex(amyX-5,amyY+6);
    curveVertex(amyX-5,amyY+6);
    endShape(CLOSE);
    //face
    fill(0);
    noStroke();
    if(food.foodDropped){
      ellipse(amyX+55,amyY,15,4);
      ellipse(amyX+75,amyY,15,4);
      ellipse(amyX+60,amyY+15,20,15);
    } else {
      ellipse(amyX+55,amyY,10,15);
      ellipse(amyX+75,amyY,10,15);
      arc(amyX+60,amyY+10,15,5,0,PI);
    }
  }

  void moveAmy(){
    amyX = amyX + xSpeed; 
      if((amyX >= width-width/100) || (amyX <= width-width*1.4)) {
        xSpeed = xSpeed *-1;
      }
    amyY = amyY + ySpeed;
      if((amyY >= height*2.2-height) || (amyY <= height-height*1.2)) {
        ySpeed = ySpeed *-1;
      }
  }   

  void petriDishLabel(){
    textSize(25);
    fill(petriDish*500);
    text("Petri Dish 42:",10,30);
    text("Amy the Amoebae",10,65);
  }

}

Food Class

class Food{
  Amy amy;
  float xFood, yFood;
  boolean foodDropped = false;
  int foodColour;

Food(){
  //xFood = mouseX;
  //yFood = mouseY;
  foodColour = color(random(0,255),random(0,255),random(0,255));
}

  void dropFood(){
    fill(foodColour);
      if(foodDropped){
        rect(xFood,yFood,25,25,9);
      }
  }

  void eatFood(){ amy = new Amy();
    if((int(amy.amyX+60)>=xFood-3 && int(amy.amyX+60)<=xFood+3) && 
       (int(amy.amyY+22)>=yFood-3 && int(amy.amyY+22)<=yFood+3)){
          food.foodDropped=false;
          amy.amyColour = foodColour;
    } 
  }

  void chaseFood(){ amy = new Amy();
    if(foodDropped){
      amy.amyX = amy.amyX + amy.xSpeed*1.2;
      amy.xSpeed = amy.xSpeed *-1;
      amy.amyY = amy.amyY + amy.ySpeed*1.2;
      amy.ySpeed = amy.ySpeed *-1;
      amy.amyX = (1-amy.easing) * amy.amyX + amy.easing * food.xFood-3;
      amy.amyY = (1-amy.easing) * amy.amyY + amy.easing * food.yFood-1;  
    }
  }
}
1 Like

This is a bit hard to jump into, because you describe several different configurations of your software, with several different problems (between you and one goal).

Could you restate as one problem, one goal? Where are you now?

Also, I’m not understanding in conceptual terms what food.eatFood() is supposed to mean. The food eats itself? I’m pretty confuse by all the methods of the Food class – eating and chasing seem like they are actions for the amoeba. Of course, there may be something I’m not understanding.

Based on what you describe, I’d imagining something like this (very simplified, no x/y, no multiple agents, etc.):

ArrayList<Food> dish;
Eater amy;

void setup(){
  amy = new Eater();
  dish = new ArrayList<Food>();
  for(int i=0; i<4; i++){
    dish.add(new Food());
  }
}

void draw(){
  if(!dish.isEmpty()) {
    amy.eat(0);
  } else {
    amy.digestAll();
    exit();
  }
}

class Food {
  int calories;
  Food(){
    calories = (int)random(0,5);
  }
}

class Eater {
  ArrayList<Food> stomach;
  int calories = 0;
  Eater(){
    stomach = new ArrayList<Food>();
  }
  void eat(int index){
    if(dish.size() > index){
      println("+", dish.get(index).calories);
      stomach.add(dish.remove(index));
    }
  }
  void digestAll(){
    for(Food f : stomach){
      calories += f.calories;
    }
    stomach.clear();
    println("=", calories);
  }
}

To describe this code: Food has a value. In setup, we make food and put it in the dish. The dish holds the food. Every frame, the eater eats. When it eats, the food moves from the dish to the eater’s stomach. When the eater digests, food disappears from the eater’s stomach and its values move to the eater.

Now, you could add a second eater and loop through a list of eaters in draw, giving each a chance to eat. Then you can further complicate it with for example collision checks between the location of the eater and the location of the food – but to start, just get the eating concept working, apart from any spatial model or any graphical representation.

2 Likes