Groovy Invaders IV


Back into the swing of things with Groovy invaders. I realize it’s been close to a week since the last installment and I apologize for leaving you all hanging (all 20 of my devoted readers). Let’s see, where did we leave off last time? If you’re following along you should have a Game.groovy file that looks something like this.


package com.craig.groovyinvaders

import java.awt.Canvas

public class Game extends Canvas
{
    def gameRunning = true
    def strategy
    def lastLoopTime = System.currentTimeMillis()
    def entities = []
    def GAME_DELAY = 10

    public Game()
    {
        def container = new groovy.swing.SwingBuilder().frame(title:”Groovy Space Invaders”,
        defaultCloseOperation:javax.swing.WindowConstants.EXIT_ON_CLOSE)
        def panel = container.getContentPane()
        panel.setPreferredSize new Dimension(800, 600)
        panel.setLayout null
        setBounds 0, 0, 800, 600
        panel.add(this)

        setIgnoreRepaint true
        container.pack()
        container.setResizable false
        container.setVisible true

        createBufferStrategy 2
        strategy = getBufferStrategy()
   }

   private void initEntities()
   {
    //we're going to put some interesting stuff here
   }

   public Game gameLoop()
   {
       while(gameRunning)
       {
           elapsedTime = calculateElapsedTime()
           processInput()
           display( moveSprites(elapsedTime,draw()) )
           delay()
       }

       return this
   }

   def calculateElapsedTime()
   {
       try{ return System.currentTimeMillis() - lastLoopTime }
       finally { lastLoopTime = System.currentTimeMillis() }
   }

   def delay()
   {
       try { Thread.sleep GAME_DELAY }
       catch(Exception e) { println e.getMessage() }
   }

   def draw()
   {
       def g = strategy.getDrawGraphics()
       g.setColor Color.black
       g.fillRect 0, 0, 800, 600
       return g
   }

   def processInput() {}

   def moveSprites(def elapsedTime, def g)
   {
       entities.each { it.move(elapsedTime) }
       entities.each { it.draw(g) }
       return g
   }

   def display(def graphics)
   {
       graphics.dispose()
       strategy.show()
   }

   public static void main(String[] args)
   {
       def g = new Game()
       g.gameLoop()
   }
}


You should also have:

I want to talk about the last couple of lines in our loadSpriteImage() method in the SpriteStore.groovy file.


      // create an accelerated image of the right size to store our sprite in
      GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration()
      Image image = gc.createCompatibleImage(sourceImage.getWidth(),sourceImage.getHeight(),Transparency.BITMASK)

      // draw our source image into the accelerated image
      image.getGraphics().drawImage(sourceImage,0,0,null)


In the Javadocs it says something like…

The GraphicsConfiguration class describes the characteristics of a graphics destination such as a printer or monitor. There can be many GraphicsConfiguration objects associated with a single graphics device, representing different drawing modes or capabilities. The corresponding native structure will… zzzzzz…

In short, the GraphicsConfiguration class is an abstraction over what the “thing you’re drawing on” is capable of. Note how that chained method call is retrieving the default screen device. (Did you note that? I didn’t until I went back for the second or third time and stared at it really hard.) To me that means that we’re configuring or “talking to” the default screen device or “graphics card”. (If you are a know-it-all egg head feel free to jump in right now and correct me. Oh, you can’t jump in right now because this is not a two way conversation, rather a one way dialog so sit down and shut up!) So that means the second line there is asking the “default screen device” or video card to create an image compatible with the dimensions of the graphic we loaded from disk. It doesn’t create an image even though we asked it to. Instead it plays a naughty trick on us and creates a space for an image to live. (Bad graphics card! Shame on you!) The next line says that we lay our disk-loaded graphic right into the image area created by the graphics card. Moving along…

What’s next?
We described the Entity as the “Sprite” logic or a “thing” that represents an animated onscreen graphic like the player’s ship or an enemy alien in our game. We also aluded to the notion of a “Sprite” which would be the visual representation of the Entity. So it will be the Sprite’s job to represent the entity visually which, in my mind, means that the Sprite will hold and draw the graphic for the entity as it should appear on screen.


package com.craig.groovyinvaders

import java.awt.Graphics
import java.awt.Image

public class Sprite {
   /** The image to be drawn for this sprite */
   private Image image

   /**
    * Create a new sprite based on an image
    *
    * @param image The image that is this sprite
    */
   public Sprite(Image image) {
      this.image = image
   }

   /**
    * Get the width of the drawn sprite
    *
    * @return The width in pixels of this sprite
    */
   public int getWidth() {
      return image.getWidth(null)
   }

   /**
    * Get the height of the drawn sprite
    *
    * @return The height in pixels of this sprite
    */
   public int getHeight() {
      return image.getHeight(null)
   }

   /**
    * Draw the sprite onto the graphics context provided
    *
    * @param g The graphics context on which to draw the sprite
    * @param x The x location at which to draw the sprite
    * @param y The y location at which to draw the sprite
    */
   public void draw(Graphics g, int x, int y) {
      g.drawImage(image,x,y,null)
   }
}


Now let’s describe a few specific entities. Our game will have a ship that will be user controlled, aliens that will dance right to left and left to right trying to eat our ship, and a shot that will eject from the ship and attempt to explode an alien. For now you can subclass Entity for each one of these ideas. We’ll add more code as we go along to make these entities behave differently. Let’s have some fun here. We’ll use a little groovy magic to generate our subclasses.


def write = { srcloc, which ->
   println "File ${srcloc}"; println """
package com.craig.groovyinvaders

public class ${which}Entity extends Entity
{
    public ${which}Entity(Game g, String spriteRef, def locX, def locY) 
    { super (g, spriteRef, locX, locY) }
}
"""
}
["Ship", "Alien", "Shot"].each { write("/path/to/proj/src/pkg/" + it + ".groovy", it) }


Running the script gives me:


File /path/to/proj/src/pkg/Ship.groovy

package com.craig.groovyinvaders

public class ShipEntity extends Entity
{
    public ShipEntity(Game g, String spriteRef, def locX, def locY) 
    { super (g, spriteRef, locX, locY) }
}

File /path/to/proj/src/pkg/Alien.groovy

package com.craig.groovyinvaders

public class AlienEntity extends Entity
{
    public AlienEntity(Game g, String spriteRef, def locX, def locY) 
    { super (g, spriteRef, locX, locY) }
}

File /path/to/proj/src/pkg/Shot.groovy

package com.craig.groovyinvaders

public class ShotEntity extends Entity
{
    public ShotEntity(Game g, String spriteRef, def locX, def locY) 
    { super (g, spriteRef, locX, locY) }
}


You can copy these class definitions into corresponding files. Extra points goes to the one who can modify the write method in my script to make it write the source code to the file system in the appropriate place. Initializing our game will require that all of our entities be created and stored in instance variables so they can be managed in other methods in our Game object. We use our entities list declared at the top of Game.groovy to hold all of our entities. We want to treat the ShipEntity special as it also gets its own individual instance variable. We’ll want to provide user control over the ship so we don’t want to lose it in the list of other entities. Put a call to initEntities() method at the end of the Game constructor, define a ship instance variable and an alien counter before filling out the initEntities() method definition. In initEntities() we instantiate a ShipEntity and instantiate 60 AlienEntity objects laying the ship at the bottom center of the screen and arranging the aliens in 5 rows at the top of the screen.


    private void initEntities()
    {
        // create the player ship and place it roughly in the center of the screen
        ship = new ShipEntity(this,"sprites/ship.gif",370,550)
        entities.add(ship)

        def game = this
        // create a block of aliens (5 rows, by 12 aliens, spaced evenly)
        5.times { row ->
            12.times { col ->
                Entity alien = new AlienEntity(game,
                                                 "sprites/alien.gif",
                                                 100+(col*50),
                                                 (50)+row*30)
                entities.add(alien)
                alienCount++
            }
        }
    }


Now here comes the hard part. I have references to gif files and they need to be available at runtime. You’ll need put these gif files into your project first.

shipalienshot

Then you’ll need to figure out how to make them available during runtime. How you make it work will depend on your development environment. For the purpose of the tutorial (and for the purpose of showing readers how I can mix a bunch of technologies in one tutorial and explain them all at once) I’m using Maven2 to build groovy-invaders. Because Groovy is a pseudo-interpreted language (it interprets and compiles source as it runs) you don’t need a build step. If you are using an IDE and you’re savvy enough with the Groovy plugins you may be able to run the project without compiling. (If so please tell me how to.) In general, you want to make the graphics available in the classpath under a folder called sprites so that the above code will work. I’ve updated part I in my series so you can referr there to see how my folders and files are laid out. If you follow suit then you should be able to create a sprites folder under the resources folder and drop the graphics in there. After copying the graphics into your project you should be able to run “mvn test” one the command line and then you’ll have an output folder full of everything you need to see our first screen. Run the Game class with the “java” command including the groovy-all-jsr-xx.jar in the classpath and you should see a window with the ship at the bottom and 60 aliens in formation at the top. (I lean on my IDE here and create a run configuration that includes the project classpath and the Game class as the main entry point.) That’ll do it for today. I gotta get some real work done. Take a moment to peruse the code and improve if you will. Until next time you can use the little box below to drop it like it’s hot…

(The it you have been asked to drop may or may not be scalding in temperature. It referrs to comments in the natural flow of blogs and visitors comments are requested from the author and provided by the reader. Thus, the authors request to “drop it like it’s hot” is intended to encourage that feedback is “dropped” into the comment box almost as if said feedback were elevated in temperature, though it need not be.)

Previous part Next Part

3 thoughts on “Groovy Invaders IV

  1. [egg-head-mode]
    The compatibility referred to isn’t with the size of the image. Create compatible image creates an image with a format compatible with the device your rendering to – which allows it to be accelerated on the card.

    [double-egg-head-mode]
    Also, I don’t think the createCompatibleImage is actually responsible for reserving the memory on the graphics card. It just makes an image that is able to be stored on the graphics card due to it’s format. Java 2D takes the decision to make it accelerated (by putting it in graphics memory) once it’s been drawn a few times (at least thats how it’s been explained a few times on http://www.javagaming.org).
    [/double-egg-head-mode]

    [/egg-head-mode]

    When I wrote the original tutorial 1.4 was the version of choice. In 1.5 I believe this sort of malarky isn’t needed. In 1.5, Java 2D is meant to make buffered images in the right format. So you can probably just use the images originally loaded rather than copying them around. The method above will still work though.

    Kev

  2. Kev!

    What’s really good? First off, thanx for the wonderful tutorial that I’m copying from! Now regarding your remarks:

    [dummy-mode]
    The compatibility I was referring to is the compatibility between two people on their first date. I was merely using images and Java as a visual example.
    [double-dummy-mode]
    The “reserving memory” thing was an analogy referring to how a man reserve a table on his first date. Surely you missed the analogy.
    [/double-dummy-mode]
    [/dummy-mode]

    The method still appears to work but when I get a moment I want to understand it better. Thanx for the feedback. I can’t wait until I’m as smart as you so I can write my own Open GL Asteroids complete with soundtracks and cut-scenes. Keep the tips coming…

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s