Architecture

In libgdx, the loading of the art and the game loop are all controlled in a single file.  For our project [source code], this is the ‘TileMap.java’ class located in com.cmein.tilemap.  This class is the top level loader and renderer; every other class in some way connects back to it.  Notice that tilemap extends the abstract class ApplicationListener.  It is this feature of libgdx that abstracts away the differences between the desktop application and the Android application.

If you look inside the TileMapDesktop class and the TileMapAndroid class (located in tilemap-0-android), you’ll notice that these lines are similar:

TileMapAndroid:


initialize(new TileMap(), false);

TileMapDesktop:


new JoglApplication(new TileMap(), "TEST", 480, 320, false);

Both create a new TileMap instance, and because of the ApplicationListener, the application will run on the desktop and on Android with only the few lines of code that are in those two classes!

Now, I will go into more detail about the TileMap class, and how it brings the rest of the project together.  Take a look at the ‘create()’ method in TileMap:


public void create() {
Art.load();
Art.loadButtons();
Art.loadBullets();
Gdx.input.setInputProcessor(input);
setScreen(new MenuScreen());
}

This method is called only when the rendering surface is being created (for example, at application start-up or resume).  Lines 16-18 are simply calling different load methods from the Art class (also located in com.cmein.tilemap).  If you look in the Art class, you’ll notice that the load method is creating numerous TextureRegions out of png images.  These png images are stored in the “data” folder of tilemap-0.  It is hugely advantageous to do all of the loading before the game starts up; it keeps the project more organized, and you won’t have to sacrifice efficiency loading images/animations while the game is running.

The next line (line 19) sets up the input processor, which handles both key and touch inputs.  Inputs are processed every frame, before the rendering begins.

Finally, line 20 introduces a new concept: the screen.  If you look inside com.cmein.tilemap.Screen, you will find five classes, one of them called “Screen.java”.  This is an abstract class which is extended by the other screen classes.  Screen.java contains many methods which apply to screens, including various “draw” methods necessary for rendering purposes.  It also contains one abstract method, called “render()”, which will be different for every screen.  When we call setScreen on the MenuScreen, we are telling the application that the MenuScreen is the screen that needs to be rendered right now.

If you look inside MenuScreen, you’ll find the following render method:

public void render() {
font = new BitmapFont();
font.setColor(Color.BLUE);

// Menu button touch-down animation
spriteBatch.begin();
draw(Art.menuBackground, 0, 0);
if (!touched) {
draw(Art.startGameButtonUnpressed, 80, 100);
} else {
draw(Art.startGameButtonPressed, 80, 100);
}
spriteBatch.end();
//spriteBatch.dispose();

// Take user input coordinates and FPS
extra.begin();
font.drawMultiLine(extra, "touch: " + Gdx.input.getX() + ", " + Gdx.input.getY(), 10, 100);
font.draw(extra, "FPS: " + Gdx.graphics.getFramesPerSecond(), 20, 20);
extra.end();
extra.dispose();
}

This is a simple example of the functionality of the render() method.  It is important conceptually to understand that for the screen that is currently set, the respective render() method is being called continuously.  Every frame, the screen is being re-rendered.  So, during each frame, Screen.draw() is called on the background image (line 20).  Note that Art.menuBackground is a TextureRegion that was already created at start-up by the Art.load() method.  Next, a button is drawn over top of the background.  However, the button is affected by user input, which I will now delve into a little more deeply.

Present in MenuScreen is another method, called tick(Input input):

public void tick (Input input) {
if (Gdx.input.isTouched()) {
if (Gdx.input.getX() >= SCREEN_WIDTH/6 && Gdx.input.getX() <= SCREEN_WIDTH/6 + 2*SCREEN_WIDTH/3 &&
Gdx.input.getY() >= 5*SCREEN_HEIGHT/16 && Gdx.input.getY() <= 5*SCREEN_HEIGHT/16 + SCREEN_HEIGHT/8) {
touched = true;
input.releaseAllKeys();
}
else {
touched = false;
}
}
else if (touched == true && !Gdx.input.isTouched()) {
touched = false;
setScreen(new LevelSelectScreen());
}
else{
touched = false;
}
}

Like the render() method, the tick(input) method is called continuously; it takes as input any key or touch events that happened in the previous frame.  The only input that is being checked for in this method is whether or not the “Start Game” button has been pressed.  If it has been pressed, we move on to the level select screen.

Now that we have analyzed the “render” and “tick” methods of a specific screen, we can move on to the render method in TileMap.java.  Understanding this method relies heavily on understanding the Screen.tick() and Screen.render() methods I just explained.


public void render() {
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
accum += Gdx.graphics.getDeltaTime();
while (accum > 1.0f / 60.0f) {
screen.tick(input);
input.tick();
accum -= 1.0f / 60.0f;
}
screen.render();
screen.remove();
}

This method is the core of the whole application.  It is called continuously while the application is open.  The while loop ensures that the input is processed before the screen is rendered (lines  38-39) .  This is essential, since input can potentially change the rendering of the screen (for example, pressing a button can cause a new image to appear).  Suppose that our current screen is the MenuScreen.  Line 38 will then call the MenuScreen.tick() method that I explained above, and make any appropriate changes caused by user input.  Line 39 will call the tick method in the Input class (located in com.cmein.tilemap), which updates the input.  Note that input.tick() is not affected by the current screen; rather, it records any input that happen in the frame.  It contains necessary methods that can be called from any individual screen in order to analyze the input.  Once all the input has been dealt with, the screen is rendered (line 42).  If the current screen is MenuScreen, the same MenuScreen.render() method I described above is called, drawing the background and the “Start Game” button.

No matter which screen is currently set, input and rendering are handled the same way.  Obviously, the render() and tick() methods are much simpler for the menu screen than they are for the game screen (TileScreen.java).  The game screen draws various objects, including a tiled map, enemies, towers, and bullets, all of which need to be updated on a frame-to-frame basis; however, the basic concept of updating the screen then rendering it remains the same.

Now that we have covered the basic architecture of the project, we can move on to tiled maps.  See the next post.

Advertisements
  1. Shouldn’t input events get recorded *before* any screen input processing? I would exchange lines 3839 to make it so..

  1. No trackbacks yet.

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

%d bloggers like this: