Saturday, December 11, 2010

New release with OpenGL rendering

I'm happy to say that JA2DAPI now has OpenGL as a back end alternative to Java2D. Why is this so exciting? OpenGL is a graphics library (written in C) that allows direct access to graphics hardware via it's infamous rendering pipeline (see the wonderful diagram at www.opengl.org/documentation/specs/version1.1/state.pdf). This means it is very low-level and often times pretty cumbersome. It's not object-oriented and is based on a state machine model. In other words, it's very powerful and efficient, yet not at all user friendly for the recreational game developer.

So, for the latest release, I've added OpenGL rendering, but I've put it completely under the hood so JA2DAPI users don't have to worry about making gl calls. So, you get the awesomeness that OpenGL can provide for 2D developers without all the headache.

Okay, now to the fun stuff. I've added a package called twodapi.lwjgl. This contains a set of classes that should look familiar to you if you know JA2DAPI. There's OpenGL versions of all the base classes as well as the GeneralScroller class (here called ScrollerGL). The class that kicks everything off is the GLPainter. The easiest way to get started is to extend this class:

import twodapi.lwjgl.*;

public class GLStarter extends GLPainter
{
    public GLStarter()
    {

        //super constructor params are window width and height
        super(1024,768);
    }
  
    public void gameInit()
    {
        //Add game initialization code
    }
  
    public void gameUpdate(float interpolation)
    {
        //Called every frame. Add update code.
    }
}


Here, we see the bare bones of a GLPainter extension. In the constructor the super constructor must be called with the window width and height as parameters. The method we have to override to do anything is gameInit(). This is where all game initialization code should go, including initialization of variables and instantiation of classes. The other overridden method is gameUpdate(). This is called every frame update and the float parameter interpolation is the number of seconds since the last frame update. This is where game objects can update their state. An alternative, if implementing SpriteGL or FlipBookSpriteGL classes is to override the update() method to update game state in the same fashion.

So, how is the frame rate determined? LWJGL, the Java OpenGL bindings I've used in JA2API has a VSync feature that syncs renders with the monitor's refresh rate, if VSync is supported by the system's graphics card. This provides the smoothest animation possible and does away with the rendering artifacts you often get with Java2D (such as tearing). If VSync is not support by your graphics card, frame updates occur as often as possible (call the GLPainter's getFPS() method to get the frame rate at a given point). 
 
Here's a simple implementation that displays a background and a spaceship that can be rotated with the left and right arrow keys and moved forward with the up arrow key:

import org.lwjgl.input.Keyboard;
import twodapi.lwjgl.*;

public class GLStarter extends GLPainter
{
    private SpriteGL ship;
    private KeyControlsGL keyControls;

    public GLStarter()
    {
        super(1024,768);
    }

    public void gameInit()
    {
        keyControls = createKeyControls();
        keyControls.set(Keyboard.KEY_LEFT);
        keyControls.set(Keyboard.KEY_RIGHT);
        keyControls.set(Keyboard.KEY_UP);

        setBackground("/images/purple-galaxy-stars.jpg");

        ship = new SpriteGL("/images/ship.gif");
        ship.setLocation(500, 330);
        ship.setScreenWrap(true, this);
        addToPaintList(ship);  
    }

    public void gameUpdate(float interpolation)
    {
        if (keyControls.isPressed(Keyboard.KEY_LEFT)) ship.rotate(-500*interpolation);
        else if (keyControls.isPressed(Keyboard.KEY_RIGHT)) ship.rotate(500*interpolation);
        if (keyControls.isPressed(Keyboard.KEY_UP)) 

            ship.moveRadial(ship.getRotation()-90, 300*interpolation);
    }

    public static void main(String[] args)
    {
        new GLStarter();
    }
}


One thing you may notice is the Keyboard class constants being used for the key controls. These are the LWJGL's keyboard constants and they're pretty straightforward. They're in the form  

Keyboard.KEY_*

where * is the key name.

The second thing you may have noticed is that angles are in degrees here. This is an OpenGL thing and I was too lazy to convert to radians.

The other thing you may have noticed is the multiplication of the movement and rotation amounts by interpolation. This is good practice with OpenGL because the frame rate can vary, especially if your graphics card doesn't support VSync. Think about the amount you multiply by interpolation as the amount you want the sprite to move, rotate, etc in 1 second.

Thanks for reading. I still haven't thoroughly documented the OpenGL features, but the classes and methods are extremely similar to those in the twodapi.core class, so I would recommend looking at the javadocs for the core package if you need help. If they're of no help, feel free to email me at bgsimpkins@gmail.com.

Sunday, October 10, 2010

More animation

The previous entry demonstrated an easy way to animate a sprite in JA2DAPI by extending a Sprite2D class and overriding the updateState() method. This approach is a great way to bind animation to a sprite: by encapsulating the sprite behavior in a single class. In  some cases, however, a more global approach might be more useful. For these cases, we can have our primary class implement the Paintable interface and override it's methods update() and paintObject(). For this example, we only need to add code to the update() override. If you read the previous blog entry, this code should seem a bit familiar:

import java.awt.Graphics;
import twodapi.core.*;

public class AnimationTest implements Paintable
{
    private Sprite2D ship = null;

    public AnimationTest()
    {
        Frame2D gameFrame = new Frame2D(Frame2D.REG_PAINTER,1024,768,false);
        GamePainter painter = (GamePainter)gameFrame.getPainter();

        ship = new Sprite2D("/images/ship.gif");
        ship.setLocation(500,400);
        painter.addToPaintList(ship);

        painter.addToPaintList(this);
    }

    public void update(float interpolation)
    {
        ship.rotate(Math.PI/16);
    }
    public void paintObject(Graphics g){}

    public static void main(String[] args)
    {
        new AnimationTest();
    }

}

So, here, instead of doing everything in main(), most of the action is in the constructor. We create a frame and a ship sprite and move the ship to the center. Now, since the AnimationTest class has implemented Paintable, we can add it (this) to the paint list. As a result, we can do game state updates in this class's update() method, as long as the ship was declared as a class member like above.

The final way to do animation is using anonymous class method overrides. If you're a Java veteran, I'm sure you can pull this off without an example. If you're not, there's probably no need to worry about it. Anonymous class code is not often the cleanest, anyways, and you have two simpler methods described above.

Animation With Keyboard Input

Okay, so this blog is boring me, so I'll add a little more beyond a simple animation example: I'll show you how to rotate the ship with keyboard input.

Using JA2DAPI, getting keyboard input is simple. We just need to create a KeyControls instance and register the desired input keys using set():

        KeyControls keyControls = new KeyControls(painter.getComponent());
        keyControls.set(KeyEvent.VK_LEFT);
        keyControls.set(KeyEvent.VK_RIGHT);


So, here we made a KeyControls and told it we want to listen for left and right arrow key presses (Notice that KeyControls uses the java.awt.event.KeyEvent constants. I'm working on moving away from that in the future so that JA2API can have its own constants). Then, we just need to check for these keys being pressed in the update() override:

    public void update(float interpolation)
    {
        if (keyControls.isPressed(KeyEvent.VK_LEFT)) ship.rotate(-Math.PI/16);
        else if (keyControls.isPressed(KeyEvent.VK_RIGHT)) ship.rotate(Math.PI/16);
    }


This code makes it so holding down the left arrow key will rotate the ship counter-clockwise and holding down the right arrow will rotate it clockwise. Note that isPressed() is not exhaustive. So, if a key is held down, isPressed() will continuously be true on all update() calls until the key is no longer pressed. isTapped() is an exhaustive alternative that is only called once per key press. This method is more appropriate for things such as firing a gun or making a character jump.

Here's the whole .java source for this example:

import java.awt.Graphics;
import java.awt.event.KeyEvent;
import twodapi.core.*;

public class AnimationTest implements Paintable
{
    private Sprite2D ship = null;

    private KeyControls keyControls = null;

    public AnimationTest()
    {
        Frame2D gameFrame = new Frame2D(Frame2D.REG_PAINTER,1024,768,false);
        GamePainter painter = (GamePainter)gameFrame.getPainter();
        painter.setFrameRate(30);
    
        keyControls = new KeyControls(painter.getComponent());
        keyControls.set(KeyEvent.VK_LEFT);
        keyControls.set(KeyEvent.VK_RIGHT);

        ship = new Sprite2D("/images/ship.gif");
        ship.setLocation(500,400);
        painter.addToPaintList(ship);

        painter.addToPaintList(this);

    }

    public void update(float interpolation)
    {
        if (keyControls.isPressed(KeyEvent.VK_LEFT)) ship.rotate(-Math.PI/16);
        else if (keyControls.isPressed(KeyEvent.VK_RIGHT)) ship.rotate(Math.PI/16);
    }
    public void paintObject(Graphics g){}

    public static void main(String[] args)
    {
        new AnimationTest();
    }

}

Let me know what you think. Talk to you soon!

Thursday, October 7, 2010

Get Started!

Intro

Hello, first of all. This is the first official JA2DAPI blog entry. We'll go ahead and jump into some basic things about the API...

Okay, so to start things off, there's a kick-off class called Frame2D that is a simple way to create a window (or full screen display), as well as an animation and render environment.  To create the Frame2D, you specify the size of the window, whether it is to be full screen or not, and the type of Painter you want. The Painter is what handles the rendering and animation of things. There's currently two Painters that are available: The GamePainter is designed for applications that have a lot of continuous animation (most games) and EventDrivenPainter is designed for applications where updates (and render calls) are driven by specific, typically user input-driven, events.

Here's a way to create a Frame2D that is a 1024X768 window with a GamePainter:

import twodapi.core.*;
import java.awt.Graphics;

public class StarterTest
{
    public static void main(String[] args)
    {
        Frame2D gameFrame = new Frame2D (Frame2D.REG_PAINTER, 1024, 768, false);
        GamePainter painter = (GamePainter)gameFrame.getPainter();
    }
}
 

Notice that we immediately have a GamePainter ready to go. The GamePainter is what we use to add objects to the window. To demonstrate, we add this code to main():

        Sprite2D ship= new Sprite2D("/images/ship.gif");
        painter.addToPaintList(ship);
        ship.setLocation(500, 400);

Here we created a Sprite2D object based on the "ship.gif" image and added it to the GamePainter's paint list. Then, we moved the ship sprite to be closer to the center of the window.

Animation

So the animation framework I've adopted is a typical "update and render" loop. Under the hood, there's an AnimationThread running hard at work to maintain the frame rate you have specified. FYI, all sprite objects (Sprite2D, FlipbookSprite, and MouseSprite) automatically receive updates from the Painter after they are added to the paint list. We can access these updates by extending a sprite class and override updateState(float interpolation). See below:

class ShipSprite extends Sprite2D
{
    public ShipSprite()
    {
        super("/images/ship.gif");
    }

    public void updateState(float interpolation)
    {
        rotate(Math.PI/16);
    }
}


In the constructor (public ShipSprite()), we are merely calling the Sprite2D constructor and passing in the filename for the ship image as we did above. What's new is the override of the method updateState(). This method is being called by the GamePainter every frame update (50 frame per second, by default), and here we are rotating the ship Pi/16 radians per update.


Well, that's it for now! There are a couple of other ways to animate in JA2DAPI, and I will explore those in the next blog. Stay tuned!