LoggerBill - A tour of the tree (Part 1 - overview)

Category: Articles Published: Sunday, 19 October 2014 Written by Troy Peterson

James and myself have lots of ideas for games that we would like to make a reality, and we had already decided when we were writing The Shaft that for future games a cross-platform library would be idea. We decided to write LoggerBill as a test-bed for future ideas because the concept was easy and we figured we could do a good job of it in a short period of time. It took us about two months to build the game from scratch by working on it only a few hours a week outside of our day-jobs. Our goals for writing Loggerbill were:

 

So, we've released LoggerBill as opensource to the community. We're happy with Libgdx and what we've managed to put together in it so in this article (and future ones) I would like to share with you our work. I hope to showcase our design decisions, why we made them, and provide an easy to follow example. You can find the source code for LoggerBill on GitHub using the link at the bottom of this page.

An overview of the core game classes

 

The Project and tree structure

Our IDE of choice is Intellij IDEA by JetBrains. It is, in our opinion, the best IDE out there by a long-shot. For Eclipse users we recommend giving IDEA a spin, it will be a true eye-opener. Unfortunately, Eclipse seems to be more widely used by the community so it is more difficult to find information on how to do things in IDEA. Generally though, tasks that are complex and require online tutorials in Eclipse are much more intuitive and easy to figure out without any help with IDEA. IDEA also seems to be gaining more traction in the Android world thanks to it being the base for Android Developer Studio.

For the project / build management tool we are using Gradle. Having used Ant and Maven for Java builds in the corporate world for ages we had never heard of Gradle until starting on Android dev, but it is a popular choice now in Android developers and it works well. The Libgdx project creator builds Gradle projects and it all works well, so we just stuck with what it gave us, naturally.

If you use the Libgdx project builder to create a new Gradle project you will end up with a parent project that contains a number of sub-modules, depending on which platforms you choose. We have:

The gradle module obviously is just some auto-generated gradle build stuff and can simply be ignored. You will always have a "core" module, which contains the bulk of the game. The other modules are the actual program entry points for the various platforms. while most of your development work will always be concentrated in core you will need to tweak the other modules as well. We have not yet worked in the ios module due to lack of any Apple hardware or experience but we have kept that module there with the intention of producing an iOS build in the future.

The Core Module

We have only one source file in the root of the core module, and 4 subpackages:

Constants

We have lots of constants that we need to refer to in different parts of the game. As is common practice, we've created a single class to statically expose these, located in com.maplescot.loggerbill.misc.Constants. Everything in this file is static so there is never an actual instance of Constants created. This lets us tweak important parameters in one easy place. Note, for the viewport resolution  there is a constant of width, but no associated height constant. We calculate the height the first time it is requested using the getViewportHeight() method also located in Constants. This was done to allow it to scale properly on devices that have different aspect ratios.

LoggerBillGame

The single class at the root level, "LoggerBillGame" is actually entirely boilerplate code. It is instantiated by the platform specific launcher. This class extends the core Libgdx class "Game" and contains only a constructor and three methods that override those in Game. They don't do a whole lot. The core Game class in Libgdx is a handy construct for building games. It's purpose it to allow us to define multiple screens by implementing the Screen interface. The currently selected screen is simply rendered every frame.

@Override
 public void create () {
        Gdx.app.setLogLevel(Application.LOG_DEBUG);
        ProfileManager.init();
        setScreen(new SplashScreen(this));
 }

Create() is called first. In this method we set logging level, initialize our profile manager and create an instance of com.maplescot.loggerbill.ui.SplashScreen, which is set as the screen for this game. The primary purpose of the SplashScreen is to provide something for the player to look at while we load our resources.

All of the Screens in LoggerBill extend a class called AbstractScreen, which simply contains boilerplate code that is common to all screens. This in turn implements the core Libgdx Screen interface.

SplashScreen

For each screen there are three methods that must be overridden at minimum; show(), render(float delta), and dispose(). Naturally, show() is called when the screen is shown and thus contains initialization code. For our splashscreen we need to load a couple of resources that are used only during the splashscreen.  Specifically we will load a texture atlas with a simple loading animation and graphical text.

@Override
public void show() {

    Gdx.app.debug(TAG, "STARTING");
    camera = new OrthographicCamera();
    camera.position.set(0, 0, 0);
    camera.setToOrtho(true, VIEWPORT_GUI_WIDTH / 2, getViewportHeight() / 2);
    camera.update();

    batch = new SpriteBatch();
    batch.enableBlending();

    atlas = new TextureAtlas(Constants.TEXTURE_ATLAS_SPLASH);
    Array<TextureAtlas.AtlasRegion> morphRegions = atlas.findRegions("morph");
    for (TextureRegion r : morphRegions) r.flip(false, true);
    morphAnimation = new Animation(10.0f / 60.0f, morphRegions, Animation.PlayMode.NORMAL);

    nameRegion = atlas.findRegion("name2");
    nameRegion.flip(false, true);
    Gdx.app.debug(TAG, "Queuing Asset Load");
    Assets.getInstance().load(); // Start loading...
    Gdx.app.debug(TAG, "Asset Load queued");
}

We use Libgdx's simple Animation class here to prepare a nice loading graphic. If you download LoggerBill from the Android market you will notice that the graphic used is different, but that's just a matter of changing the atlas file that contains our images. In order to show anything in LibGDX we need to create a Camera and SpriteBatch. These are best explained in the libgdx wiki. All of the sprites we use for this page are located in a single image called a sprite atlas (or sprite sheet). A text file associated with the image tells Libgdx's TextureAtlas class where to find and extract the various regions from our larger image to make the individual sprites.

In the render method of our splashscreen we need to do some work. Actually we need to load our assets (the sprites and sounds that will be used through the rest of the game) into memory. This can be time-consuming so we will just display an animation here and delegate the actual resource loading to our Assets class.

Assets

This class is a little unusual and convoluted. It goes against many standards in the world of general Java development, but we believe it's acceptable here. Basically, it's a collection of public reference variables that point to the different images and sounds that will be used in the game. In many ways it operates like the Constants class, except we need to actually load these resources at run time so we can't just use finals. A core Libgdx feature called AssetManager is used for this loading process. AssetManager itself and the loading of the resources is a topic for another article. In order to expose one set of assets to every class we've created this as a singleton. It probably could have worked with statics as well, but the singleton pattern is cleaner here.

The assets are public variables. This is normally considered bad practice because they can be altered unexpectedly by other users of the class. Here however I think the benefits of simplicity in accessing them in clear constant like manner outweighs any risks of misuse.  We trust everyone on the team to use the resources appropriately and not alter them in other classes. You could also make a case for better performance by directly accessing the assets instead of using getter methods, but I don't really think this is a valid argument today since most compilers should inline getters to give the same performance.

For a couple of our more complex assets we have subclasses which simply segregates them and allows for a nicer interface. Specifically we have 5 different types of cloud sprites, but when we use them we only ever just want a random cloud - we don't care which one it is. The Clouds class contains a getRandomCloud method that gives us a random cloud shape on request. The fonts are not random, but we do have 3 of them. This sub class gives a nice interface of Assets.Fonts.small (or .normal, or .big as appropriate).

Main Menu

When the Assets are finished loading and the SplashScreen has finished it's animation it starts the music playing and passes control to the main menu:

(in SplashScreen's render method)

if (assetsLoaded && stateTime >= 2.0) {
    Assets.getInstance().playMusic(ProfileManager.getProfile().isMusicOn());
    game.setScreen(new MainMenu(game));
}

MainMenu is a general scene2d-ui based menu screen. The basic layout could potentially be used for other games by simply changing the images, so we've used Libgdx skins to allow us the flexibility to re-use this in the future.

We started with just a static background image for our menu, but after building an animated background, class com.maplescot.loggerbill.game.world.BackgroundScenery, for the game we realized that we could re-use it here as well so BackgroundScenery was made into a singleton and rendered here.

The "Play" button starts the game. We create a new instance of LoggerEngine and pass that into a new instance of GameScreen, which we set as the current screen.

 

GameSreen

The class com.maplescot.loggerbill.ui.GameScreen actually does very little. This again is a simple boilerplate class that can be freely copied to a new game without any changes. The real work is done by the Engine, which is any class that extends our interface GameEngine.  We abstracted this even further and allowed for the possibility to separate the view from the controller with the GameRenderer interface as well. In theory a game could have multiple different rendering engines for drawing the graphics and multiple different engines for different mini-games. Take, for example, a card game that can play Solitaire, Poker, Cribbage, Crazy-8s, and Dominoes. We might be able to use a single Rendering engine for Solitaire, Poker, Cribbage, and Crazy-8s but a different rendering engine for Dominoes. But all of them would have different rules and therefore require different implementations of Engine.

In the case of LoggerBill, a single implementation class implements both GameEngine and GameRenderer.

The GameScreen also needs to know who will be handling user input. We figured that the Input is directly related to the engine so we let the engine decide and tell us. The render method, as always in libGDX is called for each frame of the game. Here we draw first (using the GameRenderer) and then run the game logic (GameEngine.run()).

LoggerEngine

The main game engine is an implementation of our generic GameEngine class called LoggerEngine. It starts with init() which instantiates our Shaders, particle effects, and pause dialog. Following that is a call to reset() to set all of our tracking variables to their starting values. From there the draw methods and the run method get called each frame of the game.

LoggerBill is a very simple game. When you touch the screen the touchDown() method gets called, that handles moving the Bill sprite to the correct spot and ejecting chunks out of the tree. The tree itself is simply a LinkedList of 'chunks', each time a chunk is ejected from the tree (by being chopped) it is removed from the tree list and placed on another LinkedList called ejectedChunks. On each render cycle we draw the tree chunk by chunk and then the ejectedChunks, chunk by chunk. It is important to have a list of ejectedChunks because it is quite possible for the player to touch so fast that multiple ejectedChunks are visible at one time.

The main game loop in run() is rather quite simple... All that we do is alter a variable called fall_dist which shifts the column of tree chunks down after each ejected piece and then check to see if Bill has died either from collision with a branch or by running out of time.

There are two draw methods, drawWorld() and drawHUD(). Again this was done with more complex games in mind where you may want to actually delegate the draw tasks to other classes to allow for multiple HUDs or multiple different game displays. Here though drawWorld handles drawing of the backtround scenery, the tree, ejected chunks, and the player Bill. We simply use the 'painter's algorithm' to draw things. That is, the background is drawn first and closer objects are drawn by order of their distance so that nearer objects are simply painted over the more distant ones. The HUD (heads up display) shows us the score and the timer gauge.

We use a couple of shader programs in LoggerBill, these are small programs that run on the device's graphics processor and alter the way images are drawn to the screen. In LoggerBill they allow us to create a night-time effect as as a wibbly-wobbly ghost effect. These will be covered in another article, along with details on the Google Play services Integration and our implementation of Leaderboards and achievements.

 

There are a lot of other classes in the game, but the bits that I've covered here represent the essential core of LoggerBill and the basis for any future games using the same design. If there is anything you would like explored in detail please send us an email and we will do our best to explain. If you spot any errors in the code or have suggestions on how we can improve it we welcome those comments as well.

 

 

 

Hits: 4467