Developing Multiplatform Game with Libgdx, part 11: bonus display and enemy lives

Winning The Game

Some people really don’t know when to stop. I mean, as if implementing the game losing process was not enough? Sigh. Fine, we’re going to allow our player to win.

I’m actually excited. This means that today we’re getting a first „real” prototype of the game 🙂 Usually at this stage you can see how do things feel and if it’s worth continuing to work on it.

Let’s start small. Add health value to our enemy. We’ll need to take it down before we win. Go to our „Enemy” class. Same as our player, add variable to count lives there;

private int lives;

Great! Now initialize it in our constructor. I’m setting it to 10, but you can pick however many you want. The point is not to make the game too annoying to play, so the enemies definitely should be beatable, but not very easily (leave some challenge!).

private static final int DEFAULT_ENEMY_LIVES = 10;
…
public Enemy(Resources res, EnemyAttackListener listener)
{
    lives = DEFAULT_ENEMY_LIVES;
…

Good! The only thing left to do here is to make a getter.

public int getLives()
{
    return lives;
}

This part should be good. Let’s show the enemy lives on our gamescreen. Go to GameScreen class and in our DrawUI function, add the line which will draw enemy lives in the top right corner. Use our good-old DrawShadowed function for that.

DrawShadowed("ENEMY:" + logic.getEnemy().getLives(),
        0,
        gameStage.getViewport().getScreenY() + gameStage.getHeight() - 3, // add some vertical padding
        gameStage.getWidth() - 5,
        Align.right,
        Color.WHITE);

Run the game. You’ll see that enemy lives are written on the right corner now.

Enemy Lives Displayed On The Right Top Corner

Enemy Lives Displayed On The Right Top Corner

Cool, huh?

Spawning Bonuses and Attacking

Now let’s find a way how we can make them go down. Since our mechanics involve stepping on the field and dodging attacks, why not add some pickups on the same tiles that would trigger the damage on an enemy? Or even heal the player?

First, add the graphics for those pickups. I’m going to use two pictures that my girlfriend Helen drew for me (https://twitter.com/ElenaNazaire). Those are two icons, named attack.png and health.png

attack health

Declare and load them up in our Resources class!

public Sprite attackBonus;
public Sprite healthBonus;

public Resources()
{
…
    attackBonus = gameSprites.createSprite("attack");
    healthBonus = gameSprites.createSprite("health");
}

Next, in our objects package, create a new class, name it “Bonus”; As usual, let’s think first what we want from our Bonuses.

– Same as with our effects, we should strive to reuse the bonuses instead of reinitializing them.
– We want two bonus types: one that takes enemy health away, the other gives health to our player.
– How do we detect that player gets the bonus?

Make Bonus extend Sprite (after all, the bonus needs to be displayed?) and, same as effect, implement Poolable. Let’s add bonus type and fieldX and fieldY as a private variables. Here’s what we have now:

public class Bonus extends Sprite implements Pool.Poolable {

    private byte bonusType;
    private int fieldX;
    private int fieldY;

    @Override
    public void reset() {

    }
}

Now make an empty constructor.

public Bonus()
{
    
}

Good. It will be called when our pool cannot locate any free unused bonuses. Now define two type constants:

public static byte BONUS_TYPE_ATTACK = 0;
public static byte BONUS_TYPE_HEALTH = 1;

Now, define setup function, which will assign the proper values.

public void setup(int fx, int fy, byte bType, Resources res)
{
    fieldX = fx;
    fieldY = fy;
    bonusType = bType;
    if (bType == BONUS_TYPE_ATTACK)
    {
        set(res.attackBonus);
    }
    else if (bType == BONUS_TYPE_HEALTH)
    {
        set(res.healthBonus);
    }
}

Should be self-explanatory, but the last conditions assign our bonus pictures. Now, similar to our WarningEffect, let’s make a bonus pool.

static final Pool<Bonus> bonusPool = new Pool<Bonus>() {
    @Override
    protected Bonus newObject() {
        return new Bonus();
    }
};

It will store unused bonuses (or create new if there are no free ones). Speaking about creating and releasing bonuses, let’s make a functions that do this:

public void release() {
    bonusPool.free(this);
}

static public Bonus Create(int fx,
                           int fy,
                           byte bType,
                           Resources res)
{
    Bonus bonus = bonusPool.obtain();
    bonus.setup(fx, fy, bType, res);
    return bonus;
}

Let’s not forget the drawing process too. Just cheat (the only time you should) and copy it from the Player.

public void draw(SpriteBatch batch, SizeEvaluator sizeEval)
{
    setPosition(sizeEval.getBaseScreenX(fieldX), sizeEval.getBaseScreenY(fieldY));
    super.draw(batch);
}

Finally, don’t forget the fieldX, fieldY and bonusType getters.


public int getFieldX()
{
    return fieldX;
}

public int getFieldY()
{
    return fieldY;
}

public byte getBonusType()
{
    return bonusType;
}

This should be enough for the bonus implementation (for now). Let’s get to the game logic. Since our field is small and player moves relatively rarely (maybe a few times per second max), we can make a list of bonuses and go through the list every time when player makes a move to check whether there was a collision with bonus and something should be done.

Go to our GameLogic class. Declare the bonus list, elapsed game time and the time of the last bonus spawn:

ArrayList<Bonus> bonuses;
float gameTime;
float lastBonusSpawnTime;

Initialize those variables it in the GameLogic constructor.

bonuses = new ArrayList<Bonus>();
lastBonusSpawnTime = 0;
gameTime = 0;

How often do we want to spawn bonuses? Let’s make it two seconds for experimentation purposes. Make a constant to show that:

private static final float BONUS_SPAWN_INTERVAL = 2.0f;

Great. Now to the actual spawn process. We should do this in our update function. First, we want to increase the game time, no matter what happens. Since update passes the delta value, we just add this to our gameTime and in the end we have the total game time. Then, we compare the gametime to the last spawn time and if it is greater than the bonus spawn interval, we spawn a new bonus (magic number, let’s cap our bonus amount to three. So, don’t spawn a bonus if we already have three bonuses). Yes, we need a separate function for that. Here’s how my update function looks now:

public void update(float delta)
{
    gameTime += delta;
    effectEngine.update(delta);
    enemy.update(delta);
    if (lastBonusSpawnTime + BONUS_SPAWN_INTERVAL < gameTime &&
            bonuses.size() < 3)
    {
        SpawnRandomBonus();
    }
}

Now, let’s get to SpawnRandomBonus function. We need to ensure that our new coordinates do not overlap with player or other bonuses. Also, let’s not generate the bonus on the same column and row where player is standing. That leaves us with plenty of space (9 tiles, to be exact).

private void SpawnRandomBonus()
{
    int fx = 0;
    int fy = 0;
    boolean targetNonEmpty = true;
    do {
        fx = MathUtils.random(MAX_BASE_X);
        fy = MathUtils.random(MAX_BASE_Y);
        targetNonEmpty = player.getFieldX() == fx || fy == player.getFieldY();

        for (int i = 0; i < bonuses.size() && targetNonEmpty; i++)
        {
            if (bonuses.get(i).getFieldX() == fx &&
                    bonuses.get(i).getFieldY() == fy)
            {
                targetNonEmpty = true;
            }
        }
    } while (targetNonEmpty);

    bonuses.add(Bonus.Create(fx, fy,
            MathUtils.random(3) == 0 ? Bonus.BONUS_TYPE_HEALTH : Bonus.BONUS_TYPE_ATTACK,
            game.res));
    lastBonusSpawnTime = gameTime;
}

The function might look tricky, but let’s go through it:

First, we generate new coordinates for the bonus. We check if those are equal to players coordinates. Then we go through already existing bonuses and if we find the bonus that has the same coordinates – we stop searching. We simply generate the new coorindates.

After acceptable coordinates have been found, we add the new bonus to the list. We don’t want health bonuses to spawn too often, so we generate a number from 0 to 3. If the number is 0, we spawn a health bonus. Otherwise, we spawn an attack bonus (which will damage our enemy).

Finally, we set our last bonus spawn time to current game time.

I feel that this tutorial is getting long, so let’s no program the logic of the bonuses, but only draw them. Create a bonus list getter in GameLogic:

public ArrayList<Bonus> getBonuses()
{
    return bonuses;
}

Then, in our GameScreen, right before we draw our player, let’s draw our bonuses. Here’s how the last part of the code looks:

batch.begin();
for (Bonus bonus : logic.getBonuses())
{
    bonus.draw(batch, sizeEvaluator);
}
player.draw(batch, sizeEvaluator);
enemy.draw(batch, sizeEvaluator);
batch.end();

Run the game. The bonuses should spawn now and be correctly displayed!

Bonuses are now displayed!

Bonuses are now displayed!

This concludes the lesson. In our next lesson, we are going to work on the actual logic (picking up bonuses and damaging the enemy).

Relevant commit: https://github.com/vladimirslav/dodginghero/commit/a439ec0c706ba5fd1110186cc9bd860bb97fe27b

Developing Multiplatform Game with Libgdx, part 10: adding fonts!

Adding a Font

So, our player has lives and he can lose the game. But he has no way to find out the lives left and when the game ends – everything just closes. Not cool. We’ll fix it today.

The simplest thing to fonts libgdx has to offer is BitmapFont class. Without paramters, it will simply show arial 12 font. The problem? Resizing that font. Our game cannot open ttf files, so the texture with font needs to be generated first. BitmapFont is going to use that texture, but it needs to be prepared beforehand. To do this, we are going to use Hiero. I’m downloading it here: https://libgdx.badlogicgames.com/tools.html

Make sure to pick a ttf font you want first. For pixel-art styled games, I normally use http://www.pentacom.jp/pentacom/bitfontmaker2/gallery/ to get a freely available font. I’m picking up Megaman10 by YahooXD: http://www.pentacom.jp/pentacom/bitfontmaker2/gallery/?id=528 (public domain). Download it to your computer, save it wherever you want to (but I usually save it in the project assets folder, separate from the one that is being packed).

Run the downloaded runnable Hiero. Open your font file, and pick the size you want. For out case, since the game is really low-resolution, I think 16px will be more than enough. I usually set padding to 2 (to have some extra space between letters) and use rendering type: java.

After that – go to “File” -> Save BMFont files. Name it gamefont and put the files into android/assets folder, since this is the one that is used for resource loading by default. There should now be two files: fnt file (which is a font descriptor) and png file, which is actually a texture of the font.

If you are going to use the same font throughout the game – I think it’s a good idea to put it into Resources class too:

public BitmapFont gamefont;

Initialize it in Resources constructor:

gamefont = new BitmapFont(Gdx.files.internal("gamefont.fnt"),
        Gdx.files.internal("gamefont.png"), false);

To test it, go to our GameScreen and add DrawUI function, which will draw our font at top of the screen.

private void DrawUi()
{
    batch.begin();
    game.res.gamefont.draw(batch,
            "LIVES: " + player.getLives(),
            5,
            gameStage.getViewport().getScreenY() + gameStage.getHeight());
    batch.end();
}

The thing with font is… It’s drawn from top to bottom, a complete opposite of what we have when drawing textures. I can’t explain why it is (if you can – please comment!). So in our case, when we want to draw it on top of the screen, we get the screen Y position (in case not all height is covered) and add an actual stage height. Add the DrawUI call inside our render function, right before our gamestage.draw() call.

@Override
public void render (float delta) {
// …
// old code here, not copying it
// …
    DrawUi();
    gameStage.draw();
}

Run the game. Great, now we can actually see how our lives are being deducted. What about the endgame? What I think is important – it to show the message that the player lost instead of quitting the game (sure, that’s the only thing left for our player to do now, but not for long).

Remove this part from our GameLogic:

if (player.getLives() <= 0)
{
    Gdx.app.exit();
}

Our OnEffectOver(WarningEffect effect) should look like this:

public void OnEffectOver(WarningEffect effect) {
    if (effect.getFieldY() == player.getFieldY() &&
        effect.getFieldX() == player.getFieldX())
    {
        player.takeDamage(1);
    }
}

Good. Now let’s get back to our GameScreen. We need to do two things: disable update and player controls after player died (so that player cannot move / monsters cannot attack).

Alter our AttemptMove function:

public void AttemptMove(int dx, int dy)
{
    if (player.getLives() > 0 &&
            logic.CheckMove(player.getFieldX() + dx, player.getFieldY() + dy))
    {
        logic.AssignPlayerPosition(player.getFieldX() + dx, player.getFieldY() + dy);
    }
}

After that – check our update call. Stop updating logic on player death, it is unnecessary.

private void update(float delta)
{
    gameStage.act(delta);
    if (player.getLives() > 0)
    {
        logic.update(delta);
    }
}

Finally, modify our DrawUI function to write “DEFEAT” after player has died.

if (player.getLives() <= 0)
{
    game.res.gamefont.setColor(1, 0, 0, 1);
    game.res.gamefont.draw(batch,
            "DEFEAT",
            0,
            gameStage.getViewport().getScreenY() + gameStage.getWidth() / 2,
            gameStage.getWidth(), Align.center, false);
    game.res.gamefont.setColor(1, 1, 1, 1);
}

We set font color to red, then we show 0 as X coordinate and take stageWidth as targetWidth because the writing will automatically be aligned to center. After that, we switch font color back to white (don’t forget this step!)

If you run the game now, you can see that both ‘lives’ and ‘defeat’ text are poorly visible. How can we fix this? One way to do this is to add a black border. Let’s make a new function: drawShadowed(x, y, width, align, color)

Our function will draw the background of our word in black and then will overlap the background with the word of chosen color.

private void DrawShadowed(String str, float x, float y, float width, int align, Color color)
{
    game.res.gamefont.setColor(Color.BLACK);
    for (int i = -1; i < 2; i++)
    {
        for (int j = -1; j < 2; j++)
        {
            game.res.gamefont.draw(batch, str, x + i, y + j, width, align, false);
        }
    }

    game.res.gamefont.setColor(color);
    game.res.gamefont.draw(batch, str, x, y, width, align, false);
    game.res.gamefont.setColor(Color.WHITE);
}

After that, change our DrawUI to actually draw shadowed text:

private void DrawUi()
{
    batch.begin();
    DrawShadowed("LIVES:" + player.getLives(),
            5,
            gameStage.getViewport().getScreenY() + gameStage.getHeight() - 3, // add some vertical padding
            gameStage.getWidth(),
            Align.left,
            Color.WHITE);

    if (player.getLives() <= 0)
    {
        DrawShadowed("DEFEAT!",
                0,
                gameStage.getViewport().getScreenY() + gameStage.getWidth() / 2,
                gameStage.getWidth(),
                Align.center,
                Color.RED);
    }

    batch.end();
}

Run the game! Everything should be bordered now. Now we can lose with style!

Lose with style!

Relevant commit: https://github.com/vladimirslav/dodginghero/commit/14d7cf194a9d992ea7a6753662f0ac4685879911

Developing Multiplatform Game with Libgdx, part 9: losing the game!

Life and Death

Today we have an important day: we actually get to implement an important part of the game: the losing process. To make it simple: if player does not get out of harm’s way in time – he gets punished (his lives are reduced). After the lives drop down to zero: the game ends. Since we don’t have any fonts attached, we don’t have a way to inform the player about this. So we’ll just exit the game. Yes, pretty abruptly. We’ll deal with this later. Also, don’t freak out. I’ve replaced my horribly-drawn tiles with the ones that my girlfriend has drawn for me. You can get them in the project repo.

Modifying our Effect

First, we need to modify our warning effect so that it would inform our logic that it is actually over (timed out). In our WarningEffect class, add a public interface:

public interface WarningEffectListener
{
    public void OnEffectOver(WarningEffect effect);
};

Declare the variable which will point to the actual interface.

private WarningEffectListener listener;

Modify our init method and Create function to include the interface parameter.

public void init(int x, int y, EffectEngine parent, Resources res, WarningEffectListener _listener)
{
    super.init(parent);
    listener = _listener;
    resources = res;
    fieldX = x;
    fieldY = y;
}

static public WarningEffect Create(int fx,
                                   int fy,
                                   Resources res,
                                   EffectEngine engine,
                                   WarningEffectListener _listener)
{
    WarningEffect effect = warningPool.obtain();
    effect.init(fx, fy, engine, res, _listener);
    return effect;
}

Also, make fieldX and fieldY variables private and create a getter for them.


public int getFieldX()
{
    return fieldX;
}

public int getFieldY()
{
    return fieldY;
}

Great, this part is done. Now we only have to call it as effect ends. Modify our update function:

@Override
public void update(float delta)
{
    super.update(delta); // pass the time change
    // add an aditional security in order not to call death trigger twice
    if (timeAlive > WARNING_TIME && isAlive)
    {
        isAlive = false;
        if (listener != null)
        {
            listener.OnEffectOver(this);
        }
    }
}

To expand upon this: we’ve added additional check in our condition: isAlive should be true, this is to prevent double calls of OnEffectOver.

If listener has been set (let’s leave an option not to set it, might be useful later) – call OnEffectOver and pass the pointer to this effect.

Cool, we can call our effect over checks, but we can’t reduce player’s lives yet. Because he does not have them defined. Go to our Player class. Add a private integer field that indicates the lives of our player. In our constructor, let’s pass the amount of lives our player will have.

private int lives;

public Player(int fx, int fy, Resources res, int _lives)
{
    lives = _lives;
…

Great. Now the two things we need to do is: get the amount of lives player has and reduce the lives of the player. This should not be too hard:

public void takeDamage(int amnt)
{
    lives -= amnt;
}

public int getLives()
{
    return lives;
}

Done! Now the only thing left is to modify our logic. Create a constant DEFAULT_PLAYER_LIVES (private static final int) and set it to three. Adjust the player initialization to pass amount of lives.

Make GameLogic implement WarningEffectListener. It’s quite simple:

public class GameLogic implements Enemy.EnemyAttackListener, WarningEffect.WarningEffectListener {

Press alt+enter to implement the missing method. It will be very simple. We will compare the effects position to player’s current position and if they match – reduce the lives of a player.

@Override
public void OnEffectOver(WarningEffect effect) {
    if (effect.getFieldY() == player.getFieldY() &&
        effect.getFieldX() == player.getFieldX())
    {
        player.takeDamage(1);
        if (player.getLives() <= 0)
        {
            Gdx.app.exit();
        }
    }
}

Now run the game and try to get hit intentionally 3 times. The game will exit. Not pretty (yet), but it shows the point.

Relevant commit: https://github.com/vladimirslav/dodginghero/commit/39613aa57334e37f57817c4ac53059575ad3c4f7

Developing Multiplatform Game with Libgdx, part 8: enemy attacks!

Heya!

In our previous lesson, we’ve created an enemy. Now, it is time we teach how to attack. Our enemy will be responsible for timing attacks and telling the logic that there should be a warning given to the player (so that he can dodge).

First, in our Enemy class, let’s make a float variable that will count time since last attack (name it timeSinceAttack). We don’t want our attacks to be too predictable, so we’re going to use another variable to determine how long should we wait until performing our next attack. Declare float variable nextAttackTime. It is going to use a constant value of 3 seconds and add some random time interval (up to two seconds).

Declare a private static final variable BASE_ATTACK_TIME, set it to 3.

Define a private void function resetAttackTimer(), which will regen the timers. Here’s how my Enemy class looks now:

public class Enemy extends Sprite {

    private static final float BASE_ATTACK_TIME = 3.0f;
    private float timeSinceAttack;
    private float nextAttackTime;

    public Enemy(Resources res)
    {
        set(res.enemy);
        resetAttackTimer();
    }

    public void draw(SpriteBatch batch, SizeEvaluator sizeEval)
    {
        setPosition(sizeEval.getEnemyX(this), sizeEval.getEnemyY(this));
        super.draw(batch);
    }

    private void resetAttackTimer()
    {
        timeSinceAttack = 0;
        nextAttackTime = BASE_ATTACK_TIME + MathUtils.random(2.0f);
    }
}

Add an update(float delta) function, which will be responsible for timing events and actually launching attacks. But the question appears: how will .our enemy class tell the logic that attack should happen? I generally want to avoid circular references ( http://programmers.stackexchange.com/questions/11856/whats-wrong-with-circular-references ),

So I’m going to make an interface (something like a listener), accept it in the enemy’s constructor and make our logic implement it.

public interface EnemyAttackListener
{
    void OnAttack(boolean[][] tiles);
}

It will contain one method declaration OnAttack that accepts two-dimensional Boolean array which by size mirrors battlefield tile amount. (It will also be 4×4). If value is true, that means that logic should create warning effect on the given tile.

Now create a variable that will store the reference to our listener and adjust constructor to accept one.

private EnemyAttackListener attackListener;

public Enemy(Resources res, EnemyAttackListener listener)
{
    attackListener = listener;

 

Now, the only thing that’s left is to create the actual Boolean array. We could initialize it on every attack, but it’s better to initialize it once for every enemy and reuse it on every attack (memory management on mobile devices, right?). Declare it in the Enemy class:

private boolean targetTiles[][];

 

Initialize those in Enemy constructor:

targetTiles = new boolean[GameLogic.MAX_BASE_X + 1][];
for (int i = 0; i <= GameLogic.MAX_BASE_X; i++)
{
    targetTiles[i] = new boolean[GameLogic.MAX_BASE_Y + 1];
}

First, we initialize the columns (Array that contains arrays), then, we initialize the rows (child arrays).

Great, now we’re ready to actually get to attacking process. In our update function, check the current time versus the time when we need to attack. If our time to attack has come, fill the tiles that we want attacked.

We’re actually going to diversify attacks later on, but for now let’s make a very basic one: pick two columns (non-consecutive), leave others untouched.

Here’s how our update function is going to look now:

public void update(float delta)
{
    timeSinceAttack += delta;

    if (timeSinceAttack > nextAttackTime)
    {
        int col1 = MathUtils.random(GameLogic.MAX_BASE_X);
        int col2 = 0;
        do {
            col2 = MathUtils.random(GameLogic.MAX_BASE_X);
        } while (col2 == col1);
        // not very effective, but guaranteed to get different results

        for (int x = 0; x <= GameLogic.MAX_BASE_X; x++)
        {
            for (int y = 0; y <= GameLogic.MAX_BASE_Y; y++)
            {
                targetTiles[x][y] = (col1 == x || col2 == x);
            }
        }

        attackListener.OnAttack(targetTiles);
        resetAttackTimer();
    }
}

Note that

targetTiles[x][y] = (col1 == x || col2 == x);

Is just a short way of writing:

if (x == col1 || x == col2)
{
    targetTiles[x][y] = true;
}
else
{
    targetTiles[x][y] = false;
}

You can use whatever type you like (if it helps you to understand the code better), but I prefer the shorter version.

Amazing, now let’s make our gamelogic implement attack listener:

public class GameLogic implements Enemy.EnemyAttackListener

Press Alt+Enter to implement the suggested method. We just simply need to go through the array that has been passed to us and check every tile. If the tile is set to true, we create an effect on that tile.

First thing: go to GameScreen constructor and remove the WarningEffect creationg line from there:

WarningEffect.Create(0, 0, sizeEvaluator, game.res, logic.getEffectEngine());

Delete it. Yes.

Now go back to GameLogic and do three things:

Remove SizeEvaluator from WarningEffect constructor. We don’t need it there. We need it on drawing only, similar to our Player and Enemy. Keep sizeEvaluator in WarningEffect’s draw method, but remove all other mentions and declaration of SizeEvaluator in our WarningEffect class. Adjust “draw” function to accept SizeEvaluator as second parameter. Do the same thing with our parent class, Effect.

public abstract void draw(SpriteBatch b, SizeEvaluator sizeEvaluator);

Add the same parameter to our EffectEngine.

public void draw(SpriteBatch batch, SizeEvaluator sizeEvaluator)
{
    for (int i = 0; i < effects.size(); i++)
    {
        effects.get(i).draw(batch, sizeEvaluator);
    }
}

Go back to GameLogic. In Gamelogic’s update method, let’s add the update call of our enemy:

public void update(float delta)
{
    effectEngine.update(delta);
    enemy.update(delta);
}

Yup, only that. Now, to an actual OnAttack method:

@Override
public void OnAttack(boolean[][] tiles) {
    for (int x = 0; x < tiles.length; x++)
    {
        for (int y = 0; y < tiles[x].length; y++)
        {
            if (tiles[x][y])
            {
                WarningEffect.Create(x, y, game.res, effectEngine);
            }
        }
    }
}

As discussed before, we go through all the tiles, and create the effect when necessary. The only thing left to do is to adjust the effect drawing call in our GameScreen.

Launch the game, see what we get.

I’ve actually noticed a bug: in our WarningEffect, we’re not assigning fieldX and fieldY variables in init function. Let’s fix this:

public void init(int x, int y, EffectEngine parent, Resources res)
{
    super.init(parent);
    resources = res;
    fieldX = x;
    fieldY = y;
}

Run the game! You should be getting the enemy attack warnings shown once in a while.

Final look

Relevant commit: https://github.com/vladimirslav/dodginghero/commit/1e86d0af762486ad9425e28c82e4e46a59033522

Developing Multiplatform Game With Libgdx, Part 7: Adding Enemy

Adding basic enemy

In the previous lesson, we’ve implemented a possibility to show the effect before the enemy attack. But to actually send attack effects, we need an enemy to do that. Today, let’s try adding the enemy.

First thing: open our character tileset (http://opengameart.org/content/tiny-16-basic) and cut out any enemy that you’d like. It should be a 16×16 sprite. I’m going to save it as spider.png (It’s up to you how you want to save yours, just make sure you load it properly in our resource class).

Spider

Spider

Now go to your project, and open Resources class. In it, declare:

public Sprite enemy;

Initialize it as we initialize the player sprite (make sure to change the region name though!):

enemy = new Sprite(gameSprites.findRegion("spider"));

Next, right beside our Player object (in our logic.objects package), let’s declare another class, called Enemy. It, too, will be derived from Sprite class.

Opposite from player, it won’t have fieldX and fieldY (we’re always drawing our enemy in one place, right?), so in constructor let’s simply set the sprite value to be equal to our spider sprite. For that, we need to pass Resources to our constructor, just to be able to set the enemy sprite properly.

public class Enemy extends Sprite {

    public Enemy(Resources res)
    {
        set(res.enemy);
    }
}

We’ll also need a draw method(), which will take our size evaluator and set the enemy position accordingly.

To make it possible, let’s go to SizeEvaluator class and introduce two new methods that return float values: getEnemyX() and getEnemyY(). Both will accept enemy Sprite and use it’s length to center the enemy at the right half of the screen.

public float getEnemyX(Sprite enemy)
{
    return (measuredStage.getWidth() * 3 / 4) - enemy.getWidth() / 2;
}

public float getEnemyY(Sprite enemy)
{
    return measuredStage.getHeight() / 2 - enemy.getHeight() / 2;
}

In case of X coordinate, we take the coordinates of second half of the screen (first half is measuredStage.getWidth() / 2, and add an half of an half (measuredStage.getWidth() / 4), thus resulting in (measuredStage.getWidth() * 3 / 4). From that value, we’re decreasing enemy width divided by two, so that our enemy will be exactly centered in the middle of the right half.
In case of Y, we simply take the vertical middle of the stage and substract half of enemy height (for the same, exact centering purpose).

Now go back to our Enemy class. Let’s introduce the draw method, which will set position of our enemy sprite in the necessary coordinates and then will call parent’s (sprite’s) draw method. We should get something like that:

public void draw(SpriteBatch batch, SizeEvaluator sizeEval)
{
    setPosition(sizeEval.getEnemyX(this), sizeEval.getEnemyY(this));
    super.draw(batch);
}

A bit of side thoughts: I’ve been reevaluating my Player class, and I think it’s a better idea if we make constructor similar to enemy’s contructor. Pass the resource class to constructor, everything will be set inside the class itself. It’s a good idea to make class modifications inside the class itself. So, let’s rewrite the Player class in this way:
Constructor:

public Player(int fx, int fy, Resources res)
{
    fieldX = fx;
    fieldY = fy;
    set(res.player);
}

Add a draw function:

public void draw(SpriteBatch batch, SizeEvaluator sizeEval)
{
    setPosition(sizeEval.getBaseScreenX(fieldX), sizeEval.getBaseScreenY(fieldY));
    super.draw(batch);
}

It will do the same thing as our enemy’s draw function. Now let’s do some refactoring. Go to GameScreen, remove RefreshPlayer function.

Also, in GameScreen constructor, remove following line:

player.set(game.res.player);

We won’t be setting a sprite in GameScreen anymore, this should be an inner doing of Player class. In our render function of GameScreen’s class, player drawing should look like this:

player.draw(batch, sizeEvaluator);

After that, let’s adjust our GameLogic class. I think it’s fair if we let it store the pointer to our DodgingHero class (which, in turn, will allow to access game resources). In GameLogic class, declare DodgingHero game; variable. Modify GameLogic constructor to actually assign the variable value and pass game.resources to our player Constructor. Here’s how it looks now:

DodgingHero game;

public GameLogic(DodgingHero _game)
{
    game = _game;
    player = new Player(
            MathUtils.random(MAX_BASE_X),
            MathUtils.random(MAX_BASE_Y),
            game.res
    );
    effectEngine = new EffectEngine();
}

This should suffice for now. In GameScreen class, make sure to change logic initialization:

logic = new GameLogic(game);

Try running the game. Try resizing the window. Everything should work fine now.
Let’s finally get to our enemy now. Go back to GameLogic, and, besides our Player, let’s declare our Enemy:

Enemy enemy;

Make the necessary import (alt + enter), there won’t be a choice. Initialize our enemy in GameLogic constructor.

enemy = new Enemy(game.res);

Create a getter that will return the pointer to our enemy object (similar to getPlayer function).

public Enemy getEnemy()
{
    return enemy;
}

The only thing that remains to do is to draw our enemy in our GameScreen! We are not going to do the same thing we did with Player in GameScreen (where we called logic.getPlayer() only once, inside the constructor), because our enemies might change while player remains the same. So simply do this. After our:

player.draw(batch, sizeEvaluator);

call, add

enemy.draw(batch, sizeEvaluator);

Run the game, it should show something like that:

Our Enemy!

Our Enemy!

Now we have an enemy which will attack us!
Don’t worry, I still remember that we made an Effect which will show above the battlefield. We’re definitely going to use it in our next lesson, in which we are actually going to teach our enemy how to attack.
Don’t forger to commit your changes! Also note that you have to run manual git add command every time you add a new file. I actually had to add spider drawing by using „git add” command before commiting.

Here’s the relevant git commit: https://github.com/vladimirslav/dodginghero/commit/1e86d0af762486ad9425e28c82e4e46a59033522

Developing Multiplatform Game With Libgdx, Part 6: Adding Battlefield Effects

We have the field where our character can walk. But what’s the point of walking if you can’t get anywhere? Or rather, if we cannot dodge the attacks of the enemy?

We’ll try to address this today. Let’s add the base marks on the field that will indicate where will the next enemy attack will go (essentially telling player to avoid these bases). We’ll just show a huge exclamation mark above the dangerous bases for now.

After exclamation mark passes, the player will be damaged if he stays on the marked fields. Even though it’s a graphical effect, we’ll need it to essentially affect the game logic (trigger an attack after it’s done showing).

In our graph package, let’s create „effects” package. Inside that package, create „EffectEngine” class. Create an empty public constructor and leave it as is for now. The effect engine will contain the list of all effects that are currently present in the game. It will also update them and issue drawing commands.

Let’s create an “Effect” abstract class in the same package that will implement Pool.Poolable.

To get back on this: if we’re making a game for android, it is good to reuse objects instead of creating them anew. Imagine if we have one effect in place, it vanishes and then the new one appears. If we reallocate the memory for it, it will take much more time to do this (especially with Android’s Garbage Collector). Instead, we’ll make a Pool of objects that we’re going to reuse.

For our base Effect class, we’ll just keep the most necessary things. To me, it usually comes down to two variables. Each effect should have its lifetime counted. We also need to know if the effect is alive or not (so that EffectEngine will know when to destroy it). Declare them this way:

public abstract class Effect implements Pool.Poolable {

    protected boolean isAlive;
    protected float timeAlive;

They need to be declared protected, as it means that they will be accessible by derived classes. Since those will be reinitialized often, the constructor won’t do much apart from setting the variables to initial values.

public Effect()
{
    isAlive = false;
    timeAlive = 0;
}

After this, let’s actually get to our Init function, which will reset the values to zero and will add the effect to the engine (will add it a bit later). The release function should also be there in case we’ll need extra disposal.

public void Init(EffectEngine parent)
{
    isAlive = true;
    timeAlive = 0;

}

public void release()
{

}

Now to other functions: there is a need for draw() and update functions. Update will update the current effect (most of them will have the lifetime, update will be responsible for checking if our effect has expired). In our case, we simply adjust the timeAlive.

public void update(float delta)
{
    timeAlive += delta;
}

draw is self-explanatory. It will take the batch and draw the effect. Since we do not plan to instantiate the effect class, declare draw function abstract.

public abstract void draw(SpriteBatch b);

One more thing remains: let’s allow to access isAlive variable to check whether effect is alive by adding isAlive method:

public boolean isAlive()
{
    return isAlive;
}

Now, let’s get back to EffectEngine class. First thing we need to do: declare a List of Effects:

List<Effect> effects;
public EffectEngine()
{
    effects = new ArrayList<Effect>();
}

The whole interaction is going to happen with this list. Our EffectEngine essentially needs only four functions: draw(), update(), clear(), add(). Draw will be calling draw() on all existing effects, add will be used to add a new effect to the list, clear will clear all the effects (in case we exit the gamescreen) and update will do the necessary updates (and remove the dead effects from the list). Let’s start with add function, as it is going to be the simplest one out there:

public void add(Effect effect)
{
    effects.add(effect);
}

Nice and simple, just add a new effect to an existing list. The update function is going to be a bit trickier though. First, we’ll go through the effect list and call their ‘update’ functions. After that, it’s time to root out the dead (expired) effects.

public void update(float delta)
{
    int i = 0;
    while (i < effects.size())
    {
        effects.get(i).update(delta);
        if (effects.get(i).isAlive())
        {
            i++;
            // just go forward in our list
        }
        else
        {
            effects.get(i).release();
            effects.remove(i);
        }
    }
}


First, we call the update function of every separate effect. After that, we check if the effect is alive. If it is, we simply proceed to the next effect in our list. But if it is not, we remove the effect with the current index (and do not increase our index, because the next element in the list moved to the current index).

The draw function is much simpler:

public void draw(SpriteBatch batch)
{
    for (int i = 0; i < effects.size(); i++)
    {
        effects.get(i).draw(batch);
    }
}

Go through all effects and draw them. And finally, clear function. The idea behind it is to mark the effects reusable (released) so that the next time (in case we restart the GameScreen) our effect pool won’t need to create any new elements, but reuse the old ones.

public void clear()
{
    while (effects.size() > 0)
    {
        effects.get(0).release();
        effects.remove(0);
    }
}

Finally, go back to Effect class and modify it’s Init function: at the end of it, call parent.add(this);

Here’s how my code looks for Effect class:

public abstract class Effect implements Pool.Poolable {

    protected boolean isAlive;
    protected float timeAlive;

    public Effect()
    {
        isAlive = false;
        timeAlive = 0;
    }

    public void init(EffectEngine parent)
    {
        isAlive = true;
        timeAlive = 0;
        parent.add(this);
    }


    public boolean isAlive()
    {
        return isAlive;
    }

    public abstract void draw(SpriteBatch b);

    public void update(float delta)
    {
        timeAlive += delta;
    }

    public abstract void release();
}

And EffectEngine class:

public class EffectEngine {

    List<Effect> effects;
    public EffectEngine()
    {
        effects = new ArrayList<Effect>();
    }

    public void add(Effect effect)
    {
        effects.add(effect);
    }

    public void update(float delta)
    {
        int i = 0;
        while (i < effects.size())
        {
            effects.get(i).update(delta);
            if (effects.get(i).isAlive())
            {
                i++;
                // just go forward in our list
            }
            else
            {
                effects.get(i).release();
                effects.remove(i);
            }
        }
    }

    public void draw(SpriteBatch batch)
    {
        for (int i = 0; i < effects.size(); i++) { effects.get(i).draw(batch); } } public void clear() { while (effects.size() > 0)
        {
            effects.get(0).release();
            effects.remove(0);
        }
    }

Adding the warning effect

Well, time to do a bit more preparations before we actually .Let’s draw the Warning sign effect. (16×16). Here’s mine:
warning
Save it as “warning.png”

Now we’ll derive a new class (WarningEffect) which will show the warning on the field. In our effects package, create a new Java Class, name it “WarningEffect.” Make it extend the Effect class. Automatically implement the suggested methods (draw, release, reset). Create a constructor that won’t do anything but call super(), constructor of the parent. Here’s what we have now:

public class WarningEffect extends Effect {

    public WarningEffect() {
    }

    @Override
    public void draw(SpriteBatch b) {

    }

    @Override
    public void release() {

    }

    @Override
    public void reset() {

    }

}

Here’s where it gets a bit tricky: since every type of effect requires it’s own pool, I usually make a static Pool in every separate class. And make a separate function that instantiates the object (either by creating a new one or by taking an unused one from the pool).

At the bottom of our WarningEffect class, add a new static variable warningPool:

static final Pool<WarningEffect> warningPool = new Pool<WarningEffect>() {
    @Override
    protected WarningEffect newObject() {
        return new WarningEffect();
    }
};

Essentially it will initialize a new WarningEffect if there are no released ones that are available for reinitialization. In our release() function, write

warningPool.free(this);

Which will indicate that the current object is available for reuse.

Now we need a Create function that will take care of returning a new effect instance and setting the base values to it.

static public WarningEffect Create(int fx,
                                   int fy,
                                   SizeEvaluator sz,
                                   Resources res,
                                   EffectEngine engine)
{
    WarningEffect effect = warningPool.obtain();
    effect.init(fx, fy, engine, sz, res);
    return effect;
}

Now as you have probably noticed, there’s an error when you’re trying to call inti function. We absolutely need to pass the resources (will contain warning texture region) and SizeEvaluator (will allow us to draw the effect in a proper place).

Let’s add our own init method and add a few extra variables to handle that. Make a good habit of declaring variables and constants at the start of the class: that way you’ll be able to find them easier, it improves readability. Put these lines at the start of our class:

Resources resources;
SizeEvaluator sizeEvaluator;
public int fieldX;
public int fieldY;

And then add our init method:

public void init(float x, float y, EffectEngine parent, SizeEvaluator sz, Resources res)
{
    super.init(parent);
    fieldX = x;
    fieldY = y;
    sizeEvaluator = sz;
    resources = res;
}

Let’s go back to Resources. Add a new TextureRegion declaration named warning.

public TextureRegion warning;

All that’s left is to add update() and draw() methods. Initialize it the same way you initialize any other Texture Region.

warning = gameSprites.findRegion("warning");

Now go back to WarningEffect class. In draw() method, add the actual drawing of a warning.

@Override
public void draw(SpriteBatch b) {
    b.begin();
    b.draw(resources.warning,
            sizeEvaluator.getBaseScreenX(fieldX),
            sizeEvaluator.getBaseScreenY(fieldY));
    b.end();
}

The last thing we need to do here is to set the update function. At the start of the class, declare a constant:

private static final float WARNING_TIME = 2.0f;

Our warning will be shown for 2 seconds. After that, the player will get damaged if he did not get out of the way.

Let’s override the update function now:

@Override
public void update(float delta)
{
    super.update(delta); // pass the time change
    if (timeAlive > WARNING_TIME)
    {
        isAlive = false;
    }
}

The backend part is done, let’s connect it to our logic and ensure it draws fine on our GameScreen. In our GameLogic, declare the EffectEngine variable and initialize it in constructor:

EffectEngine effectEngine;

public GameLogic()
{
    player = new Player(
            MathUtils.random(MAX_BASE_X),
            MathUtils.random(MAX_BASE_Y)
    );
    effectEngine = new EffectEngine();
}

Add the update(float delta) method, and make sure effectEngine.update(delta) is called there:

public void update(float delta)
{
    effectEngine.update(delta);
}

Now in our GameScreen’s update function, make sure to call logic.update(delta):

private void update(float delta)
{
    gameStage.act(delta);
    logic.update(delta);
}

We also need a way to draw the effects. Make a getter inside GameLogic that would return a pointer to our EffectEngine.

public EffectEngine getEffectEngine()
{
    return effectEngine;
}

Now we need to call the draw from our GameScreen. In our render function, after drawBases() call, add:

drawBases();
logic.getEffectEngine().draw(batch);

Our Effect Engine seems to be done. The lesson is getting lengthy, so I think I’ll just add one effect for the show and call it a day.

In our GameScreen constructor, after logic has been initialized, add the following:

WarningEffect.Create(0, 0, sizeEvaluator, game.res, logic.getEffectEngine());

Run the game. The warning sign will show at the leftmost bottom tile. That should be enough for now, we’ll continue to add better warning effect changes and player damage in our next lesson.

Relevant commit: https://github.com/vladimirslav/dodginghero/commit/4a2ff247520baefbd2ba5895bd1bc41acff03b5c

Developing Multiplatform Game With Libgdx, Part 5: Adding Logic and Basic Player Controls

Getting to game logic and basic controls.

Great. In our previous lesson we’ve made the walkable indicators where player can step in. We’ve also displayed the player sprite. First thing’s first: we remove it now. The teaser is over, boys. We’re back into hardcore programming reality, a no-man’s land of writing game logic.

No, seriously, remove the following line from GameScreen:

batch.draw(game.res.player,
        sizeEvaluator.getBaseScreenX(1),
        sizeEvaluator.getBaseScreenY(1) + 2);

Thanks.

Whenever we develop an application, it’s always a good idea to split logic from graphics. Even if it is much easier to go all-in in one GameScreen class, we don’t want that. The mix of code will make project harder to maintain, but it’s not the only reason. Imagine that you want to pause the game. The game should draw everything properly, only the game objects on the scene stay paused. If you intermix the code, you’re in for lots of extra conditions and workarounds. The rendering part simply should not care about the logics.

Creation of GameLogic and Player classes

Let’s create a separate package called “logic”. (in my case it is com.coldwild.dodginghero.logic). In this package, create a new class, call it “GameLogic”

Make constructor public. Forget about it for now. Inside our logic package, create another package, called “objects” (in my case it is com.coldwild.dodginghero.logic.objects)

Inside our “objects” package, create a new class, call it “Player”, make it inherit from Sprite class. (public class Player extends Sprite). Declare two variables, fieldX and fieldY, those will indicate. Make a public constructor, that takes two integers, named  fx and fy. Assign them to fieldX and fieldY respectively. This is a field position of your player. Also, introduce getters and setters for fieldX and fieldY values. Final look of player class:

public class Player extends Sprite {
    private int fieldX;
    private int fieldY;

    public Player(int fx, int fy)
    {
        fieldX = fx;
        fieldY = fy;
    }

    public int getFieldX() {
        return fieldX;
    }

    public void setFieldX(int fx) {
        fieldX = fx;
    }

    public int getFieldY() {
        return fieldY;
    }

    public void setFieldY(int fy) {
        fieldY = fy;
    }
}

Now, go to move our constants from GameScreen class

private static final int MAX_BASE_X = 3;
private static final int MAX_BASE_Y = 3;

to GameLogic class and make them public (that you’ll still be able to access from GameScreen). Change all existing references to those constants by adding GameLogic. In front of them (For example, GameLogic.MAX_BASE_X).

Run the game and make sure everything still works (only you don’t have your hero, because you’ve removed the drawing of his sprite).

Starting to incorporate GameLogic into our GameScreen

Declare GameLogic type variable in our GameScreen class (GameLogic logic;). Initialize it at the end of the GameScreen constructor. Go back to GameLogic and declare Player variable there (Player player), make sure to import it by pressing Alt+Enter. Now here’s where the fun part starts: we’re going to give our player random coordinates at the game start. Inside the GameLogic constructor, initialize our player. player = new Player();

player = new Player(
        MathUtils.random(MAX_BASE_X),
        MathUtils.random(MAX_BASE_Y)
);

Even though the GameLogic is going to make most Player changes, we still need to give access to our GameScreen class, at least for drawing purposes. In our GameLogic class, make a function that returns pointer to the player created:

public Player getPlayer()
{
    return player;
}

Here’s how GameLogic class looks at this point:

public class GameLogic {

    public static final int MAX_BASE_X = 3;
    public static final int MAX_BASE_Y = 3;

    Player player;

    public GameLogic()
    {
        player = new Player(
                MathUtils.random(MAX_BASE_X),
                MathUtils.random(MAX_BASE_Y)
        );
    }

    public Player getPlayer()
    {
        return player;
    }

}

Cool. Now we’ll be able to properly draw it.

Go back to GameScreen. Declare Player variable there (Player player;) assign value to it after our logic has been initialized:

logic = new GameLogic();
player = logic.getPlayer();

Since Player class extends Libgdx class “Sprite”, we’ll be able to draw it quite easily. All we need to do is to assign coordinates and the actual picture that is going to be drawn.

At first, we’ll need to do a small change to Resources class. Change the type of public TextureRegion player; to Sprite (public Sprite player;) Import the necessary class from libgdx (press alt+enter) and then go to Resources constructor and replace player initialization to:

player = new Sprite(gameSprites.findRegion("player"));

Now go back to GameScreen. After our getPlayer function call, add following commands. The next one assigns the picture to player sprite:

player.set(game.res.player);

And then add the next one:

player.setPosition(
        sizeEvaluator.getBaseScreenX(player.getFieldX()),
        sizeEvaluator.getBaseScreenY(player.getFieldY()));

This one was easy: just set the coordinates of our player sprite to actually match the base where the player is standing. Do not get attached to above part of code. It is not a good idea to assign inner class things in the “outside” code. We will move the sprite image and coordinate setting into Player constructor in next lessons, but right now I want to make it easier to understand.

Looks fine, now let’s actually add the drawing call into our GameScreen render() function. After drawBases() call, write: three lines:

batch.begin();
player.draw(batch);
batch.end();

Our familiar batch begind/end calls and an actual call to draw a player. Run the game a few times. You’ll see that player is located on a new position every time. (Which is preeetty cool). But let’s not dwell on our successes! Instead, we should move forward towards new victories. Let’s make that player move! Make our GameScreen implement InputProcessor:

public class GameScreen extends DefaultScreen implements InputProcessor {

Android Studio is going to tell you that some methods are missing. Press Alt-Enter and choose to add the missing ones automatically. The one you’re going to need is KeyDown. But first, we need to tell our game to accept our GameScreen input. To do this, go to GameScreen constructor and add the following line, right at the end:

Gdx.input.setInputProcessor(this);

In our GameScreen’s dispose method, we need to manually say that the game should abandon GameScreen class as input processor. Add the line

Gdx.input.setInputProcessor(null);

at the end of dispose() method.

Now we’ll be able to track key/mousepresses. Our Keydown function (at least on desktop) will be used to issue movement commands to the player. Sure, most simple thing to do now would be to simply set player’s coordinates on keydown (especially since we have the means). But, remember, logic is split from rendering? What if we are going to have animations between moves (meaning that plainly setting coordinates won’t work and we’ll need to play some animations before player moves)?

We’re still going to implement movement in a simplified way that will need refactoring later, but it’s better if we just adjust the players logical field coordinates inside the screen. Time to add functionality to our GameLogic class. We’re going to add public boolean CheckMove(int fx, int fy) function, which will tell us if player can move to the tile or not. The function should check whether new coordinates are legit. For now it’s going to be really simple: return false if coordinates are out of bounds or true otherwise. In the future, we might extend it.

In the end, our function looks like this:

public boolean CheckMove(int fx, int fy)
{
    return (fx >= 0 &&
            fx <= MAX_BASE_X && fy >= 0 &&
            fy <= MAX_BASE_Y);
}

Since each of our comparisons result in a Boolean, we can simply return comparison result instead of using an if/else logical operator.

The next function will actually save the player coordinates. Let’s name it AssignPlayerPosition, it will accept the new coordinates of a player.

public void AssignPlayerPosition(int fx, int fy)
{
    player.setFieldX(fx);
    player.setFieldY(fy);
}

 

Now let’s go back to our good old GameScreen. In our KeyDown function, let’s add some movehandling code. If we think about it, it does not matter where we move: left,right,bottom,top: the only thing that differs is a coordinate. Therefore, let’s try to move away the move attempts to a separate function. Let’s make a function called AttemptMove, to which we’ll pass the change of coordinates and it will do the rest.

public void AttemptMove(int dx, int dy)
{
    if (logic.CheckMove(player.getFieldX() + dx, player.getFieldY() + dy))
    {
        logic.AssignPlayerPosition(player.getFieldX() + dx, player.getFieldY() + dy);
        player.setPosition(
                sizeEvaluator.getBaseScreenX(player.getFieldX()),
                sizeEvaluator.getBaseScreenY(player.getFieldY()));
    }
}

First, we’re checking whether the new coordinates are legit. If they are, we’re setting a new position of the player on the base. To finalize it, we’re adjusting the position of the player on the screen.

Now we only need to assign proper function calls from our KeyDown function.

@Override
public boolean keyDown(int keycode) {
    switch (keycode)
    {
        case Input.Keys.RIGHT:
            AttemptMove(1, 0);
            break;
        case Input.Keys.LEFT:
            AttemptMove(-1, 0);
            break;
        case Input.Keys.UP:
            AttemptMove(0, 1);
            break;
        case Input.Keys.DOWN:
            AttemptMove(0, -1);
            break;
        default:
            break;
    }

    return false;
}

Each direction key moves our character relative to the field coordinates. So if we press the Right key, the character moves by one tile to the right horizontally (dx = 1), and zero tiles vertically (dy = 0). Similar things happen with other directional keys. Run the game and try pressing the keys. Our hero is moving! It could be the end of tutorial, but try resizing our window: the hero is in a bad place (literally). To change this, we need to refresh player’s screen coordinates after every resize. In our gamescreen, find both instances of player.setPosition calls and move them into separate function RefreshPlayer():

public void RefreshPlayer()
{
    player.setPosition(sizeEvaluator.getBaseScreenX(player.getFieldX()),
            sizeEvaluator.getBaseScreenY(player.getFieldY()));
}

Add RefreshPlayer() call to our resize() function (and make sure to replace the existing setPosition calls in GameScreen constructor and AttemptMove to RefreshPlayer() call). Run the program and try resizing the window! Everything should be working now.

That concludes our hero movement tutorial. Next time we’re start implementing the enemy and think of the way to display/handle his attacks! Stay tuned! Vladimir is out.

Relevant commit: https://github.com/vladimirslav/dodginghero/commit/d02228f6ecb39fdbf98bb4ad54e57b5fb8f29b73

Developing Multiplatform Game With Libgdx – part 4 – Game Field & Hero Placement

Preparing Assets

So, we have the background, maybe now it’s the time to get to the elements in the front. We’ll try to display a walkable tiles (4×4), and a player.

Since there’s really no way to indicate the walkable tiles, let’s draw a simple „base” first.  Make a small picture 16×2 and draw something like a base where character can stand at. Here’s mine (I’m really not an artist):

base

This will indicate where player can move. Save it as base.png. One thing that I’ve learned as I started to develop games is that this type of bases should never be as big as the character. Since our characters are 16×16, it might seem like a good idea to make the base as the square (which will be drawn around our character). Nothing can be further from the truth! If we do this – the characters will look ‘caged’ in the tiles which makes the game look bad.

As for the player, we pick one from the same tileset by Lanea Zimmerman (http://opengameart.org/content/tiny-16-basic). Just open up characters.png and cut out 16×16 tile with any character you like. Here’s mine:

player

Cool. Save it as player.png.

Let’s add those resources in our Resources class.  Similar to our ground and wall declarations, let’s declare two more TextureRegions: player and base.

public TextureRegion player;
public TextureRegion base;

Load them the same way you loaded wall and ground.

player = gameSprites.findRegion("player");
base = gameSprites.findRegion("base");

Ok, we’re done here. But before we proceed to actual drawing, we need to determine WHERE to draw them, right?

Programming The Coordinates

Again, we need to position those things in a good way. The way I approach it is that I usually create some sort of SizeEvaluator class which calculates the stage width and gets me the relative positions of the elements on the stage. Let’s get to programming! In our graph package, add a new class. Name it SizeEvaluator. This will actually calculate where we should place our hero / enemy / tile bases.

Add the private variable Stage measuredStage; this one will be pointing towards the current game stage. Also, add the private variable Resources which will be pointing, you’ve guessed it correctly, to the game resources.

The SizeEvaluator will also contain the max X and max Y the base tile can have. We have 4×4 field, so x and y will limit to 3 [0..3]. Similar to the screen coordinates, X will go from left to right (0 being the leftmost, 3 being the rightmost) and Y go from bottom to top (0 at the bottom, 3 at the top).

The SizeEvaluator constructor is going to accept a stage as parameter and assign the value to measuredStage. The same with resources: we’ll need them to get the tile sizes in order to properly calculate the distances. We’ll also need to pass max base x and max base y to it.

This is how it should look at first:

public class SizeEvaluator {

    Stage measuredStage;
    Resources resources;

    private final int maxTileBaseX;
    private final int maxTileBaseY;

    public SizeEvaluator(Stage _stage,
                         Resources _resources,
                         int _maxTileBaseX,
                         int _maxTileBaseY)
    {
        measuredStage = _stage;
        resources = _resources;

        maxTileBaseX = _maxTileBaseX;
        maxTileBaseY = _maxTileBaseY;
    }

}

 

It still does nothing. Let’s fix it! We’ll write the function which will give back the tile coordinates. We’ve talked that our hero (and the walkable tiles) are going to be located on the left. That means that we can simply take the width of the stage, split it into two then add some margin and take the position of the base into account.

We’ll implement the public function getBaseScreenX(int baseX) and getBaseScreenY(int baseY). It will take the x and y coordinates of the base (x and y can range from 0 to 3 included) and return their position on the screen.

The first implementation of functions is going to look like this:

public float getBaseScreenX(int baseX)
{
    return measuredStage.getWidth() / 2 - resources.TILE_SIZE * (1 + 
maxTileBaseX - baseX);
}

Quite simple: as we discussed before, we split the stage width by two (getting the coordinates of the middle), then we substract as tile widths to get to our necessary base x. (i.e. if we pass 0 as base X, we’ll take middle of our scene (call it midX) and substract (4 – 0) * 16 from it, getting the leftmost base tile coordinate).

 

We’re adding 1 to maxTileBase coordinate in order to handle the max case effectively. If we would not do it, the tile would be drawn over the middle (you can experiment and see what happens if you take away “1 +” part from the equation).

public float getBaseScreenY(int baseY)
{
    return measuredStage.getHeight() / 2 - resources.TILE_SIZE * ((maxTileBaseY + 1) / 2 - baseY);
}

getBaseScreenY looks a bit different. We want to center it vertically (equal amounts of tiles on top and bottom): to do that, we take the vertical middle of the scene and substract the half of total possible base field height (we have 4 bases with 16 sized tiles), so we substract 32 right now.

 

Adding SizeEvaluator to our Game Screen

Time to implement it now! In our GameScreen, declare a private variable SizeEvaluator sizeEvaluator; Declare constants MAX_BASE_X and MAX_BASE_Y, that we’re going to pass to our SizeEvaluator.

private static final int MAX_BASE_X = 3;
private static final int MAX_BASE_Y = 3;

Initialize the SizeEvaluator in GameScreen constructor (right at the end of it, after we’ve already initialized gameStage). Here’s how my GameScreen constructor looks now, see final line:

public GameScreen(DodgingHero _game) {
    super(_game);
    batch = new SpriteBatch();
    bg = new Background();

    ExtendViewport viewp = new ExtendViewport(STAGE_W, STAGE_H); 
    gameStage = new Stage(viewp, batch);
    sizeEvaluator = new SizeEvaluator(gameStage, game.res, MAX_BASE_X, MAX_BASE_Y);
}

Now we can proceed to drawing the bases. Let’s add function drawBases(), right here, in GameScreen class:

private void drawBases()
{
    batch.begin();
    for (int x = 0; x <= MAX_BASE_X; x++)
    {
        for (int y = 0; y <= MAX_BASE_Y; y++)
        {
            batch.draw(game.res.base,
                    sizeEvaluator.getBaseScreenX(x),
                    sizeEvaluator.getBaseScreenY(y));
        }
    }
    batch.end();
}

Before every drawing, we start our batch batch.begin(). After we are done drawing everything, we call batch.end(). We’ll go through our cycle and draw MAX_BASE_X + 1 columns, each containing MAX_BASE_Y +1 elements. (4×4, like we’ve planned).

Add the drawBases call in our render function, right after we call bg.draw:

bg.draw(gameStage, game.res);
drawBases();

Run the game and see what happens.

Tiles, first version

Tiles, first version

Well, we got some results, right? But it does feel somewhat off. Firstly, the bases are too tightly put together. Let’s add some margin before them.

Get back to our SizeEvaluator class and implement a new int constant: BASE_MARGIN, which will be equal to 2 (pick any number you like, but be reasonable: it’s the distance between bases).

private final int BASE_MARGIN = 2;

 

Now add this to getBaseScreenX and getBaseScreenY equations. We want it to affect the distance between every tile, that’s why we’re putting it in the TILE_SIZE part (which essentially means the distance between tiles).

public float getBaseScreenX(int baseX)
{
    return measuredStage.getWidth() / 2 - (resources.TILE_SIZE + BASE_MARGIN)
            * (1 + maxTileBaseX - baseX);
}

Now do the same with getBaseScreenY:

public float getBaseScreenY(int baseY)
{
    return measuredStage.getHeight() / 2 - (resources.TILE_SIZE + BASE_MARGIN)
            * ((maxTileBaseY + 1) / 2 - baseY);
}

 

Run the game again.

Tiles, distanced

Tiles, distanced

Looks much better! We can clearly distinguish the separate walkable bases. Let’s just draw the player somewhere. We’re actually going to rework that part, but I want us to feel good about ourselves. Time for instant gratification!

In our drawBases function, right before batch.end() call, add the following line:

batch.draw(game.res.player,
        sizeEvaluator.getBaseScreenX(1),
        sizeEvaluator.getBaseScreenY(1) + 2);

 

Run the game.

tiles_v3

Oh no! The dreaded ‘cage’ effect, our player looks stuck between tiles. Obviously, this distance won’t do. Let’s reduce the distance between bases by 1/3. Rewrite getBaseScreenY function:

public float getBaseScreenY(int baseY)
{
    return measuredStage.getHeight() / 2
            - ((resources.TILE_SIZE * 2) / 3 + BASE_MARGIN)
            * ((maxTileBaseY + 1) / 2 - baseY);
}

basically, we’re multiplying TILE_SIZE by two thirds, essentially reducing the distance by 66%. Save the code and run it now.

tiles_v4

It does look better, looks more like our hero is standing on one of the bases instead of being stuck between two levels.

Good job, now you have a solid tiles positioning. In the next lesson, I’ll actually address the player movement.

Relevant git commit: https://github.com/vladimirslav/dodginghero/commit/3de79268966817849163eecbc538ba804fa7afd1

Developing Multiplatform Game With Libgdx – part 3 – tile background

Going Further: Background Implementation

So, Vladimir, are we finally ready to actually do something now? Damn right we are! In this lesson, let’s make a background and give our game the pixel-artsy feel. Go to our new amazing GameScreen, and add a new declaration at the start of the class:

private Stage gameStage;

In Libgdx, The stage is used to position UI elements in a nice way and read incoming UI events. It makes our job much easier.

We will initialize it with a new ViewPort. ViewPort limits the area of the screen which we are able to see. Since our game will be run on multiple devices with different screens, we need to make sure that every player will be able to play it, no matter his screen size. Libgdx offers lots of different viewports (you can read more about it here: https://github.com/libgdx/libgdx/wiki/Viewports). We’re going to use FixViewport, this will allows us to make sure that width/height proportions are kept, the main characters are going to be drawn in the middle and the background (which will be made from tiles) is going to be extended depending on the actual size of the screen.

First, let’s introduce a constant values for game width and height (these will be the minimal field width / height and will be extended according to window proportions). Since our tiles are really small, I’m choosing 192 for width and 128 for height (basically, 192 / 16 = 12 tile width, 128 / 16 = 8 tile height.).

public final int STAGE_W = 192;
public final int STAGE_H = 128;

Then, in our GameStage contructor, let’s initialize our gameStage, by passing our spriteBatch and new Viewport as parameters.

The whole part of code will look like this now:

public final int STAGE_W = 192;
public final int STAGE_H = 128;

public GameScreen(DodgingHero _game) {
    super(_game);
    batch = new SpriteBatch();

    ExtendViewport viewp = new ExtendViewport(STAGE_W, STAGE_H);
    gameStage = new Stage(viewp, batch);
}

 

Basically, we tell our gameStage to use our new extendviewport and to scale the screen accordingly. No matter the window size, the coordinates on the screen are always going to be at least STAGE_W x STAGE_H (192×128 in this case).

Splitting game update logics from drawing

Now, let’s split the screen update part from the actual drawing part. Make a new function update:

private void update(float delta)
{
    gameStage.act(delta);
}

The function updates the contents of the stage. We don’t really have any right now, but it will be useful later on. Delta is the time that passed since the rendering of the previous frame. If our stage has any animations, it basically adjusts them accordingly.

Now, let’s add a call of our update function inside the render function. After the screen is cleared (glClear command), issue a draw command to our stage. It will apply the stages camera settings and our further drawing will be done in the coordinates system we requested during constructor call.

Change the values in glClearColor to 0, 0, 0, 1 (Red, Green, Blue, Alpha). This will set the clear color to black, away from annoying red.

@Override
public void render (float delta) {
    update(delta);

    Gdx.gl.glClearColor(0, 0, 0, 1);
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

    gameStage.draw();

    gameStage.getBatch().begin();
    gameStage.getBatch().draw(game.res.ground, 0, 0);
    gameStage.getBatch().draw(game.res.wall, 0, 16);
    gameStage.getBatch().end();
}

Finally, in dispose() function, call gameStage.dispose(). The gameStage takes up resources that needs to be manually disposed of when we exit our game. Add super.dispose() call, which is a call to parent’s (DefaultScreen) dispose method (nothing’s there yet, but it’s a good idea to do it nonetheless in case we change something in the future).

@Override
public void dispose()
{
    super.dispose();
    gameStage.dispose();
    batch.dispose();
}

Run the game. You should see something like this:

How Tiles Look At First

How Tiles Look At First

As you see, the tiles have increased in size! However, if you try resizing the window, the image stretches uncomfortably.

Uncomfortable Resize

Uncomfortable Resize

This is because our viewport is not being updated. In GameScreen, add resize function:

@Override
public void resize(int w, int h) {
    super.resize(w, h);
    gameStage.getViewport().update(w, h, true);
}

This one is called on every resize, effectively resizing our viewport on every change of window size.

Proper Resize

Proper Resize

The image is smaller, but dimensions are at least kept (and not stretched).

Filling the background with tiles

Finally, let us get to the background. In our main game package, create a new one called “graph” In my case, it is com.coldwild.dodginghero.graph

Inside that package, create a new Java Class, named “Background.” Create an empty public constructor (we’ll need it in later lessons).

Then, get to the drawing function. The current goal is quite simple: make a floor background with a wall behind. Let’s introduce the draw method, which will draw the background on the given Stage using Resource pack that we’ve loaded.

public void draw(Stage stage, Resources res){}

Make sure to include the proper stage package (alt+enter when your cursor is on Stage, then select the Stage from libgdx).

The current background idea is simple: fill the top row of the screen with wall tiles and the other parts with floor tiles. We’ll need two for cycles for that. One that goes from top to bottom and the other one inside it that goes from left side to right side. But first, go to Resources and introduce new constant TILE_SIZE (which, obviously, indicates our file size). We will use it for background drawing.

Get back to Background class file. Let’s raw the ground first, and then we’re going to worry about the walls.

public void draw(Stage stage, Resources res)
{
    stage.getBatch().begin();

    for (int y = (int)stage.getHeight(); y >= -Resources.TILE_SIZE; y -= Resources.TILE_SIZE)
    {
        for (int x = 0; x < stage.getWidth(); x += Resources.TILE_SIZE)
        {
            stage.getBatch().draw(res.ground,
                    x, y,
                    0, 0,
                    Resources.TILE_SIZE, Resources.TILE_SIZE,
                    1.01f, 1.01f,
                    0);
        }
    }
    stage.getBatch().end();
}

We’re going from the height of the stage (stage.getHeight) to the lower border and filling every row with tiles. Hold on a second, why are we using such a complicated draw procedure? We’re doing it because otherwise the images will experience tearing on stage resize. (If you figure out a better way – let me know, seriously).

Uncool black lines (poor resize result)

Uncool black lines (poor resize result)

Now, let’s add the Background declaration and initialization to our GameScreen. Add the line    Background bg; near the other declarations. In GameScreen constructor, add bg initialization:

bg = new Background();

 

And finally, we must make a call to bg.draw during our rendering. Add it before gameStage.draw() call. bg.draw(gameStage, game.res);

Run the game. The screen should be filled with floor tiles now. The only thing left now is to add wall tiles.

Right before stage.getBatch().end();, add another for cycle:

for (int x = 0; x < stage.getWidth(); x += Resources.TILE_SIZE)
{
    stage.getBatch().draw(res.wall, x, stage.getHeight() - Resources.TILE_SIZE, 0, 0, Resources.TILE_SIZE, Resources.TILE_SIZE, 1.01f, 1.01f, 0);
}

 

Run the game, see the background that stretches and handles any screen size well. Congratulations! Your background is complete.

Git Commit with code changes from this lesson: https://github.com/vladimirslav/dodginghero/commit/b15fa65097d8d1366955000e3b9dac594a6085cc

More on viewports:
https://github.com/libgdx/libgdx/wiki/Viewports

Developing Multiplatform Game With Libgdx – part 2 – project structure

Heya! The next lesson adresses the project structure and introduces asset packing. The video turned out to be a bit confusing, but the extended transcript is below. Let me know if you have any questions!

What is a game and how we build it

If we think about it, what is a game? Let’s check out the main file, DodgingHero. If we really dumb it down, the game is a cycle of displaying info and getting user’s feedback. The game screen actually renews many times per second (FPS, frames per second, actually indicate this exact value). The render() function in Libgdx is doing exactly this. It cleans the screen (glClear command), then it draws our own things.

The create() function initializes our game. Those are the operations that need to be done once. Mainly, it’s resource allocation. We need to load the textures/sprites only once (the line img = new Texture(“badlogic.jpg”); does exactly that). We also initialize SpriteBatch (batch = new SpriteBatch();), essentially this is the structure that sends the commands to our graphic card.

On the opposite side, we have dispose() function, that is called after our game is done running. We need to free up resources. And this is exactly happens in this particular case: both batch and img are being disposed.

Now that we have a very general idea what’s going on, I’m going to tell you how I usually structure my projects. A good project structure ensures that project maintenance and code updates will go much smoother. As they say, “hours of planning can save you weeks of programming.”

Preparing our art

First thing first, let’s find some art for our prototype. I usually use opengameart if I want to build something fast, and this case will be no exception. After some search, I found http://opengameart.org/content/tiny-16-basic – tileset with some monsters and humans which we can use for our game prototype. I’m going to pick the tiles they have, pick two of them and will use them to show our initial background, repeated the tiles for the background. Essentially I’m doing some extra work here (because the tilesets on the link are already very neatly organized), but I need to show you what’s going on by an example.

In our project root folder (the same folder where we have “android”, “core”, “html” … folders) , let’s make a folder named “assets.” Inside that folder, make another folder, named “unpacked.” From the tileset, cut out one floor and one wall tile (I usually use paint.net, http://www.getpaint.net/index.html for that purpose, but the simple ‘paint’ will do for now).Each image should be 16×16 pixels in size, save them as ground.png and wall.png accordingly. Now, we have two tile sprites, but what do we do with them? For a better performance, all sprites should be put into spritesheets. (It takes time for the graphic card to switch between textures/sprites). It’s really not a problem for modern computers most of the time (for a small game), but I’d rather teach you to do things the ‘proper’ way first. In our Android Studio, go to desktop package and open DesktopLauncher. For Desktop version, we’re going to add texture packing. (Whenever we run desktop version, the sprites are going to be packed into one file. We’ll be able to use this this pre-generated file in the other platforms, like android). The main reason I’m doing it this way is because TexturePacker is not supported by some of our platforms (at least HTML), so I’d rather execute it on Desktop only.

In DesktopLauncher class, add the following private function:

static void Pack()
{
      TexturePacker.Settings settings = new TexturePacker.Settings();
      settings.maxHeight = 2048;
      settings.maxWidth = 2048;
      settings.pot = true;
      TexturePacker.process(settings, "../../assets/unpacked", "packed", "game");
}

Then, right at the start of main() function, add the call to Pack() function, it will look like this now:

public static void main (String[] arg) {
   Pack();
   LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
   new LwjglApplication(new DodgingHero(), config);
}

Loading the art

Select the DesktopLauncher configuration and run our program on it (first launch might take some time). Now go and check the android\assets\ folder. There should be a new directory, “packed” there. Inside that directory, there should be two files: game.png and game.atlas. First one is the picture of two tiles put together. The other one is actually a text file (you can check it out with any text editor, I use Notepad++). It is, like extensions says, an atlas, that describes different sprites in it and coordinates of those sprites in our new big picture.

Now we’ll have to load this atlas. The next thing we do, make a resource class, which will hold the graphical data. Make it in the same package as your main game class. In my case, I right click on “com.coldwild.dodginghero”, choose “New” and pick “Java Class.”

create-new

New Class Creation

Name it “Resources.” This one will be responsible for loading and storing the assets for our game. Go to your new file, and inside Resources class declare the public constructor and TextureAtlas variable gameSprites.

public class Resources {

    TextureAtlas gameSprites;

    public TextureRegion ground;
    public TextureRegion wall;

    public Resources()
    {
        gameSprites = new TextureAtlas(Gdx.files.internal("packed/game.atlas"));
        ground = gameSprites.findRegion("ground");
        wall = gameSprites.findRegion("wall");
    }

    public void dispose()
    {
        gameSprites.dispose();
    }
}

Before constructor declaration, we declare TextureAtlas named “gameSprites”– this is the thing that is going to store our spritesheet  with game characters and background.

dispose() function will be called after the end of our program, to unload all resources that have been used.

We put the initialization of the gameSprites into our constructor with the following line:

    gameSprites = new TextureAtlas(Gdx.files.internal("packed/game.atlas"));

This will take the generated atlas from ourproject/android/assets/packed/ folder. After that, declare two TextureRegion variables, ground and wall right after gameSprites atlas.

TextureAtlas gameSprites;

public TextureRegion ground;
public TextureRegion wall;

 

Great, now let’s assign some values in our constructor. It’s not hard: just add the following lines:

ground = gameSprites.findRegion("ground");
 wall = gameSprites.findRegion("wall");

 

The final result should look like this:

public class Resources {
 
     TextureAtlas gameSprites;
 
     public TextureRegion ground;
     public TextureRegion wall;
 
     public Resources()
     {
         gameSprites = new TextureAtlas(Gdx.files.internal("packed/game.atlas"));
         ground = gameSprites.findRegion("ground");
         wall = gameSprites.findRegion("wall");
     }
 
     public void dispose()
     {
         gameSprites.dispose();
     }
 }

 

Testing what we have

Now ground and wall point to the specific tiles and we’re be able to draw them! Now go to your main class file (in my case it’s DodgingHero.java) in core folder and add new public variable, Resources right at the start of the file. You should initialize it at the start of create() function. Remove the “img” variable and all code related to it from the file. You won’t need it anymore. Let’s just test if we can draw our simple tiles. In dispose function, add res.dispose(); Final result should look like this:

  
 public class DodgingHero extends ApplicationAdapter {
     public Resources res;
     SpriteBatch batch;
 
     @Override
     public void create () {
         res = new Resources();
         batch = new SpriteBatch();
     }
 
     @Override
     public void render () {
         Gdx.gl.glClearColor(1, 0, 0, 1);
         Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
         batch.begin();
         batch.end();
     }
 
     @Override
     public void dispose () {
         batch.dispose();
     }
 }

Now, for the sake of testing our tiles, let’s add simple drawing between batch.begin() and batch.end() inside our render() function:

@Override
 public void render () {
     Gdx.gl.glClearColor(1, 0, 0, 1);
     Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
     batch.begin();
     batch.draw(res.ground, 0, 0);
     batch.draw(res.wall, 0, 16);
     batch.end();
 }

This way we should draw the wall above the ground. The first parameter is the sprite that we want to draw, the next is the coordinates(x, y). Unless you change something, the x coordinates are going from left side of the screen to right side (0->width) and y is going from bottom to top (0->height). 0 means the lowest point of the window. Run the program and you should see both small tiles drawn at the left side of the screen:

gamescreen

Our work in progress!

Adjusting Code Structure

So, Vladimir, are we ready to build the game right here? Hell no. It might seem like a good idea to write all code here, but it will quickly become bloated if nothing is done: imagine that we have to program all the menus and screens in one file. The way I usually do it is to split every separate screen into a separate file and do the rendering/control checking there. The good news is that libgdx allows you to do this quite easily.

In our core/java/com.yourname.gamename package, create a new package called “screens.” There, we’ll add the DefaultScreen parent class, which will store the link to our game object (and will be able to access our resources from there), from which we’ll inherit the next screens.

newpkg

New Package Creation

Right click on the “screens”, package select “new” -> “Java Class.” Name it DefaultScreen, make it implement Screen (public class DefaultScreen implements Screen), add the necessary import from Libgdx by placing map cursor over “Screen” and pressing alt+enter. Press alt-enter again to automatically implement the missing methods. Don’t touch them. (for now). Now, we’ll do two things:

  • Declare a variable of our main class (it will point out to game)
  • Create a constructor for DefaultScreen

That should not take much time:

public class DefaultScreen implements Screen {
 
     public DodgingHero game;
 
     public DefaultScreen(DodgingHero _game)
     {
         game = _game;
     }

 

Very good, our DefaultScreen class is ready.

Now we should implement the actual game screen. Right click on screens package, add new Java Class, let’s name it GameScreen. GameScreen should extend the Default Screen. (public class GameScreen extends DefaultScreen). Press alt+enter to implement the default constructor. Your class should look like this:

public class GameScreen extends DefaultScreen {
     public GameScreen(DodgingHero _game) {
         super(_game);
     }
 }

 

Now go to your main class (in my case it’s DodgingHero), and blatantly cut-paste render() function from there to GameScreen. Change the input parameters to accept float delta (public void render (float delta)). We now have this:

 

public class GameScreen extends DefaultScreen {
     public GameScreen(DodgingHero _game) {
         super(_game);
     }
 
     @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(res.ground, 0, 0);
         batch.draw(res.wall, 0, 16);
         batch.end();
     }
 }

batch is unresolvable. We need to move it from main class here. Main class should be very small now:

 public class DodgingHero extends ApplicationAdapter {
     public Resources res;
 
     @Override
     public void create () {
         res = new Resources();
     }
 
     @Override
     public void dispose () {
         res.dispose();
     }
 }

 

The last thing to do is to change batch.draw calls. We don’t have res variable here, but we can access it via our game variable. Change batch.draw(res.ground, 0, 0); to batch.draw(game.res.ground, 0, 0); Do the same change with wall. The final GameScreen class should look like this:

public class GameScreen extends DefaultScreen {
 
     SpriteBatch batch;
 
     public GameScreen(DodgingHero _game) {
         super(_game);
         batch = new SpriteBatch();
     }
 
     @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(game.res.ground, 0, 0);
         batch.draw(game.res.wall, 0, 16);
         batch.end();
     }
 
     @Override
     public void dispose()
     {
         batch.dispose();
     }
 }

Run the game now. Oh no! What do we have in front of us? It’s a black screen! Something went wrong. No worries, the issue is that we moved the code to the separate screen, but did not instantiate it in any way. We need to tell our game to explicitly switch to it. Go back to our main class, and do two things:

  • Change class declaration: public class DodgingHero extends ApplicationAdapter { should change to public class DodgingHero extends Game {
  • At the end of create() function add the following line: setScreen(new GameScreen(this)); Press alt+enter to resolve and auto-import GameScreen class

Run the game now. You should see the red screen with two tiles at the bottom. It might not look as much, but our project has changed a lot. After this, we’ll be able to independently work on specific screens, and adding new screens (like main menu, credits, etc) won’t be a problem.

 

Git Commit: https://github.com/vladimirslav/dodginghero/commit/d02228f6ecb39fdbf98bb4ad54e57b5fb8f29b73

I’ve taken assets from: http://opengameart.org/content/tiny-16-basic, they are made by Lanea Zimmerman. I’ve cut separate tiles so it would be easier to follow (see my git repo).

You might want to check https://libgdx.badlogicgames.com/documentation.html for more details and in-depth descriptions.