Better way to swap images in SFML?

Started by
5 comments, last by Mr C 13 years, 12 months ago
Hey all, I am working on a bit of code and I was wondering if any of you knew a better way to change one image into another then the way I am using. I looked on the SFML site and could not find anything...

#include <SFML/System.hpp>
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include <iostream>
#include <string>

int main()
{

//sf::Clock gameClock;
//float timeElapsed = 0;

try {

    sf::RenderWindow App(sf::VideoMode(800, 600, 32), "SFML Window");
    sf::Image mageReady;
    sf::Image mageCast;
    sf::Image Fire_Ball;

    if (!mageReady.LoadFromFile("mageReady.png"))
        {
            throw std::string("Your image failed to load");
        }
    if (!mageCast.LoadFromFile("mageCast.png"))
        {
            throw std::string("Your image failed to load");
        }

    if (!Fire_Ball.LoadFromFile("fireball.png"))
        {
        throw std::string("Your image failed to load");
        }

        sf::Sprite mageReadySprite;
        mageReadySprite.SetImage(mageReady);
        mageReadySprite.SetCenter(38, 38);

        sf::Sprite mageCastSprite;
        mageCastSprite.SetImage(mageCast);
        mageCastSprite.SetCenter(38, 38);

        sf::Sprite Fire_Ball_Sprite;
        Fire_Ball_Sprite.SetImage(Fire_Ball);

    bool isNotCasting = true;
    bool isCasting = false;
    bool showFireBall = false;
    bool Running = true;
    while (Running)
    {

        //timeElapsed = gameClock.GetElapsedTime();
        sf::Event myEvent;
        while (App.GetEvent(myEvent))
        {
            // Window closed
            if (myEvent.Type == sf::Event::Closed)
                App.Close();

            // Escape key pressed
            if ((myEvent.Type == sf::Event::KeyPressed) && (myEvent.Key.Code == sf::Key::Escape))
                App.Close();

            //show mage cast/spell.
            if ((myEvent.Type == sf::Event::KeyPressed) && (myEvent.Key.Code == sf::Key::C))
            {
            isCasting = true;
            showFireBall = true;
            mageCastSprite.SetX(mageReadySprite.GetPosition().x);
            mageCastSprite.SetY(mageReadySprite.GetPosition().y);
            Fire_Ball_Sprite.SetX(mageCastSprite.GetPosition().x + 10);
            Fire_Ball_Sprite.SetY(mageCastSprite.GetPosition().y -60);
            }

        }
       // Clear the screen (fill it with black color)
        App.Clear(sf::Color(255, 255, 255));

        //draw mage sprite
        if (isNotCasting == true)
        {
        App.Draw(mageReadySprite);
        }
        if (isNotCasting == false)
            {
            isNotCasting = false;
            }

        //draw mage cast
        if (isCasting == true && showFireBall == true)
        {
        isNotCasting = false;
        App.Draw(mageCastSprite);
        App.Draw(Fire_Ball_Sprite);
        }

        // Get elapsed time
        float ElapsedTime = App.GetFrameTime();
        // Move the sprite
        if (App.GetInput().IsKeyDown(sf::Key::Left))  mageReadySprite.Move(-100 * ElapsedTime, 0);
        if (App.GetInput().IsKeyDown(sf::Key::Right)) mageReadySprite.Move( 100 * ElapsedTime, 0);
        if (App.GetInput().IsKeyDown(sf::Key::Up))    mageReadySprite.Move(0, -100 * ElapsedTime);
        if (App.GetInput().IsKeyDown(sf::Key::Down))  mageReadySprite.Move(0,  100 * ElapsedTime);

        if (isCasting == true){

        if (App.GetInput().IsKeyDown(sf::Key::Left))  mageCastSprite.Move(-100 * ElapsedTime, 0);
        if (App.GetInput().IsKeyDown(sf::Key::Right)) mageCastSprite.Move( 100 * ElapsedTime, 0);
        if (App.GetInput().IsKeyDown(sf::Key::Up))    mageCastSprite.Move(0, -100 * ElapsedTime);
        if (App.GetInput().IsKeyDown(sf::Key::Down))  mageCastSprite.Move(0,  100 * ElapsedTime);

        }

        App.Display();


    }

}
catch (std::string message)
{
std::cout << "you fail because " << message;
}
    return 0;
}





Right now I have it so that when the key is pressed it puts the mageCast sprite where the mageReady sprite was, and hides the mageReady. I am looking for either a way to temporarily (or permanently) remove/delete an image when I want. The goal of this little program is to eventually have the mage walk, cast and shoot (show it casting, have the spell move X amount and then delete itself), then go back to the first image (mageReady). Right now I can move with the first image, and "C" will swap it into the second one and place the fireball where I want it. I feel that there must be a better way to do what I am trying to do.. Thanks all. Edit: Does this belong in the Alternative Game Libraries forum? I am not sure, please move if it does, and sorry if this is the wrong place.
cout << "hi!n";
Advertisement
You could create some tile sets with your character poses and use setSubRect function of Sprite objects. Or you can create arrays of images and change between them with input.

But you can't get anything farther than these, unless you create a sort of animation class
You might want to start looking at how you can encapsulate certain things so that you don't have to manually manage them like this, such as the images and frames of your animations.

Consider that a sprite (your mage) has a few qualities that define what it is: the image to draw, sub-rectangle of the image to draw from, position, rotation, etc... Any of these qualities can be changed to modify the way the sprite is drawn. Rather than hiding one sprite and showing another, you can instead just reuse a single sprite and change the image from which the sprite draws, the sub-rectangle (so that another portion of the image is "snipped" out to draw the sprite), and so forth.

First, think about animations. Animations in 2D are merely showing one image after another in sequence. So a good place to start encapsulating your images is within some sort of Animation class, that stores all of the images and sub-rectangles that comprise the animation sequence. If you make all of the frames of your animation individual images, then there is no need to remember sub-rectangles, but sub-rects allow you to cache multiple frames on a single image as a sprite sheet.

Your walking animation, then, would be its own encapsulated class owning all of the animation frames of a mage walking. It should have some sort of method by which you can pass animation state (ie, what frame of animation is the entity currently at) and which will return the Image and the Sub-Rect for that frame. These return values can be Set on a sprite to change the sprite's frame.

Now, the Mage has a single sprite associated with it, and also has certain data that determines what animation and what frame of that animation to draw. When the sprite is drawn, a pre-processing step will use the CurrentAnimation data to determine which animation to draw from (Walking or Casting) based on the mage's state. Once the correct Animation class is determined, then the data CurrentFrame will determine which frame of the animation to draw. This data is passed to the proper Animation and the resulting Image and Subrect are returned in some fashion. Call the SetImage and SetSubRect methods of the SFML::Sprite class to these results, and voila: your sprite "magically" shows the correct frame.

Once you have sufficiently encapsulated your data this way, you don't worry about manually switching images any more. You just call something along the lines of mage:setAnimation(WalkAnimation); mage:setCurrentFrame(0); to set animation state, and let the various classes worry about finding the right image and sub-rect. Plus, when you want to add more animation types down the line (Sit, Sleep, Eat, Die, Punch, what have you) it is a lot easier. All you have to do is construct another Animation object and fill it with the proper frames.

An approach I frequently use to manage all of the animations is to have all the various animation types for a given monster/player/object type owned by yet another helper class, an AnimationSet class. This class would implement a method such as getAnimation(type) so that the proper animation is returned based on the object's CurrentAnimation. Each instance of an object would own a pointer to an AnimationSet. In this way, multiple objects can share the same AnimationSet, rather than loading multiple copies of the set.

Animation and AnimationSet would provide functionality "behind-the-scenes" for loading animations and images from file.

The object itself would have logic that it owns to manage the switching of object state.

So following this example you might end up with something like this:

AnimationSet MageAnimations;
MageAnimations.loadAnimation("Walk", "walkanimationfilename");
MageAnimations.loadAnimation("Cast", "castanimationfilename");
MageAnimations.loadAnimation("Die", "dieanimationfilename");

Object mage;

mage.setPosition(100,100);
mage.setAnimationSet(&MageAnimations);
mage.setAnimationState("Walk");
mage.setAnimationFrame(0);


The Object would have a method that is called before drawing such as:

void Object::preRender()
{
Animation *anim = animationset->getAnimation(curanimation);
sf::IntRect subrect=anim->getSubRect(curframe);
sf::Image *image=anim->getImage(curframe);

sprite.SetSubRect(subrect);
sprite.SetImage(image);
}


Now, obviously, this approach takes a lot of up-front work to figure it out, but once you have it figured it out saves you a tremendous amount of time and thinking later on.




JTippetts, thank you very much for taking the time to provide such a detailed explanation. Currently I am in the process of making a spell class to learn how to attache objects to images. I plan to build this code up slowly until I have a working mage that can walk/cast correctly and have the spell "shoot" forward and perhaps add objects in the world to learn collision detection.

You lost me abit on the

void Object::preRender()
{
Animation *anim = animationset->getAnimation(curanimation);
sf::IntRect subrect=anim->getSubRect(curframe);
sf::Image *image=anim->getImage(curframe);

sprite.SetSubRect(subrect);
sprite.SetImage(image);
}

chunk but that is probably due to me not understanding pointers as well as I would like. Thanks again.
cout << "hi!n";
It's not really meant to be real code, and prolly wouldn't compile. It's just to illustrate the idea that the sprite's state data is used to query the AnimationSet and Animation classes in order to set it's image and sub-rectangle for display.
For animating a single sprite I would just have a mageSprite and the different animation frames (ready and cast) as sf::Images . Instead of hiding the mageReadySprite and replacing it you simply need to change it's image using mageSprite.SetImage(readyImage) etc... Much faster than changing sprite positions.
Quote:Original post by mongrol
For animating a single sprite I would just have a mageSprite and the different animation frames (ready and cast) as sf::Images . Instead of hiding the mageReadySprite and replacing it you simply need to change it's image using mageSprite.SetImage(readyImage) etc... Much faster than changing sprite positions.


So basically have both images tied to one call and then pick which one you want at the time?
cout << "hi!n";

This topic is closed to new replies.

Advertisement