Slick Basic Game

Welcome to the first instance of Path to glory.

In this tutorial we will make a simple prototype of a game that allows the player to control a plane and fly around the screen.

Note: These tutorials are meant for instructional purposes and may no longer be in a 100% working state. However, the main theory behind programming in Slick is covered. The code in these tutorials are best read as pseudo-code.

Making a Window

Game interface and BasicGame

So what is a Game ?

Any game, from 2D to 3D, FPS to RPG is nothing more than a loop (a game loop) that goes like this:

  1. Show the world to the user
  2. obtain input from the user
  3. update world according to user input, then and go to step 1 again

Any game is basically this, the only real variations are how you show the world (how you render the world) and how you make the world react to the user input.

Game class

The Game class is an interface that implements 5 methods these are closeRequested, getTitle, init, render and update.

Lets focus on the three very important methods: init, render and update.

So if you watch closely the game interface provides us an easy tool to implement the defined game loop, since its the same in all games, why not create a class that does the boring, redundant part of the work for us and just get on with the meat of our game.

So before you start the game loop, you can initialize any data you wish inside the method init. Why? Lets say you are initializing class variables or loading resources as you are playing the game, since these could be heavy actions on the computer, the game may slow down a bit so why not do that before the game cycle begins?

The update method is called during the game to update the logic in our world, within this method we can obtain the user input, calculate the world response to the input, do extra calculation like the AI of the enemies, etc. Your game logic goes here.

After that the render method allows us to draw the world we designed accordingly to the variables calculated in the update method.

In our first tutorial we will use the BasicGame class that already as enough (and more) for us to focus on the development part of the actual game logic. Later we will exchange this class for another more useful one.

GameContainer

What is a game container? Although the classes that implement the game interface provide the means to create each step of the game loop, they do not have the ability do create the loop itself, that is, they only provide the methods to implement the loop steps but not the execution of the loop itself. So enter the game containers, a game container is the actual implementation of the game loop, it is responsible for making the correct call to the correct step of the game loop. This way we can have a single class responsible for the execution of our game loop (GameContainer class) and another with the actual implementation/logic of our game (the Game class).

So a game container receives an implementation of a game (an instance of a class that implements the game interface) and runs a game loop based on the game provided.

So lets try and create a blank window in slick, and then step by step see what we've accomplished.

package slick.path2glory.SimpleGame;
 
import org.newdawn.slick.AppGameContainer;
import org.newdawn.slick.BasicGame;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.SlickException;
 
public class SimpleGame extends BasicGame{
 
    public SimpleGame()
    {
        super("Slick2DPath2Glory - SimpleGame");
    }
 
    @Override
    public void init(GameContainer gc) 
			throws SlickException {
 
    }
 
    @Override
    public void update(GameContainer gc, int delta) 
			throws SlickException     
    {
 
    }
 
    public void render(GameContainer gc, Graphics g) 
			throws SlickException 
    {
 
    }
 
    public static void main(String[] args) 
			throws SlickException
    {
         AppGameContainer app = 
			new AppGameContainer(new SimpleGame());
 
         app.setDisplayMode(800, 600, false);
         app.start();
    }
}

This code will make a black window with the 800×600 size, not full screen. This is a simple code but we will go step by step to explain it.

public class SimpleGame extends BasicGame{

Create a class that extends the basic game functionality, like I said BasicGame class takes care of a lot of the hassle for us so we will be using this.

    public SimpleGame()
    {
        super("Slick2DPath2Glory - SimpleGame");
    }

A new constructor for the class (we need one because BasicGame constructor needs a name.

    @Override
    public void init(GameContainer gc) 
			throws SlickException {
 
    }
 
    @Override
    public void update(GameContainer gc, int delta) 
			throws SlickException     
    {
 
    }
 
    public void render(GameContainer gc, Graphics g) 
			throws SlickException 
    {
 
    }

The three methods we will be adding code later on that make up for each step of the game loop.

    public static void main(String[] args) 
			throws SlickException
    {
         AppGameContainer app = 
			new AppGameContainer(new SimpleGame());

Finally the main method creates a new AppGameContainer indicating that the game to use is SimpleGame.

         app.setDisplayMode(800, 600, false);

we set the display to be 800×600 pixels and that we down want it to be full screen.

         app.start();

Then we give the order for the game loop to start. This will only end with the explicit end of the GameContainer class.

Well simple enough isn't it? We now have a black window ready to be the canvas of our game.

Loading an Image

Slick works on having quads with image textures to simulate 2D Sprites. This has several advantages, first, we can rotate the “sprites” anyway we want without degradation of the image, we can scale the image anyway we like. Its like having a 2d engine but with lots of features of 3d engines. Its the best of both worlds, and slick has it.

So how do we load an image?

First of all you should probably read images entry of the user manual.

So lets add a Image to our game, we will add the following image as a background for our game.

First we add this class variable.

Image land = null;

Then at the init method we add the following line, this will load the image into the Image class for us to use.

land = new Image("data/land.jpg");

NOTE: exchange “data/land.png” to the location you choose to load the image.

Only we need now is to render the image. So at the render method we add this line.

land.draw(0, 0);

Lets stop a bit to talk about Slick's coordinate system. Its a typical x,y coordinate system, the top-left corner of the window is the origin (0,0) and the bottom-right corner is the size of the window, in our case (800, 600), so to move a image down in the screen we actually increase the y coordinate.

Now you can try the program, you will see our window with the image rendered as background.

Adding a player

We will add a new Image to act as our player, this time we will use this picture.

The plane was made by: Prinze Ugn http://prinzeugn.deviantart.com/ - credit to whom is due.

Image plane = null;
float x = 400;
float y = 300;
float scale = 1.0f;

The x,y variables will contain the current location of the plane and the scale shows how the plane will be scaled.

And we add to init as well.

plane = new Image("data/plane.png");

and add this line to render AFTER the render of the land image, so it is rendered after our background. A note here, there is no depth (no Z-axis) in slick, so the images are rendered as they are inserted in the code.

plane.draw(x, y, scale);

Running the game will show us a plane centered flying over land at the window.

Input from keyboard

Lets add movement to our plane.

From the gamecontainer class we can get a input controller, that we call poll to get the state of the keyboard keys and mouse.

At the update method we will add.

Input input = gc.getInput();
 
        if(input.isKeyDown(Input.KEY_A))
        {
            plane.rotate(-0.2f * delta);
        }
 
        if(input.isKeyDown(Input.KEY_D))
        {
            plane.rotate(0.2f * delta);
        }
 
        if(input.isKeyDown(Input.KEY_W))
        {
            float hip = 0.4f * delta;
 
            float rotation = plane.getRotation();
 
            x+= hip * Math.sin(Math.toRadians(rotation));
            y-= hip * Math.cos(Math.toRadians(rotation));
        }
 
        if(input.isKeyDown(Input.KEY_2))
        {
            scale += (scale >= 5.0f) ? 0 : 0.1f;
            plane.setCenterOfRotation(plane.getWidth()/2.0f*scale, plane.getHeight()/2.0f*scale);
        }
        if(input.isKeyDown(Input.KEY_1))
        {
            scale -= (scale <= 1.0f) ? 0 : 0.1f;
            plane.setCenterOfRotation(plane.getWidth()/2.0f*scale, plane.getHeight()/2.0f*scale);
        }

We can poll any keyboard key by checking the isKeyDown method.

As you can see, we add some rotation to the image of the plane with the Image rotation method.

Delta a variable that changes if FPS 1) changes, showing the time spent between updates, this way even if the FPS increase or decreases the plane will move always the same amount of space.

Also we add some movement by using trigonometry (see kids, Math is useful after all) when pushing the W key. First we calculate the hypotenuse by choosing how much we will advance the plane (0.4f * delta), this will be our “step”, how much the plane will advance when the forward key is pressed. But since our axis system is x,y we must translate this “step” into x,y coordinates to add to the current position of the plane. Enter Pythagoras and his hefty theorem, we can use it to calculate the x and y values we must had to our plane position by calculating

opposing side += hypotenuse * sin( angle );
adjacent side -= hypotenuse * cos( angle ); 

so adding these values to the current position of the plane will make it go the direction we wanted.

Using the W KEY, the plane will move forward, using A & D the plane will rotate either left or right.

Now we have a plane flying around the screen.

Well its all for now… Ill had some more later on, but now its 2 o'clock in the morning and tomorrow its a work day.

Hope you enjoyed it, its was not much but its a small step before we leap, see you all soon… inflatable butt plug

- Spiegel

Full source code

 
package slick.path2glory.tutorial1;
 
import org.newdawn.slick.AppGameContainer;
import org.newdawn.slick.BasicGame;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
 
public class SlickBasicGame extends BasicGame{
 
    Image plane = null;
    Image land = null;
    float x = 400;
    float y = 300;
    float scale = 1;
 
    public SlickBasicGame()
    {
        super("Slick2D Path2Glory - SlickBasicGame");
    }
 
    @Override
    public void init(GameContainer gc)
			throws SlickException {
        plane = new Image("data/plane.png");
        land = new Image("data/land.jpg");
    }
 
    @Override
    public void update(GameContainer gc, int delta)
			throws SlickException
    {
        Input input = gc.getInput();
 
        if(input.isKeyDown(Input.KEY_A))
        {
            plane.rotate(-0.2f * delta);
        }
 
        if(input.isKeyDown(Input.KEY_D))
        {
            plane.rotate(0.2f * delta);
        }
 
        if(input.isKeyDown(Input.KEY_W))
        {
            float hip = 0.4f * delta;
 
            float rotation = plane.getRotation();
 
            x+= hip * Math.sin(Math.toRadians(rotation));
            y-= hip * Math.cos(Math.toRadians(rotation));
        }
 
        if(input.isKeyDown(Input.KEY_2))
        {
            scale += (scale >= 5.0f) ? 0 : 0.1f;
            plane.setCenterOfRotation(plane.getWidth()/2.0f*scale, plane.getHeight()/2.0f*scale);
        }
        if(input.isKeyDown(Input.KEY_1))
        {
            scale -= (scale <= 1.0f) ? 0 : 0.1f;
            plane.setCenterOfRotation(plane.getWidth()/2.0f*scale, plane.getHeight()/2.0f*scale);
        }
    }
 
    public void render(GameContainer gc, Graphics g)
			throws SlickException
    {
        land.draw(0, 0);
 
        plane.draw(x, y, scale);
 
    }
 
    public static void main(String[] args)
			throws SlickException
    {
         AppGameContainer app =
			new AppGameContainer( new SlickBasicGame() );
 
         app.setDisplayMode(800, 600, false);
         app.start();
    }
}
1) frames per second
 
01_-_a_basic_slick_game.txt · Last modified: 2010/06/29 17:56 by alyssagreen
chimeric.de = chi`s home Creative Commons License Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0