Tag Archives: gamedev

Developing Multiplatform Game with LibGDX, part 16: fade in, fade out and progression

Lesson 16: screen transition

Now that we have transactions, let’s switch to gameplay enhancements: first thing – move to next screens on victory.

In GameLogic class, create a new interface.

public interface GameEventListener
{
    void OnGameEnd(boolean playerWon);
}

Modify GameLogic to take the listener as one of the constructor’s parameters:

GameEventListener eventListener;

public GameLogic(DodgingHero _game, GameEventListener _listener)
{
    eventListener = _listener;

We should notify our victory listener at the same time when we mark our player as victorious.

In our AssignPlayerPosition, where we check bonus pickups and mark player victorious on killing enemy, add a new line:

if (enemy.getLives() <= 0)
{
    player.markVictorious();
    eventListener.OnGameEnd(true); // added this line!
}

It will notify the player about the end of the game. Now, make GameScreen implement the said listener.

public class GameScreen extends DefaultScreen implements InputProcessor, GameLogic.GameEventListener {

As usual, press Alt+Enter to implement the missing method.

Now, what do we want to do on GameEnd? We need a smooth fadeout, and then a progression to next level. Well, a restart for now. It is going to be a progression in the next lesson though. Since we are using sprites (and not gamestag eactors), we won’t be able to use libgdx in-built fadeout action. No worries, it’s not hard to implement ourselves. In advance, make two static constants at the beginning of the GameScreen class:

public static final float GAME_END_FADEOUT = 0.5f;
public static final float GAME_START_FADEIN = 0.25f;

Then, get to working on GameEnd function call.

@Override
public void OnGameEnd(boolean playerWon) {
    gameStage.addAction(Actions.sequence(
            new Action() {
                float t = 0;
                @Override
                public boolean act(float delta) {
                    t += delta;
                    float tempt = t / GAME_END_FADEOUT;
                    tempt *= tempt;
                    batch.setColor(1, 1, 1, 1 - tempt);
                    return t >= GAME_END_FADEOUT;
                }
            },
            new Action() {
                @Override
                public boolean act(float delta) {
                    dispose();
                    game.setScreen(new GameScreen(game));
                    return true;
                }
            }
    ));
}

We’ll ignore playerWon Boolean for now (we’ll implement another transaction on loss). Now, what do we want to do on GameEnd? We create a sequence of actions. First is essentially a fade-out timer, we fade the screen out for GAME_END_FADEOUT seconds (0.5 in this case), then we return true, which indicates that that time has been spent. And then actually we create a new GameScreen that restarts the game.

Now, to get further with our implementation, in our logic initialization, add the line:

logic = new GameLogic(game, this);

Now, that we’ve added a smooth fadeout, we should actually add a smooth fadein. In our GameScreen initialization, add a new action to our freshly initialized stage (at the end of GameScreen constructor).

gameStage.addAction(new Action() {
    float t = 0;
    @Override
    public boolean act(float delta) {
        t += delta;
        float tempT = t / GAME_START_FADEIN;
        tempT *= tempT;
        if (tempT > 1.0f)
        {
            tempT = 1.0f;
        }

        batch.setColor(1, 1, 1, tempT);
        return t >= GAME_START_FADEIN;
    }
});

It’s the same quadratic fadein as you’ve seen before: take the time, add the delta, divide by expected time for fadein (0.25) in our case, thus normalizing the value to 0..1. After that, square it. Great! We got a new transparency.

Finally, if the time has passed the necessary fadein time, return true (thus ending the action). Run the game. As you see when screens actually fadeIn, you have a quick flicker (if you look closer: it’s a scaled-down version of your screen). To prevent this, we need to update a gameStage camera before drawing frame one. At the end of GameStage constructor, add:

gameStage.getCamera().update();
batch.setProjectionMatrix(gameStage.getCamera().combined);

Final thing. By now you’ve probably noticed that enemy attacks are very easy to evade. Decrease WARNING_TIME from 0.75 to 0.5f. Try the game out. FadeIns/Fadeouts should be smooth now.

Relevant git commit:

https://github.com/vladimirslav/dodginghero/commit/1a2fb0896293a80e1b12ae4fc60ee73167cdcabf

Developing Multiplatform Game with LibGDX, part 15: smooth leave on victory

Player Leaving On Victory

Now that we’ve implemented a smooth approach- let’s work on smooth leave. Whenever player wins a battle – let’s make him move to the right side of the screen (to make an illussion of leaving and progressing further).

In our Player.java, add two variables:

private boolean winning = false;
private float winTime = 0;

Now, we need to add an extra condition to our draw:

if (timeAlive < APPROACH_TIME)
{
    float t = timeAlive / APPROACH_TIME; // 0..1
    t = t * t;
    setPosition(
            t * sizeEvaluator.getBaseScreenX(fieldX),
            sizeEvaluator.getBaseScreenY(fieldY));
}
else if (winning)
{
    float t = 1;
    if (timeAlive - winTime < APPROACH_TIME)
    {
        t = (timeAlive - winTime) / APPROACH_TIME; // 0..1
        t = t * t;
    }
    float fx = sizeEvaluator.getBaseScreenX(fieldX);
    setPosition(
            fx + t * (sizeEvaluator.getRightSideX() - fx),
            sizeEvaluator.getBaseScreenY(fieldY));
}
else
{
    setPosition(sizeEvaluator.getBaseScreenX(fieldX),
            sizeEvaluator.getBaseScreenY(fieldY));
}

As you see, we need to know the right side of the screen. In our SizeEvaluator, let’s add methods setRightSideX and getRightSideX. Since the width of our window (and therefore screen) can change, we need to keep it updated. SizeEvaluator constructor should also accept right side x.

private float rightSideX;

public SizeEvaluator(Stage _stage, Resources _res, int maxBaseX, int maxBaseY, float _rightSideX)
{
    measuredStage = _stage;
    resources = _res;
    maxTileBaseX = maxBaseX;
    maxTileBaseY = maxBaseY;
    rightSideX = _rightSideX;
}

public void setRightSideX(float value)
{
    rightSideX = value;
}

public float getRightSideX() {
    return rightSideX;
}

Now adjust GameScreen constructor to pass gameStage.getWidth() as the last parameter into SizeEvaluator constructor.

sizeEvaluator = new SizeEvaluator(gameStage,
        game.res,
        GameLogic.MAX_BASE_X,
        GameLogic.MAX_BASE_Y,
        gameStage.getWidth());

Then, we have to take care that sizeEvaluator adjusts properly on screen resize. In our GameScreen’s resize method, add the call to sizeEvaluator’s setRightSideX function.

@Override
public void resize(int width, int height)
{
    super.resize(width, height);
    gameStage.getViewport().update(width, height, true);
    sizeEvaluator.setRightSideX(gameStage.getWidth());
}

One more thing: we need to tell our player class that the game is won. There’s also a problem now: the player’s time is updated inside our logic calls. But we’re not updating the logic if the game has ended (enemy or player died). Time for some refactoring! Change GameScreen’s update function from:

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

To:

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

And adjust GameLogic accordingly:

public void update(float delta)
{
    gameTime += delta;
    player.update(delta);

    if (player.getLives() > 0 && enemy.getLives() > 0) {
        effectEngine.update(delta);
        enemy.update(delta);

        if (lastBonusSpawn + BONUS_SPAWN_INTERVAL < gameTime &&
                bonuses.size() < MAX_BONUSES_ON_FIELD) {
            SpawnRandomBonus();
        }
    }
}

Player is always updated, but all the other stuff gets updated only if both player and enemy are alive. Now the only thing is left is to tell our player object that we won! In our Player class, add new function:


public void markVictorious()
{
    winTime = timeAlive;
    winning = true;
}

Then, in our gamelogic, when we damage the enemy, we have to check enemy health. If it equals zero – tell the player that we won! Adjust the part of code with attack bonus pickup:

else if (currentBonus.getBonusType() == Bonus.BONUS_TYPE_ATTACK)
{
    enemy.takeDamage(1);
    if (enemy.getLives() <= 0)
    {
        player.markVictorious();
    }
}

Great! Now our player leaves after the game ends, marking the continuation of the journey. Looks much better!

Frequent Flyer: Promotion Stats so Far

As development of Frequent Flyer is coming to an end, we’re actively preparing to take part in GameOn convention in Lithuania, so I think it’s a good idea to show some stats on publicity and my observations so far.

pleasantscentedfennecfox-size_restricted

Facebook, Google Adwords, Twitter

First, let’s clear up some lightweight and most obvious stuff that comes to mind when someone mentions marketing: facebook ads and google adwords. In my case, both of those did not work.

Adwords: high CPI, impossible to beat A+ games. Offered $0.5 for install in US. I know, it’s not much. Offered $0.3 for install in Pakistan (you know, for science) and still got nothing.

Facebook Ads:

Not really related, but spent $10 on Unblocking Puzzle Link (http://coldwild.com/unblocking/) promo. Targeted olderpeople in US/UK who like puzzles: 182 likes, 22 link visits, 1000 shows. I don’t consider it a good result for $10 spent, especially since the link was leading directly to the game. Likes != visits.

Regarding Frequent Flyer: tried using facebook ad campaign to promote steam greenlight link. Even worse. One like, zero visits, stopped it as amount spent got to $3. Audience: retro shmup fans in US/UK (again).

For the future projects: not going to use it much (unless I want to boost a game that already had some traction). OK, now for the good parts.

Some thoughts about twitter (not backed up by hard facts): seems like a huge circlejerk sometimes. In my case, I often see  developers following developers. If you have a real player follow: be happy about it, value it like a 100 business followers. Coldwild Games has ~1000 followers and almost no conversions (direct link to web version of FF got about 40 views).

Forums

I’ve made a post on reddir.com/r/gamedev almost as soon as I had greenlight on. Not much publicity, but good feedback from players, lots of which I’ve tried to implement. The greenlight really came early, the game was much less polished and it’s no surprise it did not take off.

In the thread, I’ve got comments from Bigosaur with link to his blog and his game promoting experience (http://bigosaur.com/blog/27-marketing-android-game) <- if you are starting out, check this out, very informative and decided that I’m going to do the same.

Overall, I’ve posted on six forums:

Java-gaming.org, LibGDX forums (badlogicgames.com), TigSource, Gamedev.ru, Gamedev.net, shmups.system11.org. Apart from gamedev.ru (does not give any stats), here’s how everything turned out regarding views and discussion:

Forum Thread Views

Forum Thread Views

Java-Gaming turned out to be the most popular one. I understand that this is very specific (especially since I’m developing on Java/Libgdx), but you can try to get similar results on Unity forums.

Here’s how I fared reply-wise (including my posts, you can substract 12 from every reply). Not many people were replying, but again, java-gaming turned out to be a popular one.

Forum Replies

Forum Replies

Right, so I actually got more replies/suggestions/bug reports on java-gaming too. Tigsource, despite smaller amount of views, got me a good amount of people replying and being interested.

Important note: you have to write all the time. At first I had maybe 100 views on most of the forums, but as I kept putting the progress, I started getting replies and increasing amount of views. It does not take much time, you get used to it (use the similar forum posts with similar BBCode that links to your awesome images and gifs). If you are starting out a serious project – I suggest you publish your devlogs on the forums. This thing helps, really and in the end each devlong won’t take more than an hour of your time (you can do it once or twice a week).

Making a Web Version

Forums are one thing. But what about actually putting your game out there? Since I was developing in LibGDX, it was no trouble to prepare a web version and put it online. Which I did. Here: http://coldwild.com/flyer/ Plot twist? Nobody cares. And technically, why should they? Why would anyone, who can visit kongregate / itch.io / newgrounds / etc, go to your website to play this one single game?

So, the time has come to spread the word further. I’ve put the game on four networks: itch.io / newgrounds / kongregate / gamejolt.

itch.io turned out to have worst performance for me. Maybe it’s the logo or the page looks ugly, dunno. Total, in two months, I had fourty views (I’m pretty sure half of those is me checking if the game works after I update the version).

kongregate is easiest of them all: http://www.kongregate.com/games/comrad_gremlin/frequent-flyer/, 120 plays. You have some views when you just publish (they show it somewhere I think), but after that: silence. On the bright side: you can embed your website into their player, so I’ve simply put the link to my coldwild.com/flyer and forgot about it. No more reuploads, the version is updated automatically as soon as I upload a new one to my hosting!

newgrounds turned out better: http://www.newgrounds.com/portal/view/680363  ~700 plays so far, and the number gradually keeps going up. No complaints. But you need to reupload/review the game every time you have an update.

So, essentially, The more places you put your game, the higher you maintenance time cost. (obviously). I regret putting html version of my game on itch.io (~40 views in 2 months), because itch.io does not allow embedding your html simply and seems to not give much organic views (maybe I did something wrong?).

Gamejolt deserves a separate word. I’ve put the game there and in first few weeks was getting results similar to itch.io. I’ve kept updating it regularily and writing my devlog. On about fourth week, the game got featured on front page. This got me: 5.3k Views, 1.4k Plays, 75 Ratings, with average score 3.7867 out of 5.

I was not ready for this. Only when my highlight time was coming to an end, I’ve figured out that I should add a popup that asked players to vote. Got maybe 10 greenlight votes after I made a simple popup (shown to returning players asking them to vote).

Here’s a total amount of times my game has been played online:

Game Plays Online

Game Plays Online

It might not seem much, but it’s much more than my previous games, so I’m really happy about that. I was also happy when I got the messages  from some players that they’ve unlocked all the planes. Feelsgoodman.

Sad Part: The Greenlight

The game looks much better now, but the momentum has been lost.

Frequent Flyer: before

Frequent Flyer: before

FF: Now

FF: Now

The Greenlight Marketing tips seem obvious, but they are spot on. Put the best possible version out there, polish one level instead of making 20 (my mistake: was trying to make all enemies/player planes first). Make the game look as good as possible. You get the initial spotlight from Valve, but after that it becomes increasingly complicated. I got maybe 20 votes from web version, but here’s how Greenlight looks now:

greenlight

As you can see, not much hope is left 🙂 I’m definitely going to show the game off at GameOn and see if I can get some good feedback and traction. If not – I’m going to consider this a lesson learned, will employ the promo things that I’ve learned (forums / web games).

Summary

  • Post devlogs on Forums regularly, as soon as you have a simple prototype to show (make sure to update the first post to indicate the biggest advancements)
  • For web version in my case, best results came from: gamejolt > newgrounds > kongregate
  • Greenlight: put it out there only when you have a decent version to show. No surprise, getting votes after the launch is much more complicated.

In case you want to support me with the vote, here’s the link: http://steamcommunity.com/sharedfiles/filedetails/?id=744697163

 

Developing Multiplatform Game with LibGDX, part 14: screenshake and appearance

Screenshake

In our previous lesson, we made our character blink on hit. That feels better, but it’s not enough. Let’s make our screen shake to indicate how hurt the player is!

In our Character class, let’s make a public function that returns last time our character has been hurt:

public float getTimeOfDmgTaken()
{
    return timeOfDmgTaken;
}

We also need one getter to see time alive:

public float getTimeAlive()
{
    return timeAlive;
}

In our GameScreen.java, define two new constants:

private static final float SHAKE_TIME_ON_DMG = 0.3f;
private static final float SHAKE_DIST = 4.0f;

One is responsibe on how long the shake is going to happen, the other one for the intensity of shake (Shake Distance, amplitude). After we do that – we’re ready to start screenshake process! In our GameScreen render function, before DrawUi() call, we have to add the following lines:

gameStage.getCamera().position.set(gameStage.getWidth() / 2, gameStage.getHeight() / 2, 0);
if (player.getLives() > 0 &&
    player.getTimeAlive() - player.getTimeOfDmgTaken() < SHAKE_TIME_ON_DMG)

    gameStage.getCamera().translate(-(SHAKE_DIST/2) + MathUtils.random(SHAKE_DIST),
            -(SHAKE_DIST / 2) + MathUtils.random(SHAKE_DIST), 0);
}
gameStage.getCamera().update();

The sequence:

  1. Reset camera position
  2. If player has recently taken damage, move the camera. Note that player must be alive (otherwise we’ll get an infinite shake)
  3. Apply our camera movement

Run the game and take damage! The screen should be shaking after receiving damage now 🙂

Appearance at the start of the game

Right now our player and enemy both appear at the fixed position at the start of the game. Let’s make them appear from the side of the screen and then take the initial positions.

In our Player class, make a separate constant,

private static final float APPROACH_TIME = 0.5f;

Then, modify our draw method call.

public void draw(SpriteBatch batch, SizeEvaluator sizeEval)
{
    preDraw();

    if (timeAlive < APPROACH_TIME)
    {
        float t = timeAlive / APPROACH_TIME;
        setPosition((t * t) * sizeEval.getBaseScreenX(fieldX), sizeEval.getBaseScreenY(fieldY));
    }
    else
    {
        setPosition(sizeEval.getBaseScreenX(fieldX), sizeEval.getBaseScreenY(fieldY));
    }
    super.draw(batch);

    postDraw();
}

To explain: in the first 0.5 seconds, the player is going to seemingly move from left to right side.

Let’s do something different for our enemy. Why not make him increase in size (starting from very small)?

Modify our Enemy class, declare a constant:

private static float SCALE_TIME = 0.5f;

And modify our Enemy.draw method:

public void draw(SpriteBatch batch, SizeEvaluator sizeEval)
{
    preDraw();
    setPosition(sizeEval.getEnemyX(this), sizeEval.getEnemyY(this));
    if (timeAlive < SCALE_TIME)
    {
        float t = timeAlive / SCALE_TIME;
        t = t * t;
        setScale(t);
    }
    else
    {
        setScale(1);
    }
    super.draw(batch);
    postDraw();
}

Finally, make a small bugfix to prevent spawning on already existing places: in our gamelogic.java, in SpawnRandomBonus function, change for cycle from

for (int i = 0; i < bonuses.size() && targetNonEmpty; i++)

to

for (int i = 0; i < bonuses.size() && (targetNonEmpty == false); i++)

Run the game. You can see that both player and spider smoothly appear on the screen now.

Relevant Github commit: https://github.com/vladimirslav/dodginghero/commit/ab9d4e938bf8dde615bacbccdc87005672770ebc

Developing Multiplatform Game with LibGDX, part 13: blinking on damage

Adding Damage Effects

Right now, the only way to tell that the player or enemy has been damaged is by checking their hp. This is not very observable. Let’s try to implement basic blinking on player / enemy damage.

We are starting to notice more and more similarities between our enemy and a player. Let’s refactor this a bit and move common functions into an abstract parent class. In our logic/objects package, create a new abstract class, Character. It should inherit from Sprite. Let’s move the similar functions from both player and enemy into the character class.

We need to move following stuff from Player/Enemy to our Character Class:

Variable lives. Also, make this variable protected. Other stuff:

Create a Character constructor, that takes “lives” as a parameter; since both enemy and player have lives – it’s a good idea to move this variable into parent class.

TakeDamage function works the same way for player and enemy. getLives apply to both. That’s it. For now. Here’s the final look of all 3 classes.

Character.java:

public class Character extends Sprite {

    protected int lives;

    public Character(int _lives)
    {
        lives = _lives;
    }


    public int getLives()
    {
        return lives;
    }


    public void takeDamage(int amount) {
        lives -= amount;
        if (lives < 0)
        {
            lives = 0;
        }
    }
}

Player.java:

public class Player extends Character {

    private final int max_lives;
    protected int fieldX;
    protected int fieldY;

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

    public int getFieldX() {
        return fieldX;
    }

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

    public int getFieldY() {
        return fieldY;
    }

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

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

    public void addLives(int amount) {
        lives += amount;
        if (lives > max_lives)
        {
            lives = max_lives;
        }
    }
}

Enemy.java:

public class Enemy extends Character {

    private static final float BASE_ATTACK_TIME = 3.0f;
    private static final int DEFAULT_ENEMY_LIVES = 10;
    private float timeSinceAttack;
    private float nextAttackTime;
    private EnemyAttackListener attackListener;

    private boolean targetTiles[][];

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


    public Enemy(Resources res, EnemyAttackListener listener)
    {
        super(DEFAULT_ENEMY_LIVES);
        attackListener = listener;
        set(res.enemy);
        resetAttackTimer();
        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]; } } 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();
        }
    }

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


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

Cool. Now let’s start modifying stuff! The task is to make enemy and player blink whenever they are damaged. We’ll do it the following way: when character gets damaged, we record the current time of damage. For the next 0.5 seconds, it will be blinking. Sounds easy enough?

In our Character class, add two new variables:

protected float timeAlive;
private float timeOfDmgTaken;

The reasoning behind making timeAlive protected is that we may use it later somewhere. Outside of the character class. In our Character class, init those two variables:

timeAlive = 0;
timeOfDmgTaken = -1;

We want timeOfDmgTaken to represent a negative number for now, so that the game would not think that we took damage on second 0.

In our Character class, add an update function:

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

It simply adds to the total time alive. Now add a public constant, which would show how much time (total) we want our character to blink.

public static final float BLINK_TIME_AFTER_DMG = 0.25f;

Great, the character should be blinking for a quarter of a second. Now, for the blinking part. In our takeDamage function, set the valuje of timeOfDmgTaken to our current time.

timeOfDmgTaken = timeAlive;

And now we’ll have to check the player out. Now, right after the same place in GameLogic where we call enemy.update (GameLogic.update method), add player.update(delta) call.

public void update(float delta)
{
    gameTime += delta;
    effectEngine.update(delta);
    enemy.update(delta);
    player.update(delta);
…

Great. Speaking about enemy update: in our enemy’s update function, right at the start, add a call to our Character’s update function (super.update(delta)). Also, add an @Override keyword before update to indicate that we’re overriding the parent function.

Enemy.java:

@Override

@Override
public void update(float delta)
{
    super.update(delta);

Great. So how do we do the blinking? Since both enemy and player will blink the same way, we want such functionality function in Character. The issue is: player’s and enemy draw functions are different. We’ll need two functions: a function that we call before drawing and one that we call after.

Let’s call them preDraw and postDraw. Both should be inside the Character class.

public void preDraw()
{
    if (timeAlive < timeOfDmgTaken + BLINK_TIME_AFTER_DMG)
    {
        float t = (timeAlive - timeOfDmgTaken) / BLINK_TIME_AFTER_DMG;
        t = t * t;
        setColor(1, 1, 1, t);
    }
}

So what does it do? The idea is simple: as soon as character is hit, he disappears (his alpha channel iz zero). Then, he gradually reappears on the screen. The thing is, we don’t want him to reappear in linear timing. We want him to be transparent for a bit more time, and then to reappear rapidly. To do this, we apply something that is called an easing function. It makes animation/transaction look smoother. In our case, it’s Quadratic Easing (take the current time, remove the time of damage taken and divide it by the time when our sprite should be blinking. Then, we multiply the value with itself. In case of lower values (not much time passed) – we get even smaller transparency value: if our time has passed (timeAlive – timeOfDmgtaken is 0.05 seconds, then we have 0.05 / 0.25 = 0.2. After multiplying it with itself, we get 0.04 – much smaller transparency value). There are various formulas and various approaches to this: you can check out http://gizma.com/easing/ on how animation changes depending on an easing equation.

postDraw is much easier:

public void postDraw()
{
    setColor(1, 1, 1, 1);
}

We reset the transparency of the sprite. Sounds good! Now add it to both Player’s and Enemy draw functions.

Enemy:

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

Player:

public void draw(SpriteBatch batch, SizeEvaluator sizeEval)
{
    preDraw();

    setPosition(sizeEval.getBaseScreenX(fieldX), sizeEval.getBaseScreenY(fieldY));
    super.draw(batch);

    postDraw();
}

Run the game! Even though we have not changed anything gameplay-wise, you can see that it feels a bit better now, because player is getting more feedback when something happens.

Blinking on damage!

Blinking on damage!

Relevant commit: https://github.com/vladimirslav/dodginghero/commit/04b22909fe89ef16ceff444db690d60fe14884da

Developing Multiplatform Game with LibGDX, part 12: game logic behind bonuses, winning the game

Putting Game Logic behind bonus visuals. Winning the game.

In our previous lesson, we’ve discussed how to display bonus pickups, but we have not introduced a way for the player to actually pick them. Time to change that! We want our player to pick bonuses in order to restore health and deal damage. To do this, we need to check when player moves to a tile to see if the tile contains something. If it does – we pick it up.

In our GameLogic, let’s extend our AssignPlayerPosition function. After the player moves to a new coordinates, let’s check if there’s actually a bonus there. If there is – do something according to the bonus type.

Since we don’t have many bonuses at the same time, we’ll go through the whole list (from end to the beginning) and check the coordinates of each bonus. If they match – we remove the bonus and apply its effect to the game. Sounds easy? Let’s get to it.


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

    for (int i = bonuses.size() - 1; i >= 0; i--)
    {
        Bonus currentBonus = bonuses.get(i);
        if (currentBonus.getFieldX() == fx &&
                currentBonus.getFieldY() == fy)
        {
            currentBonus.release();
            bonuses.remove(i);
            break;
        }
    }
}

As I said: go through the bonus list from end to beginning. In this case it does not make a difference if you go from beginning or end (since you remove bonus only once), but if you remove multiple elements, you want to do the traversal from end to beginning, because removing an element decreases all next indexes by one which makes traverse via for cycle unreliable. (You might skip an element that needs to be deleted).

Run the game. Try moving around and picking up bonuses. You can see that it is working, bonuses are disappearing as you step on them! Now, we need to actually do something when you pick them up. Before you release the bonus, let’s check the bonus type and do something depending on that type.

We know what should be happening: on life pick, increase player lives. On attack bonus pick – reduce enemy lives. Unfortunately, we have no means (yet!) to do this.

if (currentBonus.getBonusType() == Bonus.BONUS_TYPE_HEALTH)
{
    player.addLives(1);
}
else if (currentBonus.getBonusType() == Bonus.BONUS_TYPE_ATTACK)
{
    enemy.takeDamage(1);
}

Increasing Player’s Lives

We’ll need to add addLives function to our Player and takeDamage function to our enemy. Start with the first. Adding lives is not complicated, but it gets a bit tricky: we don’t want to go over the top when player picks extra lives. We only want to restores the ones our player has lost. Let’s cap the lives of the player. Introduce a private final variable max_lives. It’s going to hold the initial amount of lives player was assigned. Modify our Player constructor:

private int lives;
private final int max_lives;

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

Great, our max_lives are saved. Now we’ll have something to check player’s lives against. Let’s start working on addLives function.

public void addLives(int amount) {
    lives += amount;
    if (lives > max_lives)
    {
        lives = max_lives;
    }
}

Add the lives, then if our lives are above the cap, reduce them to a max possible value.

Reducing enemy lives, winning the game

With enemy, it’s going to be even easier. Introduce the function takeDamage that substracts the amount from lives. For the aesthetical reasons, let’s not allow the enemy lives go below zero.

public void takeDamage(int amount)
{
    lives -= amount;
    if (lives < 0)
    {
        lives = 0;
    }
}

Simple. If you run the game now, you’ll see that your lives get restored and enemy lives are taken away once you pick the right bonus. Cool. But you still can’t win the game. As the final step, let’s adjust our GameScreen.

We don’t want to update the game (for now) once enemy got destroyed, so adjust our update() function to add an extra condition.

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

Enemy lives should be greater than zero. Also, adjust AttemptMove function to restrict player movement in case enemy lives are less than zero. How it will look now:

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

Then, in our DrawUI function, let’s make sure we tell the player that he won. Create a separate function ShowGameResult(String result) and move the “Defeat” writing there.

private void ShowGameResult(String result)
{
    DrawShadowed(result,
            0,
            gameStage.getViewport().getScreenY() + gameStage.getWidth() / 2,
            gameStage.getWidth(),
            Align.center,
            Color.RED);
}

The DrawUI part where we used to write “DEFEAT” should look like this now:

if (player.getLives() <= 0)
{
    ShowGameResult("DEFEAT!");
}

Add an extra condition that will write “Victory!” in case player won. Should be easy:

else if (enemy.getLives() <= 0)
{
    ShowGameResult("VICTORY!");
}

Run the game. Everything works! Wooho. One thing I suggest doing to make the game a bit more dynamic, is to reduce warning effect time (WARNING_TIME) from 2 to, say, 0.75 seconds. Whatever makes the game more interesting for you.

Today, we’ve reached an important achievement! We have a working prototype. Took us about 5 hours total. Usually, at this stage you can see if the game is worth working on (is it at least slightly entertaining to play the prototype?).

If this was a book, it would conclude volume one. The next volume I’ll spend on polishing the looks and the feel on the game. And finally, I’m going to work on publishing things (Android: ads / in-app purchases / achievements / leaderboards). So, stay tuned, and let’s have a great journey developing the game together!

Our victory screen, after you take all of the enemy lives

Our victory screen, after you take all of the enemy lives

Building Html Version

A side note: to build and compile web version, go to “Terminal” tab in Android Studio and write “gradlew html:dist” command, without quotes. It will take some time, but you’ll have a web version that you’ll be able to upload/show off somewhere online. Just check yourproject/html/dist folder, it has all the necessary files.

Mine prototype is at: http://coldwild.com/dodge/
Relevant git commit: https://github.com/vladimirslav/dodginghero/commit/a337f155744c058aacd2ed3367cc1ac309485baf

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