1
\$\begingroup\$

I'm not entirely sure how to best explain what I'm doing. Maybe I'm going about this completely the wrong way. If any of you find that that is in fact the case, I would appreciate it if you told me how I can do this better.

I've started looking into game development about a week ago and came across a cross-platform framework called LibGDX. So I started working on a practice project which is an incremental game. I'm using the sprites supplied by a program called BannedStory. I want every part of the player's body to be customizable. That means that I'm using every sprite's animations separately.

This is a snippet of code to illustrate how I'm building the character right now.

float playerScreenWidth = HackerStory.WORLD_WIDTH;
    float playerScreenHeight = HackerStory.WORLD_HEIGHT;

    float playerPositionX = playerScreenWidth*0.3f;
    float playerPositionY = playerScreenHeight*0.7f;

    manager = new AssetManager();

    textureAtlas = new TextureAtlas(Gdx.files.internal("spritesheets/body/light/walking.atlas"));

    bodyAnimation = new Animation(0.25f , textureAtlas.getRegions()); // textureAtlas.getRegions()
    bodyAnimationActor = new AnimatedImage(bodyAnimation);
    bodyAnimationActor.setPosition(playerPositionX, playerPositionY);

    float bodyWidth = bodyAnimation.getKeyFrame(0).getRegionWidth();
    float bodyHeight = bodyAnimation.getKeyFrame(0).getRegionHeight();
    float bodyX = bodyAnimationActor.getX();
    float bodyY = bodyAnimationActor.getY();



    Group body = new Group();
    body.addActor(bodyAnimationActor);


    Group head = new Group();

    textureAtlas = new TextureAtlas(Gdx.files.internal("spritesheets/head/light/walking.atlas"));

    headAnimation = new Animation(0.25f , textureAtlas.getRegions()); // textureAtlas.getRegions()
    headAnimationActor = new AnimatedImage(headAnimation);
    headAnimationActor.setPosition(bodyX - bodyWidth * 0.05f, bodyY + bodyHeight * 0.87f);

    float headWidth = headAnimation.getKeyFrame(0).getRegionHeight();
    float headHeight = headAnimation.getKeyFrame(0).getRegionHeight();
    float headX = headAnimationActor.getX();
    float headY = headAnimationActor.getY();

    head.addActor(headAnimationActor);



    textureAtlas = new TextureAtlas(Gdx.files.internal("spritesheets/face/male/002/walking.atlas"));

    faceAnimation = new Animation(0.25f, textureAtlas.getRegions());
    faceAnimationActor = new AnimatedImage(faceAnimation);
    faceAnimationActor.setPosition(headX+headWidth*0.25f, headY + headHeight*0.1f);

    head.addActor(faceAnimationActor);


    textureAtlas = new TextureAtlas(Gdx.files.internal("spritesheets/hair/male/003/black/walking.atlas"));

    hairAnimation = new Animation(0.25f, textureAtlas.getRegions());
    hairAnimationActor = new AnimatedImage(hairAnimation);
    hairAnimationActor.setPosition(/*headX+headWidth*0.085f*/headX+hairAnimation.getKeyFrame(0).getRegionWidth()*0.04f, headY + headHeight*0.39f);

    head.addActor(hairAnimationActor);


    Group player = new Group();
    player.addActor(body);
    player.addActor(head);

As you can see, I am currently setting the X position for (for example the hair) by using its parent's X co-ordinate plus a fraction of its own width. However, this causes issues when the hair is different widths. What works for one hair sprite, won't work for another.

This is what the previous code results in: http://imgur.com/a/ogP5K

I am at a loss for where to go next. I also couldn't find anything on the subject in other questions, so again, I might be completely wrong in my approach to this.

Does anyone have any idea as to how I could solve this issue?

\$\endgroup\$
  • \$\begingroup\$ Adding screenshots of your issue would improve the question quality and help others help you :) \$\endgroup\$ – Alexandre Vaillancourt Dec 20 '15 at 16:20
  • \$\begingroup\$ My bad! I've made an album. Using three different hair spritesheets but the same code results in this: imgur.com/a/ogP5K . \$\endgroup\$ – Ryangr0 Dec 20 '15 at 16:26
  • \$\begingroup\$ You can edit your question :) \$\endgroup\$ – Alexandre Vaillancourt Dec 20 '15 at 16:31
  • \$\begingroup\$ Although I don't know libGDX, but first of all correct float headWidth = headAnimation.getKeyFrame(0).getRegionHeight(); to float headWidth = headAnimation.getKeyFrame(0).getRegionWidth();. Second thing I am gonna say depending upon theory that keep hair sprite size same, set all the hair frame size to the biggest one. Means Size of biggest hair sprite should be the size of all hair sprites. \$\endgroup\$ – Hamza Hasan Dec 20 '15 at 19:30
1
\$\begingroup\$

Solution

Well, you could get the sprite sheet and open it up in Adobe Photoshop / Pixlr as two different layers then position the hair to fit on each of the sprite animations. Then you could remove the bottom layer with the sprites and you have a row of hair textures that can all be turned into an animation via. a custom class such as this one:

import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.*;

public class SpriteSheet {

    //All the frames from the entity
    private TextureRegion[][] textureSheet;

    //Gets the sprite sheet for the entity and splits it into frames
    public SpriteSheet(Texture spriteSheet, int tileWidth, int tileHeight) {
        //Splits the textures into a two dimensional array
        /*
         * Dimension 1: Row
         * Dimension 2: Column
         * Example:
         * ********
         * ****0***
         * ********
         * ********
         * 0 = textureSheet[1][4] to access the second row and the fifth column
         * Some of the columns may be blank, check the sprite sheet to reference
         */
        textureSheet = TextureRegion.split(spriteSheet, tileWidth, tileHeight);
    }

    //Disposes of the textures by looping through the rows then the individual columns and disposing of all the textures
    public void dispose() {
        for(TextureRegion[] textureRow : textureSheet) for(TextureRegion texture : textureRow) texture.getTexture().dispose();
    }

    //Returns the texture at a certain row or column (starting at 1,1 being textureSheet[0][0])
    public TextureRegion getTexture(int row, int column) {
        return textureSheet[row][column];
    }

    //Returns an animation using pieces of the sprite sheet
    public Animation getAnimation(int row, int columns, float animationFPS) {
        //The array that stores the frames in the animation
        TextureRegion[] animationTextures = new TextureRegion[columns];
        //Loops through the columns in the row and add them to the array of textures
        for(int i = 0; i < columns; i++) animationTextures[i] = textureSheet[row][i];

        //Returns the animation generated using the frames from the row specified and the FPS from the parameters
        return new Animation(animationFPS, animationTextures);
    }
}

Or you could use the gdx-texturepacker-gui application to split them into animations which is a lot more tedious than using the Animation class but albeit easier. It's up to you though but you can then simply draw the animations on top of each other like so:

exampleBatch.begin();
exampleBatch.draw(exampleBaseAnimation.getKeyFrame(stateTime, true), x, y);
exampleBatch.draw(exampleHairAnimation.getKeyFrame(stateTime, true), x, y);
exampleBatch.end();

If you want to look up more about LibGDX's 2D animation framework then you can check out this wiki page which gives an in-depth example of how both methods work and about delta/state time and such (delta/state time is a decimal value that represents how many frames have passed by this second so 0.1 running at 10FPS would be 1 frame passed since 0.1 * 10 and 0.2 would be 2 frames and so on and so forth with the general equation when n is the number of frames passed, d is the decimal value of delta/state time and s is the frames per second: n=d*s).

Delta/State time is useful with animation in LibGDX as LibGDX can automatically find out which frame it needs to draw using a form of the delta/state time instance that increments every frame. To get this value, create a field to store this in and just add on the delta/state time value on each frame like so: stateTime += Gdx.graphics.getDeltaTime().

Hope this helps!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.