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

  • Use "W" and "S" to move up and down respectively.

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 - PLAYER_SPEED > 0)

{

xSpeed -= PLAYER_SPEED;

}

Other Directions

  • Each movement will need to do a similar check. Think carefully about whether it deals with x or y, 0 or width, and whether we add or subtract speed.

  • Test all sides in your game before moving on!

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.


Temporary Note (4/21):

  • If you find that it still isn't working, check the GameObject constructor. You'll want to set damage to a positive value, such as 1.

  • This means that *any* collision between any objects will do some damage, including when your ship hits an evil square. If you want to make it do damage more slowly, pick a small value like 0.01. Later on, we can set more specific damage values for things like player shots and enemy shots.

  • This has been fixed in the newest downloaded version, so if you started the project after the original day you may not have this issue.


Reminders:

  • You do not declare curHealth and maxHealth because they are inherited from GameObject.

  • You'll need to create new constants in values for the code below to work. Try different numers and see what works.

  • Repeat this example for EvilSquare.

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.

  • Use println() to output information to the console

  • Use text() to draw information on the screen


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:

  • The information applies to a lot of different objects at the same time

  • The information is tied to position or a visual element

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:

  • We don't display objects with a max health of 1 (avoids health on every bullet)

  • Uses (int) to avoid decimal values

  • Adds w / 2 to x and uses textAlign to center it

  • Subtracts 15 from y to put it above the object

PROJECTILE DURATION

PROJECTILE

TO DO

ADD ME IN 2023

BETTER ENEMY

EVIL SQUARE

Enemy Shooting

Copy the player's shooting code and add into 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:

  • Have them fire an enemy shot called RedShot() instead of PlayerShotBasic()

  • Remove the keyPress condition

  • Make the shotTimer change based on EVIL_SQUARE_COOLDOWN


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:

  • Enemies bounce off the right and left sides of the screen

  • Enemies wrap from the right to the left and vice versa


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:

  • Making the enemy stop moving to fire

  • Add in acceleration and deceleration

  • Have enemies move in different directions (some go left, some go right)