SPACE SHOOTER
PROGRESSION
THREE STYLES
The Idea
In your game, players must have some way of getting stronger as the game progresses. The most common options are:
Shop System (★★)
When an enemy is defeated it awards the player money. Money can be expended at a shop to buy upgrades. Shop can be the pause screen or a new game state between levels.
Level Up System (★★)
When an enemy is defeated it awards the player experience. At a certain amount of experience, the player levels up. At each level they get to pick an upgrade
Roguelike System (★★★★)
Between waves the player is presented with three possible upgrades, randomly selected from a list of upgrades. The player can choose one each time. This makes every game different, while still allowing player choice.
Roguelike systems have gained a lot of popularity in recent years. The game Nova Drift is an example of a modern Space Shooter that uses roguelike progression for its upgrades.
SHOP SYSTEM
MAIN / ENEMY
The Default Path
Mr. M is only providing a tutorial for one of these systems, assuming the most basic implementation of a shop system. This method will add money every time an enemy is defeated, and allow the player to upgrade while on the pause screen. Note that in order to do this, you must have already implemented a pause game state to your program.
Tracking Credits
Just like we did when we added in lives, we'll need to track another piece of data - the amount of money the player has earned. We will call the unit of money "credits."
At the top of your program declare credits and initialize it to a value. I started with 50 for now, because it will make the program easier to test. Later on we can change it to 0.
int credits = 50;
As usual, we'll want to display this value. Add the relevant code to the renderGameplay() mode to display it alongside wave and lives.
✓ Test your program - you should see the number of credits
Assigning Value To an Enemy
To start, we are going to assign every enemy a value. In the Enemy class, add a new class-level variable and give it the value 1 in the constructor:
abstract class Enemy extends GameObject
{
int value;
Enemy(float x, float y, float w, float h)
{
super(x, y, w, h);
value = 1;
}
/* other code */
}
In enemy subclasses, like Evilsquare, we can now set different values. For example, a difficult enemy might be worth 5 credits instead of one.
Adding Credits To The Player
We have a method in all game objects that is called when an enemy is defeated - the die() method. This is part of the GameObject class. We can override this method in class Enemy. We'll need to complete two steps:
Call super.die() so that it still does the basic dying behavior
Add the unit's value to the player's total credits
void die()
{
super.die();
credits = credits + value;
}
✓ Test your program - when you defeat an enemy, your credits should be going up
Displaying Our Shop
Our simple shop is going to be part of the pause screen, and the user will buy things by pressing a hotkey corresponding to the item they wish to purchase. Our shop should tell the user which items are for sale, which key to press to buy them, and how much they cost.
Since we haven't implemented any fancy upgrades yet, we will start by simply having our shop sell extra lives for 10 credits each. The below example also includes a second item, which we'll add during a later tutorial.
void renderPause()
{
renderGameplay();
/* code to set size, color, alignment */
text("1 - Extra Life (10 credits)", 50, height * .33);
text("2 - Multishot (30 credits)", 50, height * .40);
}
✓ Test your program - the above messages should be displaying on the pause screen.
Buying Upgrades
To buy an item, in the updatePause method, we'll need to check three things:
Did the user press the "1" key?
Does the user have enough money?
Is the pauseTimer zero? (This part is there to avoid multiple activations for holding the button down, using the same system we had in place during the gamestates tutorial).
Once we are approved, we'll need to do three things:
Decrease credits by an appropriate amount
Set the pauseTimer to a fixed value
Apply the upgrade (In this case, add lives)
void updatePause()
{
/* other code */
if(getKey('1') && credits >= 10 && pauseTimer == 0)
{
credits = credits - 10;
pauseTimer = 15;
lives++;
}
}
In this tutorial I am using hardcoded numbers, but I encourage you to create a constant in the Values tab and use that here for things like the credit cost of each upgrade. Notice that the value is repeated twice here, and once in our render method. If I change the cost of an upgrade, I'll need to remember to change it in all three places! If I used a constant, I only need to change it once.
✓ Test your program - can you buy an extra life? What happens if you try to buy one when you can't afford it? Does everything display properly?
Adding Functional Upgrades / Next Steps
In the rubric you are required to add in two functional upgrades. This means adding in an upgrade that changes behavior of your player, not simply changing a number. This means adding one life isn't enough! Visit the upgrades page for more information on this requirement and how to add in complex, interesting power ups for your player.
EXPERIENCE SYSTEM
MAIN / ENEMY
Summary
When a user defeats an enemy, it gains experience
When a user gains enough experience, it gains an upgrade point
Usually this is displayed with a experience bar rather than just a value
All upgrades cost exactly one upgrade point
ROGUELIKE SYSTEM
EVERYWHERE
Summary
This is substantially more complex than the other two. However, if you have a strong grasp of classes and objects it can make your program really cool. It also requires a large pool of upgrades to really make sense.
Gamestates
Add a new gamestate to the game that activates between waves. So it should go wave 1 --> upgrade screen --> wave 2 --> upgrade screen etc...
Upgrade Classes
Create an Upgrade class and a series of subclasses for each upgrade
An upgrade should have a render method that displays its name and cost. Have this method take a parameter and use that to define where it shows up on the screen. For example, "0" might be on the left third, "1" the middle third, and "2" the right third.
Keep them simple at this stage. For now, just try designing two until the system is set up.
Tracking Upgrades
Create an ArrayList of Upgrades in your main program called upgradePool
Add one of each type of upgrade to the pool
Create an ArrayList of Upgrades called availableUpgrades
Each time you go to the upgrade state, clear these upgrades. Loop three times, each time selecting a new upgrade from upgradePool to add to availableUpgrades
Create an ArrayList of Upgrades called myUpgrades
During the upgrade state, when the user selects an upgrade add it to myUpgrades.
Making Upgrades Work
Have the upgrade class contain an use() method. That should be overriden in each subclass. The use() method defines what happens when the player has the upgrade. For example, you might have it contain code to fire additional shots when the user presses space bar.
Each frame in player's update() method, loop through all of myUpgrades and call the use() method.