SFML community forums

Help => Graphics => Topic started by: lezebulon on December 24, 2014, 04:34:52 pm

Title: Animated tiles without looping ?
Post by: lezebulon on December 24, 2014, 04:34:52 pm
Hi guys

I'm making a 2D overhead game where the world is made of 32x32 tiles.
Some tiles are just a picture, some tiles are animated ie they cycle through a few frames during gameplay.

For now, each tile in my 2D map is a sprite. Now let's say that at a given frame, the animated tile frame should change. All I could manage to do for this effect was to call setTextureRect() on every animated tile and manually change the texture rectangle for each sprite, so that it matches the current frame. This works fine but it seems to me that I'm doing something wrong with editing manually *each* sprite's texture rectangle.
Is there a more logical way to do this that avoid looping over all the tiles ? Like doing some work directly on the texture itself?
Also, if I intend to change things later to a vertex array instead of a 2D table of sprites, would my concerns still apply ? what would be the solution then?

Thanks!
Title: Re: Animated tiles without looping ?
Post by: Hapax on December 24, 2014, 05:20:52 pm
I don't think that looping through all relevant sprites that need to change is incorrect, just to change a textureRect is incorrect. I'm not sure why you think you're doing something wrong.
If your map extends beyond the visible view, you may want to only check the visible tiles but even then, adjusting some textureRects is unlikely to slow anything down.

Also, I'm not sure what you meant by the word "manually" in this context.
Title: Re: Animated tiles without looping ?
Post by: Ixrec on December 24, 2014, 05:36:52 pm
I'm guessing you have code vaguely resembling this:

int main() {
    ...
    sf::Sprite animatedSprite1;
    ...

    sf::Clock clock;
    while(window.isOpen) {
        // handle events

        sf::Time deltaTime = clock.restart();
        animatedSprite1.setTextureRect((deltaTime.asSeconds() / 10) * 32, 0); // a 1 x 10 spritesheet
        animatedSprite2.setTextureRect((deltaTime.asSeconds() / 5) * 32,
                                       (deltaTime.asSeconds() > (5 * 32)) ? 32 : 0); // a 2 x 5 spritesheet
        animatedSprite3.setTextureRect(...); // you get the idea
        ...
        // clear/draw/display
    }
}
 

If so, the answer is a class with an update() method:

class AnimatedSprite {
    public:
        AnimatedSprite(const sf::Texture&, /* tile size/count/etc */) { ... }
        update(sf::Time deltaTime) {
            d_sprite.setTextureRect(...);
        }
        draw(sf::RenderTarget target) { target.draw(d_sprite); }
    private:
        sf::Sprite d_sprite;
        /* tile size/count/etc */
}

int main() {
    ...
    AnimatedSprite animatedSprite1(...);
    ...

    sf::Clock clock;
    while(window.isOpen) {
        // handle events

        sf::Time deltaTime = clock.restart();
        animatedSprite1.update(deltaTime);
        ...
        // clear/draw/display
    }
}

Note that Thor has some animation classes to help do stuff like this: http://www.bromeon.ch/libraries/thor/v2.0/tutorial-animations.html


Hapax is right that you do have to loop over all your animating entities every frame no matter what, but when you reduce main()'s job to simply calling update() on them, it's not much of a problem.
Title: Re: Animated tiles without looping ?
Post by: lezebulon on December 25, 2014, 03:51:01 pm
Hi
no I'm already doing the second solution in the code you posted.
Basically I'm asking if there's a way for the code that updates the animated tiles to not call a function on each instance of sf::Sprite (whether it's hidden away or not). I'm guessing the only way then would be to change the sf::Texture directly but it's going to be even slower and more complex.

Title: Re: Animated tiles without looping ?
Post by: Ixrec on December 25, 2014, 04:26:24 pm
In principle, no.  The main() loop always has to update every entity that needs updating.

You can do things like store all the sprites in a container so you can update all of them with a 2-3 line foreach loop (usually a good idea), or if you're feeling OOP-y have an AnimatedSpriteManager that wraps this container in a single update method (which would technically solve the question as you just phrased it, but I'm guessing that's not what you want).  In the end, yes it is all about how much hiding you want to do.

In theory you can put the update logic inside the draw() function, since you also have to call draw() on everyone, but that tends to cause a lot of other far worse problems.