3
\$\begingroup\$

I am having trouble reading a spritesheet with rows and columns. If it is just one row, then my code works fine. The sheet looks like this:

enter image description here

Source: Character animations, clothes, armor, weapons, skeleton enemy, combat dummy

Right now, it only works with the first row. I can't figure out how to move it down to the next row. I know I have to change my SourceRect for the x and y but don't know to what.

Here's my code:

class SpriteSheet
{
    public Texture2D Texture { get; set; }
    public Vector2 Origin { get; set; }
    public Rectangle SourceRect { get; set; }
    public Vector2 position;

    private float timer, interval;
    private int currentFrame;
    private int spriteWidth, spriteHeight, spriteSpeed;
    private int rows, columns;
    private KeyboardState currentKBState, previousKBState;

    public SpriteSheet(Texture2D texture, int spriteWidth, int spriteHeight, int rows = 1, int columns = 0, int currentFrame = 1)
    {
        Texture = texture;
        this.currentFrame = currentFrame;
        this.spriteWidth = spriteWidth;
        this.spriteHeight = spriteHeight;
        this.rows = rows;
        this.columns = columns;

        spriteSpeed = 100;
        interval = 200;
    }

    // Handle movement of sprite
    public void Update(GameTime gameTime)
    {
        previousKBState = currentKBState;
        currentKBState = Keyboard.GetState();

        // Get the sprite from the sheet
        // THIS LINE NEEDS TO BE CHANGED I THINK
        SourceRect = new Rectangle(currentFrame * spriteWidth, 0, spriteWidth, spriteHeight);

        if(currentKBState.GetPressedKeys().Length == 0)
        {
            if (currentFrame > 0 && currentFrame < 9)
                currentFrame = 0;

            if (currentFrame > 9 && currentFrame < 18)
                currentFrame = 9;

            if (currentFrame > 18 && currentFrame < 27)
                currentFrame = 18;

            if (currentFrame > 27 && currentFrame < 36)
                currentFrame = 27;
        }

        // Animate Up
        if (currentKBState.IsKeyDown(Keys.Up))
        {
            AnimateUp(gameTime);

            // Check boundary
            if (position.Y > 25)
                position.Y -= (float) (spriteSpeed * gameTime.ElapsedGameTime.TotalSeconds);
        }
    }

    private void AnimateUp(GameTime gameTime)
    {
        if (currentKBState != previousKBState)
            currentFrame = 1;

        timer += (float)gameTime.ElapsedGameTime.TotalMilliseconds;

        if(timer > interval)
        {
            currentFrame++;
            if (currentFrame > 8)
                currentFrame = 0;

            timer = 0f;
        }
    }
}
\$\endgroup\$
  • \$\begingroup\$ You keep Y at a constant 0. That parameter should (judging by the spritesheet) be set to a value corresponding to the direction your character is moving. \$\endgroup\$ – Felsir Aug 7 '16 at 6:44
4
\$\begingroup\$

Your y-coordinate is always 0, so only the first row is used. You need to change this line:

SourceRect = new Rectangle(currentFrame * spriteWidth, 0, spriteWidth, spriteHeight);

into something like this (see the second parameter):

SourceRect = new Rectangle(currentFrame * spriteWidth, wantedRow * spriteHeight, spriteWidth, spriteHeight);
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Thanks for the reply; unfortunately, the sprite doesn't show up no matter what row I put for wantedRow. I did figure it out in another way but had to change some of the code. \$\endgroup\$ – o.o Aug 7 '16 at 18:03
0
\$\begingroup\$

So I changed up the code a bit to make it work:

Constructor change:

public SpriteSheet(Texture2D texture, int rows = 1, int columns = 0, int currentFrame = 0)
{
    this.texture = texture;
    this.sheetRows = rows;
    this.sheetColumns = columns;

    // Default = top left
    this.currentFrame = currentFrame;

    // Get a single sprite's width and height
    sheetWidth = texture.Width / sheetColumns;
    sheetHeight = texture.Height / sheetRows;
}

I noticed I didn't have to manually supply the width and height of the sprite and I could just get it from the sprite sheet. I handle the spriteSpeed and interval in my movement methods instead of the constructor now.

I removed the SourceRect line from the Update method and moved it to a custom Draw method:

public void Draw(SpriteBatch spriteBatch)
{
    // Get the correct frame
    spriteRow = currentFrame / sheetColumns;
    spriteColumn = currentFrame % sheetColumns;

    sourceRect = new Rectangle(sheetWidth * spriteColumn, sheetHeight * spriteRow, sheetWidth, sheetHeight);
    origin = new Vector2(sourceRect.Width / 2, sourceRect.Height / 2);

    spriteBatch.Draw(texture: texture, sourceRectangle: sourceRect, position: position, origin: origin, scale: new Vector2(1.0f));
}

Finally, getting the row and column of the sprite became easier with a little division.

If I wanted row 3 and column 1, the currentFrame would be 18. Knowing this, it was pretty straightforward to fix the part of the code where I reset the currentFrame where no keys are pressed.

| improve this answer | |
\$\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.