Before we jump straight into some coding action, let's first take a look at a couple of core classes that will make our lives easier. When you created your project with the setup tool, the core of the game, the MyGDXGame class, which is the default name of the class, extends a class called ApplicationAdapter. This in turn implements an interface called ApplicationListener. Now, you might think these are good enough for us to get going; however, there is a better class that we can extend and that is the Game class.
What is so special about this class? Essentially, it is ApplicationListener that delegates the game to a screen. Every bar method, such as onCreate(), is implemented. This will save us lots of time going forward.
The following code is the Game class from the LibGDX framework:
public abstract class Game implements ApplicationListener {
protected Screen screen;
@Override
public void dispose () {
if (screen != null) screen.hide();
}
@Override
public void pause () {
if (screen != null) screen.pause();
}
@Override
public void resume () {
if (screen != null) screen.resume();
}
@Override
public void render () {
if (screen != null) screen.render(Gdx.graphics.getDeltaTime());
}
@Override
public void resize (int width, int height) {
if (screen != null) screen.resize(width, height);
}
public void setScreen (Screen screen) {
if (this.screen != null) this.screen.hide();
this.screen = screen;
if (this.screen != null) {
this.screen.show();
this.screen.resize(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
}
}
public Screen getScreen () {
return screen;
}
}
As we can see in the preceding code, the Game class is abstract, this will require us to provide our own implementation. Then, as mentioned earlier, it delegates life cycle calls to the Screen class.
The Screen class is used to define what a player is looking at, such as the main menu or the game screen. It has various methods that may be overridden. Let's take a look at the following code snippet:
public interface Screen {
public void show ();
public void render (float delta);
public void resize (int width, int height);
public void pause ();
public void resume ();
public void hide ();
public void dispose ();
}
It has the same method signature as the ApplicationListener class. However, there is an additional method, show(), which is called when the screen becomes the current screen in the game.
We wouldn't want from implement all those methods every time we wanted to create a Screen class implementation. Luckily, LibGDX has an adapter class, ScreenAdapter, which is purely for convenience and contains zero logic. It implements the methods with empty bodies. I won't show the code structure here, I will leave it to you to look it up if you like.
Out of the life cycle methods that are mentioned here, we are only interested in a couple of these to start off with, show() and render().
The render() method is called on every cycle. By default, this is 60 times a second. This is configurable; however, we are happy with 60fps (frames per second) for now. In the default project, you will see that there is some interesting code already. What's happening here is that, with every frame, the screen is being cleared with the background color, which is red in this case, and rendering the LibGDX logo from scratch. This is achieved with the calls to glClearColor() and glClear(). Later, we will look at how we can clear with other colors.
Let's get our Game class up and running. Initially, it will not do much, but it will set us up to make our game.
First, let's generate a new project. This time, we will use some proper names as opposed to the defaults. To create the Snake game, we set the tool as follows:
Once you have the project that is generated from the LibGDX setup application, import it into your IDE; if you are using Eclipse, refer to the previous chapter.
The Game class that is generated for you extends ApplicationAdapter by default.
public class SnakeGame extends ApplicationAdapter {
Let's change the extended class to Game:
public class SnakeGame extends Game {
Ensure that the imports are updated at the top of the class:
import com.badlogic.gdx.Game;
You will now notice that, essentially, nothing has changed. If you run the project again—via DesktopLauncher—it will still show the red screen with the LibGDX logo.
But why is this happening? We haven't set a Screen class for game object to use. The keen-eyed among you will have spotted that this is because we are still overriding the render() method. Thus, the delegation to the Screen class is not happening.
Before we remove the override, let's create our Screen class. Create a class called GameScreen and make it extend by the class ScreenAdapter:
public class GameScreen extends ScreenAdapter {
As all the interface methods are implemented in the ScreenAdapter class, you will notice that the IDE does not request that you implement anything.
Let's return to our SnakeGame class and remove the default project example code, so all we are left with is the create() method's override:
public class SnakeGame extends Game {
@Override
public void create() {
}
}
In the create() method, we can set a new instance of our GameScreen method:
public class SnakeGame extends Game {
@Override
public void create() {
setScreen(new GameScreen());
}
}
If you run the project again, this time you will see that we just have a black screen; however, now the rendering is coming from the screen. Let's draw something on the screen!
Texture rendering
Now we have a blank screen, but let's make our screen do something! To start off with, let's put the default project code—the lovely LibGDX logo—onto the screen. You will notice that we don't have a create() method to put the object initialization in. We do, however, have a show() method. Let's put it in there. So now your GameScreen class should look something like this:
public class GameScreen extends ScreenAdapter {
private SpriteBatch batch;
private Texture img;
@Override
public void show() {
batch = new SpriteBatch();
img = new Texture("badlogic.jpg");
}
@Override
public void render(float delta) {
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
batch.draw(img, 0, 0);
batch.end();
}
}
If you run the project again, you will see we are back to where we started. Before we go any further, I am going to provide a little explanation to what is going on, in the code, here.
The batch class
Next we need something that will draw our textures; this is where the batch classes come in. A Batch is used to draw 2D rectangles that reference a texture. Among the different Batch class implementations is SpriteBatch, which is the one we will use for our game.
The texture class
A texture is a bitmap image that gets drawn on the screen through mapping. The texture class wraps an OpenGL texture without having to worry too much about the internal workings of OpenGL—remember we are here to make games!
When using the texture class, we should ensure that our textures are managed; by this, we mean that if the OpenGL context is lost, for example, that loss can happen by the user switching to another application, then our managed textures will automatically get reloaded for us. Excellent!
The dispose() method
Everything we have talked about so far, somewhere in the inner workings of LibGDX, holds on to real resources, such as memory. To ensure that our games are well behaved and do not eat all available system memory, we have to remember to dispose of our SpriteBatch and textures when we are done using them. You will notice that they all have a dispose() method in them. When called, this will release all resources associated with that object. The Screen class has a dispose() method. We will look at that later on.