Friday, January 23, 2015

Anansi: Sprites


Okay, we're eager to finally see stuff happening on the screen. From the prototype we can identify various common denominators. Every sprite has the following attributes:

  • an Image
  • a container, which is an ImageView
  • a layer in which we embed the sprite
  • position x and y
  • rotation r
  • a delta by which x, y and r are modified per frame 
  • the sprite dimensions


And while we're at it we can as well add other attributes which we may need later:

  • health

    When the health is depleted, the sprite is considered dead.
  • damage

    The damage a sprite can inflict on another sprite.

    In order to make things not too complicated we think that every sprite is created using the same material. This means that when sprites collide, then "current health = current health - colliding sprite's damage value"
  • width, height, center

    that information is usually always needed
  • removable

    Determine whether the sprite is removable or not. A sprite is removable when it's outside the screen and won't be visible anymore or when its health is depleted.

Given that information we can also create common methods. The things we need are

  • add a sprite's view to a layer
  • remove the sprite's view from the layer
  • move the sprite internally
  • check health status
  • update the sprite on the playfield layer
  • check the bounds

The methods are simple. Although, what about the bounds checking? How do we know that an object is out of its bounds? A missile may go out of bounds, but can come back. A bullet on the other hand will never return. The one who knows whether it can come back or not is the object itself. So we create an abstract checkRemovability() method in which we set a flag with which the game loop as a sprite manager can decide whether or not the sprite should be disposed of.

So all in all the code is as simple as this:


public abstract class SpriteBase {

 Image image;
 ImageView imageView;

 Pane layer;

 double x;
 double y;
 double r;

 double dx;
 double dy;
 double dr;

 double health;
 double damage;

 boolean removable = false;

 double w;
 double h;

 public SpriteBase(Pane layer, Image image, double x, double y, double r, double dx, double dy, double dr, double health, double damage) {

  this.layer = layer;
  this.image = image;
  this.x = x;
  this.y = y;
  this.r = r;
  this.dx = dx;
  this.dy = dy;
  this.dr = dr;

  this.health = health;
  this.damage = damage;

  this.imageView = new ImageView(image);
  this.imageView.relocate(x, y);
  this.imageView.setRotate(r);

  this.w = image.getWidth();
  this.h = image.getHeight();

  addToLayer();

 }

 public void addToLayer() {
  this.layer.getChildren().add(this.imageView);
 }

 public void removeFromLayer() {
  this.layer.getChildren().remove(this.imageView);
 }

 public void move() {

  x += dx;
  y += dy;
  r += dr;

 }

 public boolean isAlive() {
  return Double.compare(health, 0) > 0;
 }

 public void updateUI() {

  imageView.relocate(x, y);
  imageView.setRotate(r);

 }
 
 public abstract void checkRemovability();

  // -----------------------------------------
  // automatically generated getters & setters
  // -----------------------------------------
  
  ...
}


That seems rather short, but indeed, that's it. And it looks like we can not only create the player's ship, enemy ships and bullets, but also the background and clouds with the same sprite code base. Nice. You may think that a background won't have health and damage, that's right. But you never know what we'll come up with. A cloud doesn't have health and damage either. Only by thinking about how a cloud can have health and damage, we come up with a new gameplay element: Acid clouds! So let's not limit ourselves by unplanned ideas. The base is there, let's use it. Now it's time to add the gameplay elements.

No comments:

Post a Comment