
Space Warz: Java Game Inspired by Space Invaders
Timeline: | Nov 16-20, 2013 | |
Languages Used: | Java | |
School: | Colorado College | |
Course: | CP122 Computer Science I |
Watch the Demo
In the fall of 2013, for my Computer Science I final project, I set out to build a classic: a 2D top-down space shooter. The result was Space Warz, a game heavily inspired by the timeless arcade classic Space Invaders. Built over a single weekend, it features waves of enemy ships, mini-bosses, player shields, and the satisfying chaos of laser fire.
It was a whirlwind of a project, pushing my early Java skills to their limits, and while it had its share of bugs (as any weekend project might!), it was an incredibly fun and rewarding dive into game development.
If you’re curious to try it out yourself, feel free to download the executable versions below.
💡 Tip
If you’re on Mac or Linux, use Wine to run the
exe
!
Building Space Warz: Under the Hood
Space Warz was my first real foray into building an interactive application from the ground up, and it was a crash course in core programming concepts like game loops, event handling, and object-oriented design.
The Game’s Heartbeat: GamePanel
At the core of Space Warz was the GamePanel.java
class, acting as the main stage for all the action. This is where the game’s “heartbeat” resided – a javax.swing.Timer
that continuously updated the game state and redrew the screen every 15 milliseconds. This rhythmic call to actionPerformed
and repaint
brought everything to life.
Here’s a glimpse into the central game loop that kept everything moving:
1// GamePanel.java snippet
2public void actionPerformed(ActionEvent e) {
3 // Only update game state if not paused, won, or game over
4 if (alive && !won && !paused) {
5 // Update player position based on input
6 spr.moveSprite();
7
8 // Move player's bullets
9 bullets = spr.getBulletsFired();
10 for (int i = 0; i < bullets.size(); i++) {
11 if (bullets.get(i).isInScreen())
12 bullets.get(i).moveSprite();
13 else {
14 bullets.remove(i);
15 }
16 }
17
18 // Move enemies and their bullets, handle their logic
19 for (int i = 0; i < enemies.size(); i++) {
20 if (!enemies.get(i).isDying()) {
21 enemies.get(i).moveSprite();
22 // Randomly fire bullets
23 // ...
24 }
25 // ... move enemy bullets ...
26 }
27
28 // Check for interactions between all game objects
29 checkForCollision();
30 } else {
31 // Handle game over, win, or paused states
32 }
33
34 // Request a redraw of the entire panel
35 this.repaint();
36}
And the paint
method, responsible for rendering every sprite, star, and UI element on the screen:
1// GamePanel.java snippet
2public void paint(Graphics g) {
3 super.paint(g);
4 Graphics2D g2 = (Graphics2D) g;
5
6 // Draw background stars
7 for (StarSprite star : stars)
8 g2.drawImage(star.getSpriteImage(), star.getXPos(), star.getYPos(), this);
9
10 // Draw player's bullets
11 bullets = spr.getBulletsFired();
12 for (BulletSprite blt : bullets)
13 g2.drawImage(blt.getSpriteImage(), blt.getXPos(), blt.getYPos(), this);
14
15 // Draw enemies, rotating them to face the player
16 for (int z = 0; z < enemies.size(); z++) {
17 // ... draw enemy bullets ...
18 double rotationDegrees = getEnemyRotAngle(enemies.get(z));
19 // ... apply rotation and draw enemy sprite ...
20 }
21
22 // Draw the player ship, also rotated towards the mouse cursor
23 double sprCenterX = (double) spr.getWidth() / 2;
24 double sprCenterY = (double) spr.getHeight() / 2;
25 AffineTransform spr_atrans = AffineTransform.getRotateInstance(angleToRotate, sprCenterX, sprCenterY);
26 AffineTransformOp spr_atop = new AffineTransformOp(spr_atrans, AffineTransformOp.TYPE_BILINEAR);
27 if (spr.isVisible())
28 g2.drawImage(spr_atop.filter(spr.getSpriteImage(), null), spr.getXPos(), spr.getYPos(), this);
29
30 // ... draw UI elements like score, lives, level ...
31
32 g.dispose();
33}
Player Control and Projectiles
The player’s ship, represented by the PlayerSprite.java
class, responded to keyboard input for movement. When the player pressed a key, keyPressed
would update the intended direction, and the moveSprite
method would then apply that movement, gradually accelerating or decelerating for a smoother feel.
Here’s how key presses initiated movement:
1// PlayerSprite.java snippet - Handling keyboard input
2public void keyPressed(KeyEvent e) {
3 int keyCode = e.getKeyCode();
4 if (!dying) { // Can't move if exploding!
5 // Move right or 'D' key
6 if ((keyCode == KeyEvent.VK_RIGHT || keyCode == KeyEvent.VK_D)) {
7 if (changex == 0) accel = true; // Start accelerating if at a stop
8 changex = PLAYER_SPEED;
9 }
10 // Move left or 'A' key
11 else if ((keyCode == KeyEvent.VK_LEFT || keyCode == KeyEvent.VK_A)) {
12 if (changex == 0) accel = true;
13 changex = -PLAYER_SPEED;
14 }
15 // ... similar logic for UP/W and DOWN/S ...
16 }
17}
When the player fired, a BulletSprite
object was created. Its trajectory was determined by the player’s position and the mouse cursor’s position, ensuring bullets flew directly where the player was aiming. The BulletSprite
then simply updated its coordinates each frame based on this calculated path.
1// PlayerSprite.java snippet - Firing a bullet
2public void fireBullet() {
3 bulletsFired.add(new BulletSprite(panel, x + PLAYERSPRITE_WIDTH / 2, y + PLAYERSPRITE_HEIGHT / 2, bulletDistX, bulletDistY, 1, 0));
4}
1// BulletSprite.java snippet - Bullet movement
2public void moveSprite() {
3 // Update position based on pre-calculated X and Y increases
4 xPos = xPos - (int) xIncrease;
5 yPos = yPos - (int) yIncrease;
6
7 // Make bullet invisible if it goes off-screen
8 if (yPos < 0 || yPos > GAMESCREEN_HEIGHT || xPos < 0 || xPos > GAMESCREEN_WIDTH)
9 bulletVisible = false;
10}
Enemy AI and Behavior
The Enemy1Sprite.java
class defined our standard enemy ships. They had basic vertical movement, often cycling from top to bottom or vice-versa. A more interesting element was their “gravitational pull” – a subtle AI that nudged them towards the player if they were within a certain distance, making them feel a bit more dynamic. They also had a simple random chance to fire bullets, adding to the challenge.
1// Enemy1Sprite.java snippet - Movement and 'gravitational' pull
2public void moveSprite() {
3 // Basic vertical scrolling movement
4 if (direction.equals("down")) {
5 if (yPos > GAME_HEIGHT)
6 yPos = 0 - GAME_HEIGHT * 1; // Loop back to top
7 yPos = yPos + ENEMY_SPEED;
8 } else {
9 if (yPos < 0 - height)
10 yPos = GAME_HEIGHT + GAME_HEIGHT * 1; // Loop back to bottom
11 yPos = yPos - ENEMY_SPEED;
12 }
13
14 // If enemy is on screen, apply a "gravitational" pull towards the player
15 if (yPos > 0 && yPos < GAME_HEIGHT) {
16 sprXPos = sprPlayer.getXPos();
17 sprYPos = sprPlayer.getYPos();
18 sprDistX = sprXPos - xPos;
19 sprDistY = sprYPos - yPos;
20
21 // If within gravitation range, calculate and apply pull
22 if (((sprDistX < 0 && sprDistX > -DIST_TO_GRAVITATE) || (sprDistX > 0 && sprDistX < DIST_TO_GRAVITATE)) &&
23 ((sprDistY < 0 && sprDistY > -DIST_TO_GRAVITATE) || (sprDistY > 0 && sprDistY < DIST_TO_GRAVITATE))) {
24 // ... complex gravity calculation based on distance ...
25 yPos += gravPullY;
26 xPos += gravPullX;
27 }
28 }
29}
Collision Detection
Crucial for any game, the checkForCollision
method in GamePanel.java
was a workhorse. It systematically checked for intersections between different game entities – player, player bullets, enemies, and enemy bullets – and triggered appropriate consequences like health reduction, explosions, or score updates.
1// GamePanel.java snippet - Collision detection logic
2public void checkForCollision() {
3 Rectangle shipSize = spr.getSpriteSize();
4
5 // Check Player vs. Enemy collisions
6 for (Enemy1Sprite enm : enemies) {
7 Rectangle enmSize = enm.getSpriteSize();
8 if (enm.isCollidable() && shipSize.intersects(enmSize) && !playerDying) {
9 killEnemy(enm); // Enemy explodes
10 if (!spr.isHurting())
11 killPlayer(); // Player takes damage or dies
12 }
13 }
14
15 // Check Player vs. Enemy Bullet collisions
16 for (ArrayList<BulletSprite> enemyBulletsList : enemyBullets) {
17 for (BulletSprite blt : enemyBulletsList) {
18 Rectangle bltSize = blt.getSpriteSize();
19 if (bltSize.intersects(shipSize) && !playerDying) {
20 blt.setVisible(false); // Bullet disappears
21 if (!spr.isHurting())
22 killPlayer();
23 }
24 }
25 }
26
27 // Check Player Bullet vs. Enemy collisions
28 for (BulletSprite playerBullet : bullets) {
29 Rectangle bltSize = playerBullet.getSpriteSize();
30 for (Enemy1Sprite enm : enemies) {
31 if (enm.isCollidable() && bltSize.intersects(enmSize)) {
32 soundExplode.playSound();
33 playerBullet.setVisible(false); // Player bullet disappears
34 killEnemy(enm); // Enemy explodes
35 score += 15; // Earn points!
36 }
37 }
38 }
39 // ... similar checks for mini-bosses and other interactions ...
40}
Lessons Learned
Looking back, Space Warz was more than just a game; it was a foundational learning experience. Much like Duct-O, it provided a rich environment for practical lessons, though its focus was purely technical rather than business-oriented.
- Game Loop Fundamentals: Building a robust
actionPerformed
andpaint
cycle taught me the core rhythm of real-time applications. Understanding how state updates and rendering combine is critical for any interactive software. - Object-Oriented Design in Practice: Creating separate classes for
PlayerSprite
,Enemy1Sprite
,BulletSprite
, etc., wasn’t just academic; it was essential for managing complexity in a dynamic system. It showed me the power of abstraction and encapsulation. - Event-Driven Programming: Handling keyboard and mouse inputs, and tying them to game actions, solidified my understanding of event listeners and responders.
- Debugging and Iteration: Weekend projects are brutal but effective teachers. Every bug was a puzzle, and fixing it iteratively refined both the code and my problem-solving skills.
- The Joy of Creation: While Duct-O had a distinct physical component, Space Warz was a pure dive into bringing a creative vision to life with code. The satisfaction of seeing sprites move, lasers fire, and enemies explode was immense, setting the stage for future projects and reinforcing my passion for building.
Screenshots
Source Code
Space Warz, though simple, was a testament to what can be built in a short time with focused effort, and it laid crucial groundwork for my later, more complex endeavors.