Let’s add movement!
In our game we have a screen that shows all of the aliens and the ship on a black background. Now let’s make everybody dance. In the last part I had you define a bunch of empty subclasses. Why did I do that? Because I wanted to get off topic and show you how cool Groovy can be when it comes to mundane tasks like writing a bunch of redundant code. Well that’s not the only reason. We created subclasses because all of our Entity objects will behave diffeently. We want different behaviour right? You wouldn’t want the Ship to fly up the screen attempting to explode an alien would you? (Well that might make a cool game to but that behaviour doesn’t fit for classic space invaders!) So how does each game piece move?
ShipEntity
The ship is reeeal cool! It can move left all the way to the end of the screen and then when it gets there it can move right all the way to the other end! That’s it. No other movement. Ignoring how “way cool” our ship is, we dive into the code. (The TEST! We should dive into the TEST FIRST right? Well in practice I would dive into the unit test to describe my objects but since I’m copying somebody else’s tutorial and since that other person is smarter than I am I’m going to do as the other person did and dive into the code.) The only thing we put in the ShipEntity class is limits to prevent it from moving off of the screen.
package com.craig.groovyinvaders
public class ShipEntity extends Entity
{
public ShipEntity(Game g, String spriteRef, def locX, def locY)
{ super (g, spriteRef, locX, locY) }
//These are closures or executable "method-like" things that return theresult of the enclosed expression
def imAtTheLeftEdgeOfScreen = {(x < 10)} //an x coordinate less than 10 means we're at the edge
def imMovingLeft = {(dx < 0)} //a negative x direction means we're moving left
def imAtTheRightEdgeOfScreen = {(x > 750)} //an x coordinate greater than 750 means we're at the edge
def imMovingRight = {(dx > 0)}//a positive x direction means we're moving right... Yaay!
public void move(long delta) {
if( (imMovingLeft() && imAtTheLeftEdgeOfScreen()) || (imMovingRight() && imAtTheRightEdgeOfScreen()) )
return;
super.move(delta);
}
}
ShotEntity
If you think the ShipEntity is cool wait till you see the shot class. It moves in one direction mindlessly until it falls off the screen at which point it removes itself from the picture. How does it do that? On creation it sets its vertical movement speed to 300 pixels per second and in the move method it contiues until it falls off the screen.
package com.craig.groovyinvaders
public class ShotEntity extends Entity
{
def moveSpeed = -300 //Speed of shot in pixels per second
public ShotEntity(Game g, String spriteRef, def locX, def locY)
{ super (g, spriteRef, locX, locY); dy = moveSpeed; }
public void move(def delta) {
// proceed with normal move
super.move(delta);
// if we shot off the screen, remove ourselfs
if (y < -100) game.removeEntity(this);
}
}
AlienEntity
Alright, how many of you have actually played SpaceInvaders in your youth? Don’t be shy, raise your hand. (Of course I can see you or else I wouldn’t have asked the stoopid question!) I ask because aliens are a little more involved and I wouldn’t want to lose any of you. If you’ve never seen or played SpaceInvaders then you wouldn’t be familiar with how the Aliens work their way left and right and down the screen trying to land on our ship.
package com.craig.groovyinvaders
public class AlienEntity extends Entity
{
public AlienEntity(Game g, String spriteRef, def locX, def locY)
{ super (g, spriteRef, locX, locY) }
//These are closures or executable "method-like" things that return theresult of the enclosed expression
def imAtTheLeftEdgeOfScreen = {(x < 10)} //an x coordinate less than 10 means we're at the edge
def imMovingLeft = {(dx < 0)} //a negative x direction means we're moving left
def imAtTheRightEdgeOfScreen = {(x > 750)} //an x coordinate greater than 750 means we're at the edge
def imMovingRight = {(dx > 0)}//a positive x direction means we're moving right... Yaay!
public void move(long delta) {
if( (imMovingLeft() && imAtTheLeftEdgeOfScreen()) || (imMovingRight() && imAtTheRightEdgeOfScreen()) )
game.updateLogic();
super.move(delta);
}
}
At this time it would be a good idea to move some code into our Base Entity class. Why? Well because both the ship and the alien need to ask the very same stoopid question.
Entity.groovy
//These are closures or executable "method-like" things that return theresult of the enclosed expression
def imAtTheLeftEdgeOfScreen = {(x < 10)} //an x coordinate less than 10 means we're at the edge
def imMovingLeft = {(dx < 0)} //a negative x direction means we're moving left
def imAtTheRightEdgeOfScreen = {(x > 750)} //an x coordinate greater than 750 means we're at the edge
def imMovingRight = {(dx > 0)}//a positive x direction means we're moving right... Yaay!
def iveMovedOffTheLeftOrRightSideOfScreen = {
(imMovingLeft() && imAtTheLeftEdgeOfScreen()) || (imMovingRight() && imAtTheRightEdgeOfScreen())
}
public Entity(Game g, String spriteRef, def locX, def locY)
{
While the ship asks if( iveMovedOffTheLeftOrRightSideOfScreen() ) return;
the alien asks the same but answers differently. While we’re in the base class now would be a good time to point out a problem I’ve had with sub-classing in Groovy. We’ll need to change our move() method signatures from move(long) to move(def). (In Groovy the def keyword indicates an undefined type.) We are leaving the parameter passed into the move method undefined because calling super class methods with a primitive like long appears to generate runtime errors for some unknown reason. Hopefully the issue will be resolved in the next release of Groovy but for now we work around it by using def whenever we see such issues. That also means we must make sure it matches the move method as it’s defined in Entity.groovy, Ship.groovy, Alien.groovy and Shot.groovy. We need additional setter/getter methods for our movement variables in our Entity class. Our Game will later access these values to add control to our entites. Add these methods to Entity.groovy:
/**
* Set the horizontal speed of this entity
*
* @param dx The horizontal speed of this entity (pixels/sec)
*/
public void setHorizontalMovement(double dx) { this.dx = dx }
/**
* Set the vertical speed of this entity
*
* @param dx The vertical speed of this entity (pixels/sec)
*/
public void setVerticalMovement(double dy) { this.dy = dy; }
/**
* Get the horizontal speed of this entity
*
* @return The horizontal speed of this entity (pixels/sec)
*/
public double getHorizontalMovement() { return dx; }
/**
* Get the vertical speed of this entity
*
* @return The vertical speed of this entity (pixels/sec)
*/
public double getVerticalMovement() { return dy; }
Control
We want to let players control the ship so that it can line shots up with the aliens don’t we? Unless you want to give the aliens an unfair advantage your answer should be yes. Create a new class in the same package. (I know, all of our code has been in one package so far but bear with me!) Call it KeyInputHandler and make it extend java.awt.event.KeyAdapter so that it can respond to key presses. We’ll use our little KeyInputHandler to make our ship spring to life. (It’s also a cool place to put Easter Eggs! Don’t know what an Easter Egg is? How about up, up, down, down, left, right, left, right, B, A? You’ll learn in due time don’t worry!) The events we’re interested in are key presses and key types. In other words we want the ship to fly to the right when the player presses right, fly left when the player presses left and stop flying (moving) when the player releases the key. We also want a photon bean to emit from our vessel when the player hits the fire key. Ship control in our game will be controlled in the game loop by referring to a set of boolean flags. In every iteration through our game loop we will examine these booleans and tell our ship to act accordingly. The original tutorial uses an inner class for the KeyInputHandler and since Groovy doesn’t support inner classes and also since I’m too duhmb to dream up a reasonable alternative I give you the ugly hack. (Honestly, if someone can advise me better on what to do in these situations where it is common to use an inner class I would really appreciate it.)
import java.awt.event.KeyAdapter
import java.awt.event.KeyEvent
class KeyInputHandler extends KeyAdapter {
Game g
public KeyInputHandler(Game game) { g = game }
public void keyPressed(KeyEvent e) {
g.leftPressed = (e.getKeyCode() == KeyEvent.VK_LEFT)
g.rightPressed = (e.getKeyCode() == KeyEvent.VK_RIGHT)
g.firePressed = (e.getKeyCode() == KeyEvent.VK_SPACE)
}
public void keyReleased(KeyEvent e) {
g.leftPressed = (e.getKeyCode() == KeyEvent.VK_LEFT)
g.rightPressed = (e.getKeyCode() == KeyEvent.VK_RIGHT)
g.firePressed = (e.getKeyCode() == KeyEvent.VK_SPACE)
}
public void keyTyped(KeyEvent e) {
// if we hit escape, then quit the game
if (e.getKeyChar() == 27) System.exit(0);
}
}
We need to define that set of boolean flags in our Game class
//Control flags
boolean leftPressed, rightPressed, firePressed
As well as registering our KeyInputHandler with out Game in the Game contructor: addKeyListener(new KeyInputHandler(this))
That code registers the Game with the KeyInputHandler then registers the KeyInputHandler with the Game and then registers the Game back to the key handler which is then registered to the Game after we register the… you get the point right? Ugly hack! So far we’ve left the little processInput method in our Game class empty. Now it’s time to fill it. With our key handler correctly (confusingly) registered we dive into the method and start telling our ship what to do there. Plop these lines in the middle of those curly braces after the processInput method:
// resolve the movement of the ship. First assume the ship
// isn't moving. If either cursor key is pressed then
// update the movement appropraitely
ship.setHorizontalMovement(0)
if ((leftPressed) && (!rightPressed)) ship.setHorizontalMovement(-moveSpeed)
else if ((rightPressed) && (!leftPressed)) ship.setHorizontalMovement(moveSpeed)
That’s enough to get our lazy ship moving. Oh, but wait! We referenced a moveSpeed variable so we should define that in the header section of our Game class. long moveSpeed = 300 //Speed of ship movement in pixels per second.
Next thing would be to allow the player to shoot aliens. That is the sole point of the game anyhow, right? Read the following line of code and it shold read like pure English: if(firePressed) tryToFire()
That’s how you know when your design is looking good. When sections of code start to resemble grammatically correct english. Hold on! If fire pressed what? Which key do we anticipate fire will press? Well it almost makes sense if you don’t stare too hard! (Stop staring so dang hard!) Put that line after the others in processInput. Now why are we trying to fire instead of just firing? Well it takes a while for our photon beam to charge up so we need to simulate that with a call to method that dilly-dallys while aliens are marching towards us. Here’s the method that shoots the breeze between photon charges:
public void tryToFire() {
// check that we have waiting long enough to fire
if (System.currentTimeMillis() - lastFire < firingInterval) return
// if we waited long enough, create the shot entity, and record the time.
lastFire = System.currentTimeMillis()
ShotEntity shot = new ShotEntity(this,"sprites/shot.gif",ship.getX()+10,ship.getY()-30)
entities.add(shot)
}
That means we have to add more variables to our Game header to store the time the last shot was fired and define the firing interval.
/** The time at which last fired a shot */
long lastFire = 0
/** The interval between our players shot (ms) */
long firingInterval = 500
We have some more work to do in our Game class. Earlier we decided to call a method from our ShotEntity that would remove entities from the screen. We now need to define that method along with an additional list and logic required to make that happen. Rather than allowing an Entity to snatch itself from the screen abruptly we will treat the removeEntity method as a request method where requests to remove stuff can be made. We can then go about our game logic as normal and remove entities at the appropriate time. We need a removeList which we will define Groovy stile in the Game header section.
def removeList = []
We can then add the removeEntity method along with a helper method that actually performs the remove.
removeEntity(def entity) { removeList << entity }
def cleanDeadEntities() {
entities.removeAll(removeList)
removeList.clear()
}
We will then change the innards of our GameLoop as follows to perform the actual removal:
while(gameRunning)
{
def elapsedTime = calculateElapsedTime()
processInput()
display( moveSprites(elapsedTime,draw()) )
cleanDeadEntities() //Remove all dead objects from the screen.
delay()
}
Recap
Whew! It’s been a while since the last installment in this series and we’ve covered a lot of ground. I want to note a few things. First off, look at how some of the above examples are colorized while others are black and white. It took me a couple of days to write everything here and during that time WordPress introduced the ability to in-line CSS attributes. I’m too lazy and don’t have the time to go back and fix all of the examples. I make no apologies here as I believe the CSS thing should have been here all along and my Blog has been handicapped for a while as a result. Secondly I want to talk about some of the difficulties I faced. I had to change my coding style half way through this post as I noted the issue regarding subclassing above. Also, a Groovy committer pointed out that classes and methods within are public by default so there’s no need for the “public” key word. A lot of things are going on here and I plan to clean it up later. For now what I have so far gives me a very glitchy display where the ship moves and flickers. I can go on forever trying to figure out each problem but it’s time for another big break. I have real work to finish. If you’ve been keeping up I thanmk you and please feel free to comment and/or contribute fixes. I’ll try to post corrections later as well as what I hope to be the sixth and final part to my series. (Open GL refactoring will be in a different series at a different time.) For now, keep it movin’ and I’ll holla…