11.2 – Arrays of Objects

Arrays of objects really aren’t all that different from arrays of int’s. Nor is initializing an array of objects all that different from initializing one object.

Take our car example. Here’s just the constructor part of the Car class:

Car(color c_, float xpos_, float ypos_, float xspeed_) {
   c = c_;
   xpos = xpos_;
   ypos = ypos_;
   xspeed = xspeed_;
  }

 

When I declare my cars array of length 100- I’m saying, make 100 car objects. That’s all I have to do to decide how many cars to make. Everything from here on out else stays the same whether it’s 2 cars or 1000 cars.

Pay attention to the the part where we initialize each element of the Car Array. Note the use of i as the new car’s arguments.

When we’re making lots and lots of objects, it’s important that we give them some kind of variation so we can tell the difference between our many objects. When we create them one by one (the old way), this is easy. We tell one car to be red, another to be blue, so on and so forth. Automation (aka using a for loop) poses a problem- how do we make every object different when we’re repeating the same task over and over again?

One way is to call the random function. If you know you want to have your cars scattered up and down your sketch’s window, you can call random(height), producing a new random number between 0 and your window’s height for the starting placement of your car.

But sometimes you want to have a little bit more control over the randomness. Another method is using “i” to your advantage. You have a variable at your disposal that is different every single time we cycle through the for loop. Sometimes we can use that variable directly but other times we just need to get a little clever with math to turn that variable into something that’s useful to us. Each scenario is going to require a little problem solving, so I encourage you to email me if you feel stuck about how to use i to create the kind of objects you want.

In the above example, i is going from 0 – 100. When we need to make a new car, we also need to define a color for it, a variable we know goes from 0-255. That’s why we do i*2 to give us (almost) a full variation of the available colors. The first car will be black, the next car will be a little less black, the next car will be even less black, and so on until the cars are (nearly) white.

So we’ve made our objects, now we need to be able to call the functions we defined for them. This is easy. We’ve put all our objects into our array (the filing cabinet that holds all our objects), now we just need to visit each drawer and tell it to display, move, bounce, draw, etc.

for(int i = 0; i < cars.length; i++){
   cars[i].move();
   cars[i].display();
}

Note the use of cars.length– we could program in the exact number of cars that are in our array, but it’s better to call cars.length, which will automatically call the exact size of our array. We know the size of our array when we say:

Car [] cars = new Car[100];

I can easily decide to make 500 cars instead of 100 by changing the size of my array. That’s all I have to do! The rest of our code will stay the same.

But back to the point- it’s not a good idea to program in the hard value of our array’s size. If we do decide to make more objects (or as you’ll see later, use the ‘append’ function), you have to be able to update the end of your for loop too. Putting in cars.length is idiot proof- it saves us the hassle of having to remember how many objects we made in the first place.

Cycling through the for loop above visits every car in your array and tells it to display, drive, turn around, etc.

Here’s the example we went over in class today that refers to the code we looked at last week that used a ball to test for a collision with a paddle. Now we have lots of balls!

MyBall[] balls = new MyBall[100];
MyRect paddle;

void setup() {
  size(400, 600);
  paddle = new MyRect();
  for (int i = 0; i < balls.length; i++) {
    balls[i] = new MyBall(int(random(10, 20)),int(random(10, 20)), int(random(0, height)), i*2 );
  }
}

void draw() {
  background(0);
  paddle.display();
  for (int i = 0; i < balls.length; i++) {     
     balls[i].display();     
     balls[i].inActive();     
     balls[i].moveTheBall();     

     if (balls[i].collision(paddle)) {       
        balls[i].bounce();     
     }   
   } 
 } 

class MyBall {   

int ballx;   
int bally;   
int ballw;   
int ballh;   
int speed;   
color ballColor;   
boolean active;   

MyBall(int tempSpeed, int tempX, int tempY, color tempColor) {     ballx = tempX;     
 bally = tempY;     
 speed = tempSpeed;     
 ballw = 10;     
 ballh = 10;     
 ballColor = tempColor;     
 active = true;
}   

void display() {     
  if(active){  //asking if a ball is still on the screen   
    fill(ballColor);     
    ellipse(ballx, bally, ballw, ballh);     
  }   

}   

void moveTheBall() {     
  ballx += speed;   
}      

void bounce(){    
  speed = speed * -1;    
}      

void inActive(){    
if(ballx > width){ //is the ball off the screen?
    active = false; //if so, it's no longer active
   } 
  }

 boolean collision(MyRect theRect) {
    if (ballx + ballw > theRect.x && bally > theRect.y && bally < theRect.y + theRect.h) {
      return true;
    }
    else if(ballx < 0){
      return true;
    }
    else {
      return false;
    }
  }
}

class MyRect {
  int x;
  int y;
  int w;
  int h;

  MyRect() {
    w = 10;
    h = 200;
    x = width - 20;
  }

  void display(){
    fill(255);
    y = mouseY; //why does this need to be in display?
    rect(x, y, w, h);
  }
}

 

Back to the “append” function– I’ve been lying to you for the past week. You actually can change the size of your array on the fly, but you have to follow some strict rules about doing so. You can tag on an object to the end of your array, but you have to have that object ready to go in advance! There’s also some obnoxious syntax to get straight.

Say, for instance, you want to make a new bouncing ball object every time you press the mouse (or a new Connect 4 game piece, an “x” or an “o” for tic-tac-toe, or a new bullet to shoot at your asteroid). First, you make the new object.

Ball b = new Ball(mouseX,mouseY,16);

(All the new balls can be named ‘b’. As soon as you store them in your array, they take on the name arrayName[index], so it’s ok to use ‘b’ over and over again with every click of the mouse).

The next part is syntax heavy. First, you need to set the whole following shebag equal to the array you want to update.

balls = …..

Next, you need to tell it what data type this new object your sticking in is. You put this in () after saying which array it is.

balls = (Balls[]) …..

Now you call the “append” function. This function has two arugments, the first is the array name (again), and the second is the new object that you just created.

  Ball b = new Ball(mouseX,mouseY,16); 
  balls = (Ball[]) append(balls,b);

Again, the syntax here is new, kinda weird, and really strict. Triple check that you have it right! That’s really all that’s complicated about it. Utilizing this function gives you SO much power over the control of your program- such as in the following program that creates a new bouncing ball (affected by gravity) starting at the location of the mouse.

// Learning Processing
// Daniel Shiffman
// http://www.learningprocessing.com

// Example 9-11: Resizing an array using append()

Ball[] balls = new Ball[1]; // We start with an array with just one element.
float gravity = 0.1;

void setup() {
  size(200,200);
  smooth();

  // Initialize ball index 0
  balls[0] = new Ball(50,0,16);
}

void draw() {
  background(255);

  // Update and display all balls
  for (int i = 0; i < balls.length; i ++ ) { // Whatever the length of that array, update and display all of the objects.     
balls[i].gravity();     
balls[i].move();     
balls[i].display();   
} 
} 

void mousePressed() {   
// A new ball object   
Ball b = new Ball(mouseX,mouseY,16); // Make a new object at the mouse location. balls = (Ball[]) append(balls,b); // Here, the function, append() adds an element to the end of the array. // append() takes two arguments. The first is the array you want to append to, and the second is the thing you want to append. // You have to reassign the result of the append() function to the original array. // In addition, the append() function requires that you explicitly state the type of data in the array again by putting the // array data type in parentheses: (Ball[]) This is known as casting. } class Ball { float x; float y; float speed; float w; Ball(float tempX, float tempY, float tempW) { x = tempX; y = tempY; w = tempW; speed = 0; } void gravity() { // Add gravity to speed speed = speed + gravity; } void move() { // Add speed to y location y = y + speed; // If square reaches the bottom // Reverse speed if (y > height) {
      speed = speed * -0.95;
      y = height;
    }
  }

  void display() {
    // Display the circle
    fill(175);
    stroke(0);
    ellipse(x,y,w,w);
  }
}

 

Leave a Reply

Your email address will not be published. Required fields are marked *