SPACE SHOOTER

 QUICK FIXES

MOVING IN FOUR DIRECTIONS

PLAYER

Moving Left

In the starting code, the player can only move to the left.  This is defined in the act() method, shown below:

if(getKey('a'))

{

xSpeed -= PLAYER_SPEED;

}        

Moving Right

To allow the player to move in different directions, we simply need to add other key commands to this list.  For example, use the getKey() method with the letter 'd' to try and move to the right by adding the player's speed.


if(getKey('d'))

{

xSpeed += PLAYER_SPEED;

}     

Other Directions

CASE INSENSITIVITY

PLAYER

Taking Both Cases

The starting code has a small bug;  it only works on lowercase letters!  This means that if the user accidentally hits the caps lock key, they won't move at all.  Let's make each direction respond to both capital and lower case letters.

if(getKey('a') || getKey('A'))

{

xSpeed -= PLAYER_SPEED;

}        

STAY ON SCREEN

PLAYER 

Moving Left

We need to add a requirement to our movement to make sure that the player only moves to the left when that move would not put them off the screen.  We'll need to add another case to our if statement.  Be careful with parentheses to make sure our logic is correct.

if((getKey('a') || getKey('A')) && x > 0)

{

xSpeed -= PLAYER_SPEED;

}        

Other Directions

SHOT COOLDOWN

PLAYER 

Making a Timer

There are many ways to solve this problem, but we'll start by using a system that you might commonly want to implement:  a shot timer with a specified cooldown.


First, we'll start by adding a new variable to the Player class.   Make sure it is declared inside the player class but not inside any methods.

class Player extends GameObject

{

int shotTimer = 0;

...

Shooting and Cooldown

Next, we'll modify the shooting code to (1) only shoot when shotTimer is zero and (2) set the shotTimer to PLAYER_COOLDOWN.

if (getKey(' ') && shotTimer == 0)

{

objects.add(new PlayerShotBasic(x, y));  

objects.add(new PlayerShotBasic(x+24, y));  

shotTimer = PLAYER_COOLDOWN;

}

Note that we didn't need to declare PLAYER_COOLDOWN because it was already setup for you in values.  Take a look over there and try changing the number it represents.  The larger the value, the longer time between shots.

Updating the Timer

Finally, we need to add code to decrease our shot timer every frame until it reaches zero.  Anywhere in the act method, you can add the following code:

if(shotTimer > 0)

{

shotTimer--;

}

TAKING DAMAGE

GAME OBJECT 

The takeDamage() method

This method is called automatically when there is a collision.  Every GameObject has an amount of health represented by the variable curHealth.   We want an object to lose health , but the current code just kills the object!

public void takeDamage(float amount)

{

die();

}

Reducing curHealth

We can modify takeDamage to reduce health by the amount of damage passed to this method. 

public void takeDamage(float amount)

{

curHealth = curHealth - amount;

if(curHealth <= 0)

{

   curHealth = 0;

   die();

}

}

Setting starting health for Player and EvilSquare

Notice that when you run this code, it won't do anything!  This is because all objects have exactly 1 health to start.

To fix this, modify the constructors of both the player and evil square to set starting current and maximum health values.   

Reminders:

Player(float x, float y)

{   

      super(x, y, blueTriangle.width, blueTriangle.height);

      image = blueTriangle;

      

      curHealth = PLAYER_HEALTH;

      maxHealth = PLAYER_HEALTH;

}

DEBUG MESSAGES

GAME OBJECT 

Getting Information

So often while you're working on this program, it might not be clear WHY something isn't working.  For instance, when we fixed health we might think our code was incorrect.  But actually, we just hadn't set the proper health values.

To get information, we have two main options.


Print Example:  Shot Timer

Let's imagine that our shot timer wasn't working, and we couldn't tell why.  

We might add a print statement to output the value of the shotTimer each time we fire.  If we notice it never changes from 0, then we know something is wrong with that variable!


println("Shot Timer: " + shotTimer);


Text Example:  Displaying Health

We can also display information on the screen as text.  This is especially useful when:

I recommend that while you're working on your game, you add in code to GameObject to display the health of each unit above its head.  It's simple and we can comment out this line later for release.


void render()

{

image(image, x, y, w, h);

if(maxHealth != 1)

{

   textAlign(CENTER, CENTER);

   text((int) curHealth, x + w / 2, y - 15);

  }

}


A few notes on the code above:

PROJECTILE DURATION

PROJECTILE

Why Set A Duration?

Making projectiles have a limited duration has two important features:

Projectile Class

void act()

{

super.act()


if(timer > duration)

{

   die();

  }

}

PlayerShotBasic

RedShot

BETTER ENEMY

EVIL SQUARE

Enemy Shooting

Copy the player's shooting code and add into EvilSquare.  This includes all of the shotTimer elements.


If you run this code, you'll find that pressing space bar causes the enemy to shoot player bullets and they are immediately removed from the game.  Ooops! 


You'll need to make a few changes to enemies:


if (shotTimer == 0)

{

objects.add(new RedShot(x, y));  

shotTimer = EVIL_SQUARE_COOLDOWN;

}

Enemy Movement

You'll need to make your first enemy do more than simply move straight down.  Start by changing the constants that define their speed in values.  For instance, consider making them move diagonally down and to the right.


You'll need to handle the edges of the screen.   Some common solutions:


This is a game design decision.   Both are okay. Which one looks better?  Feels more fun?  Choose the method that fits your vision.

You can certainly make this enemy more complex, consider things like: