A Spinning Box

In these notes we’ll create a class for a rectangle that can move and spin. It’s a good example of how to go about creating your own animated sprites.

We will need the Sprite class:

class Sprite {
  float x;
  float y;
  float dx;
  float dy;

  void update() {
    x += dx;
    y += dy;
  }
}

Here is the SpinningBox class, and its variables:

class SpinningBox extends Sprite {
    float width;
    float height;

    float angleInDegrees;
    float dAngle; // rate of change of angle

    color fillColor;
    color strokeColor;

    // ...

} // class SpinningBox

Recall that extends Sprite means that a copy of all the variables and functions in the Sprite class will be put in SpinningBox. So, we don’t need to add x, y, dx, or dy because they are inherited from Sprite.

The render() function draws the box, while the update() function makes it move:

void render() {
  rectMode(CENTER);
  pushMatrix();

  // move the origin to the center of the box
  translate(x, y);

  rotate(radians(angleInDegrees));

  fill(fillColor);
  stroke(strokeColor);
  rect(0, 0, w, h);

  popMatrix();
}

void update() {
  super.update();
  angleInDegrees += dAngle;
}

This draws the box centered at, and rotated around, the point (x, y). The statement super.update() calls that update() function in the Sprite class. The term “super” refers to the fact that Sprite is the “super class” of SpinningBox.

Here’s a program that animates it:

SpinningBox b = new SpinningBox();

void setup() {
  size(500, 500);

  b.x = 250;
  b.y = 250;
  b.w = 200;
  b.h = 150;
  b.angleInDegrees = 0;
  b.dAngle = 2;
  b.fillColor = color(0, 255, 0);
  b.strokeColor = color(255, 0, 0);
}

void draw() {
  background(255);

  b.render();
  b.update();
}

Changing update

To change how the box moves, modify update(). For example:

class SpinningBox extends Sprite {

  // ...

  void update() {
    // super.update();
    angleInDegrees += dAngle;
    w = map(mouseY, 0, 500, 50, 300);
    h = map(mouseX, 0, 500, 50, 300);
    x = mouseX;
    y = mouseY;
  }
} // class SpinningBox

The call to super.update() has been commented-out but left in as a reminder that we can call if it we want the default Sprite update behaviour.

Multiple Spinning Boxes

It’s easy to make as many spinning boxes as you like. For example:

Box b = new Box();
Box c = new Box();

void setup() {
  size(500, 500);

  b.x = 250;
  b.y = 250;
  b.w = 200;
  b.h = 150;
  b.angleInDegrees = 0;
  b.dAngle = 2;
  b.fillColor = color(0, 255, 0);
  b.strokeColor = color(255, 0, 0);

  c.x = 25;
  c.y = 350;
  c.w = 400;
  c.h = 15;
  c.angleInDegrees = 0;
  c.dAngle = -0.5;
  c.fillColor = color(23, 255, 44);
  c.strokeColor = color(255, 200, 100);
}

void draw() {
  background(255);

  b.render();
  b.update();

  c.render();
  c.update();
}

The procedure for creating and using sprites is always the same:

  • Create a class for your animated object that extends Sprite.

  • Add a render() function that draws the object based on the current values of its variables.

    Do not put any code that changes variables in render()! Put all such code into update().

  • Add a customized update() function, calling super.update() (the update() function from Sprite) if necessary. If you are happy with the default update() in Sprite, then you don’t need a custom update().

    Do not put any drawing code in update! All drawing code should be in render().

To use a sprite, follow these steps:

  • Create an object using new and assign it to a variable, e.g.:

    SpinningBox b = new SpinningBox();
    
  • Inside setup(), assign initial values to all the variables in the object.

  • Inside draw(), call the objects render() function to display it on the screen, and its update() function to change its variables.

An Image Sprite

An image sprite is an animated object that draws an image.

Images behave similarly to rectangles, and so we will use SpinningBox as a guide for writing the ImageSprite class:

class Sprite {
  float x;
  float y;
  float dx;
  float dy;

  void update() {
    x += dx;
    y += dy;
  }
}

class ImageSprite extends Sprite {
  PImage img;
  float angleInDegrees;
  float dAngle;

  void render() {
    imageMode(CENTER);
    pushMatrix();

    // move the origin to the center of the image
    translate(x, y);

    rotate(radians(angleInDegrees));

    image(img, 0, 0);

    popMatrix();
  }

  void update() {
    // super.update();
    x = mouseX;
    y = mouseY;
    angleInDegrees += dAngle;
  }
}

ImageSprite a = new ImageSprite();

void setup() {
  size(500, 500);

  a.img = loadImage("cat.jpg");
  a.x = 250;
  a.y = 250;
  a.angleInDegrees = 0;
  a.dAngle = 2;
}

void draw() {
  background(255);

  a.render();
  a.update();
}

For this code to work, you must have an image file named cat.jpg in your program’s data folder.

Questions

  1. Why are x and y not defined in the SpinningBox class?

  2. What does the statement super.update() mean in the update() function of SpinningBox? What would happen if that statement were deleted?

  3. What is the value of b immediately after this statement is executed:

    SpinningBox b;
    

Programming Questions

  1. Make a class called SpinningTriangle that extends Sprite and makes a triangle move around the screen, and also rotate around one of its corner points.
  2. Make a class called BouncingBox that extends Sprite and makes a box bounce around the screen. Don’t let any part of the box go off the screen.

Source Code

class Sprite {
  float x;
  float y;
  float dx;
  float dy;

  void update() {
    x += dx;
    y += dy;
  }
}

class Box extends Sprite {
  float w;
  float h;

  float angleInDegrees;
  float dAngle; // rate of change of the angle

  color fillColor;
  color strokeColor;

  void render() {
    rectMode(CENTER);
    pushMatrix();

    // move the origin to the center of the box
    translate(x, y);

    rotate(radians(angleInDegrees));

    fill(fillColor);
    stroke(strokeColor);
    rect(0, 0, w, h);

    popMatrix();
  }

  void update() {
    // super.update();
    angleInDegrees += dAngle;
    w = map(mouseY, 0, 500, 50, 300);
    h = map(mouseX, 0, 500, 50, 300);
    x = mouseX;
    y = mouseY;
  }
} // class Box

Box b = new Box();
Box c = new Box();

void setup() {
  size(500, 500);

  b.x = 250;
  b.y = 250;
  b.w = 200;
  b.h = 150;
  b.angleInDegrees = 0;
  b.dAngle = 2;
  b.fillColor = color(0, 255, 0);
  b.strokeColor = color(255, 0, 0);

  c.x = 25;
  c.y = 350;
  c.w = 400;
  c.h = 15;
  c.angleInDegrees = 0;
  c.dAngle = -0.5;
  c.fillColor = color(23, 255, 44);
  c.strokeColor = color(255, 200, 100);
}

void draw() {
  background(255);

  b.render();
  b.update();

  c.render();
  c.update();
}