HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaMinor

Creating and playing animations for a game using LibGDX

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
playingcreatinganimationsgamelibgdxforusingand

Problem

It feels like there's quite a lot of code involved in order to manually build up an animation using the libGDX framework. In my specific case, I am creating a number of animations for a portrait view of a character. The character will do things like talk, blink, and laugh. There are a handful of different characters to worry about.

I would like to get some feedback on my approach. I'm hoping to simplify things as much as I can, but this is the best that I have come up with so far.

First, a texture atlas is created from a file. Then, the types from an enum are used to create a map of the types to the frames. I've removed all but one of the types just for brevity, but there is one for every single frame of animation.

PortraitType.java

public enum PortraitType {

    GOBLIN_TALK01("goblinTalkRight01", 106),
    GOBLIN_TALK02("goblinTalkRight02", 107),
    GOBLIN_TALK03("goblinTalkRight03", 108),
    GOBLIN_TALK04("goblinTalkRight04", 109),
    GOBLIN_TALK05("goblinTalkRight05", 110),
    GOBLIN_TALK06("goblinTalkRight06", 111),
    GOBLIN_TALK07("goblinTalkRight07", 112),
    GOBLIN_TALK08("goblinTalkRight08", 113),
    GOBLIN_TALK09("goblinTalkRight09", 114),
    GOBLIN_TALK10("goblinTalkRight10", 115),
    GOBLIN_TALK11("goblinTalkRight11", 116);

    public final String fileName;
    public final int id;

    private PortraitType(String fileName, int id) {
        this.fileName = fileName;
        this.id = id;
    }
}


LibGDXGame.java

private Map loadPortraitTextures() {
    Map textures =  new HashMap();
    TextureAtlas atlas = new TextureAtlas("rampartedPortraits01.atlas");
    for (PortraitType type : PortraitType.values()) {
        AtlasRegion region = atlas.findRegion(type.fileName);
        TextureRegion textureRegion = region;
        textures.put(type, textureRegion);
    }
    return textures;
}


After the map of all the frames is created, another map is created for each character that maps the type of animation to the animation its

Solution

First of all, it's a pleasure watching your games evolve, keep it up!

Data management

Most of the code is about data. Creating all your objects with their behaviors programmatically is tedious, not practical. It's not easy to see all the data, as you have to jump between multiple classes to piece everything together. Probably it didn't seem that way in the beginning, but now it definitely is. I suggest to rework how the characters are built up, using a data driven approach.

As the first step, create factory and repository interfaces that will be in charge of materializing all the characters with their behaviors. The initial implementation can be the current code, transformed appropriately, still creating the characters fully programmatically.

As the second step, create an alternative implementation, creating the characters from flat files, for example, it could be CSV, or XML, it doesn't matter much. At some point later you might want to ditch that too, for example using a database backend or REST service backend, it doesn't matter, because thanks to the factory and repository interfaces, you will be able to replace the implementation without affecting the rest of the program.

Creating arrays

This is a very fragile way to populate an array:

TextureRegion[] noneRegions = new TextureRegion[6];
noneRegions[0] = textures.get(PortraitType.GOBLIN_TALK01);
noneRegions[1] = textures.get(PortraitType.GOBLIN_TALK01);
noneRegions[2] = textures.get(PortraitType.GOBLIN_TALK01);
noneRegions[3] = textures.get(PortraitType.GOBLIN_TALK01);
noneRegions[4] = textures.get(PortraitType.GOBLIN_TALK01);
noneRegions[5] = textures.get(PortraitType.GOBLIN_TALK01);
return new Animation(1/6f, noneRegions);


It's fragile, because the array size must match the assigned elements, the indexes must be correct, unique and compete. There are many possible points of human error. Better to write like this:

return new TextureRegion[] {
    textures.get(PortraitType.GOBLIN_TALK01),
    textures.get(PortraitType.GOBLIN_TALK01),
    // ..
};


Unused variables

The cycleAnimations method takes a delta parameter that is never used.

The paused field is never used.

Naming

The currentAnimation field is of type PortraitAnimationType, which is confusing considering there is also an Animation type, which is a close collaborator. Other methods that work with PortraitAnimationType also have just Animation in their names, further aggravating the confusion. It would be better if method names were more consistent with the types of objects they work with.

Code Snippets

TextureRegion[] noneRegions = new TextureRegion[6];
noneRegions[0] = textures.get(PortraitType.GOBLIN_TALK01);
noneRegions[1] = textures.get(PortraitType.GOBLIN_TALK01);
noneRegions[2] = textures.get(PortraitType.GOBLIN_TALK01);
noneRegions[3] = textures.get(PortraitType.GOBLIN_TALK01);
noneRegions[4] = textures.get(PortraitType.GOBLIN_TALK01);
noneRegions[5] = textures.get(PortraitType.GOBLIN_TALK01);
return new Animation(1/6f, noneRegions);
return new TextureRegion[] {
    textures.get(PortraitType.GOBLIN_TALK01),
    textures.get(PortraitType.GOBLIN_TALK01),
    // ..
};

Context

StackExchange Code Review Q#145355, answer score: 4

Revisions (0)

No revisions yet.