I have a problem with my sprite engine

Started by
7 comments, last by walle 18 years, 10 months ago
Hello. Before I start: I use SDL with c++ and I use DevC++ on windows professional sp1 Ok, this will be kind of long, so if someone reads trough it I'll be happy =) But what about getting to the point? I've made a little sprite engine to use in my games. But it doesn't work at all, or maybe it does, just that I've made a programming error. Anyway I get this error: (window caption) Microsoft Visual C++ Runtime Library (the error message) Runtime error! Program: "the path to my program" This application has requested the Runtime to terminate it in an unusual way. Please contact the application's support team for more information. I assume that the error is in the drawing code, because it doesn't happen if I only load in the sprite. But if I check the width of a sprite, which is set when the sprite is loaded it just returns 0. so there have to be some error in the loading code too. Ok, before I post the source I just want to explain some stuff. I'm going to make a special sprite file format, just a zip file that holds all animations and so, but for now I use folders because it'll be easy to change when I'm ready. But the file format looks like this: Sprite // The main folder ->animations.info // A file describing how many animations the sprite has animationfolder // One of all animation folders ->frames.info // A file describing how many frames the animation has frame#1.png // One of all frames in the animation This is because I'll make a tool for making sprites, so the artist (if I work with someone) can make the sprites more easily Ok, I'm sorry for the messy source, I haven't really learned to make "good" source... The header file sprite.h

#ifndef SPRITE_H 
#define SPRITE_H 
#include <string> 
#include <vector> 
#include <fstream> 
#include <SDL/SDL.h> 
#include <SDL/SDL_Image.h> 
// A single frame in the animation 
struct frame 
{ 
    int delay; // The number of milliseconds to wait before next frame 
    SDL_Surface *img; // The image of the frame 
}; 

// A whole animation 
struct animation 
{ 
    std::string name; // The name of the animation, the name of the folder the frames are in 
    int framenr; // Keeping track of which frame we are currently viewing 
    std::vector<frame> frames; // All frames 
    
}; // A basic animated sprite 
class sprite 
{ 
    public: 
        sprite(); 
        ~sprite(); 
        
        // Self explained just look at the names 
        int getx(); 
        int gety(); 
        int getw(); 
        int geth(); 
        SDL_Rect getrect(); 
        std::string getanimation(); 
        
        void setx(int x); 
        void sety(int y); 
        void setw(int w); 
        void seth(int h); 
        void setrect(SDL_Rect rect); 
        void setanimation(std::string name); 
        
        // This function loads a sprite from a directory, first it checks how many different animations 
        // the sprite has, then loads the frames of every animation. The width and height of the sprite 
        // is decided by the first frame of the first animation 
        bool load(std::string dir, SDL_Surface *dest); 
        
        // This function draws the sprite, it animates automatically, if it isn't paused 
        void draw(); 
        
        // Animation controls 
        void pause(); 
        void resume(); 
        void reset(); 
        
    private: 
        SDL_Rect m_rect; // The rect of the sprite x,y,width and height values 
        int m_animation; // Keeps track of which animation we are on 
        int m_lastframe; // Keeps track of how many milliseconds it has been since the last frame 
        std::vector<animation> m_animations; // Every animation 
        SDL_Surface *m_dest; // The surface to draw to 
        bool m_paused; // Is the animation paused? 
}; 

// Load a image and convert it to the format SDL use 
SDL_Surface* load_IMG(std::string file); 

#endif

The cpp file sprite.cpp

#include "sprite.h" 
// The constructor clears all vectors 
sprite::sprite() 
{ for(int i=0; i<m_animations.size(); i++)
    {
        m_animations.at(i).frames.clear();
    }    
    m_animations.clear();
}

// The destructor doesn't do anythin becuase we don't have any allocated memory (by new)
sprite::~sprite() {}

// These are self explaned
int sprite::getx()
{
    return m_rect.x;
}    

int sprite::gety()
{
    return m_rect.y;
}

int sprite::getw()
{
    return m_rect.w;
}

int sprite::geth()
{
    return m_rect.h;
}

SDL_Rect sprite::getrect()
{
    return m_rect;
}

std::string sprite::getanimation()
{
    return m_animations.at(m_animation).name;
}

void sprite::setx(int x)
{
    m_rect.x=x;
}     

void sprite::sety(int y)
{
    m_rect.y=y;
}

void sprite::setw(int w)
{
    m_rect.w=w;
}

void sprite::seth(int h)
{
    m_rect.h=h;
}   

void sprite::setrect(SDL_Rect rect)
{
    m_rect=rect;
}

// Change the animation if there are a animation with the name 
void sprite::setanimation(std::string name)
{
    for(int i=0; i<m_animations.size(); i++) 
    { 
        if(name==m_animations.at(i).name) { m_animation=i; } 
    } 
} 

// This function loads a sprite from a directory, first it checks how many different animations 
// the sprite has, then loads the frames of every animation. The width and height of the sprite 
// is decided by the first of the first animation 
bool sprite::load(std::string dir, SDL_Surface *dest) 
{ 
    // Temporary variables to receive the file input 
    std::string temps; 
    int tempi; 
    // Make a string by adding the dir and a specified file 
    std::string file=dir+"/animations.info"; 
    std::ifstream fin; // Our fstream object, read only 
    fin.open(file.c_str()); // Open the file 
    if(fin==NULL) { return false; } // Return if the file don't exist or some other error 
    else 
    { 
        // Read by the "Animations:" string in the beginning of the file then read in how many animations there are to tempi 
        fin>>temps>>tempi; 
        m_animations.resize(tempi); // Resize the vector to the number of animations there are 
        for(int i=0; i<m_animations.size(); i++)
        {
            getline(fin,temps);    // Read the folder name for the animation
            m_animations.at(i).name=temps;   // Assign the folder name to the animations name
        }
    }    
    fin.close();   // We are done with this file
    
    // In this loop we'll load the frames of every animation
    for(int i=0; i<m_animations.size(); i++) 
    { 
        // Make a filename of the dir and the name of the animation and the file containing the frames data 
        // Example: dir="gfx/sprites/hero" animation name="walkleft" then the whole path becomes: 
        // "gfx/sprites/hero/walkleft/frames.info" 
        file=dir+"/"+m_animations.at(i).name+"/frames.info"; 
        fin.open(file.c_str()); // Open the file 
        if(fin==NULL) { return false; } // Exit if something unexpected happens 
        else 
        { 
            fin>>temps>>tempi; // Read past the "Frames:" string in the file and read the number of frames this animation has 
            m_animations.at(i).frames.resize(tempi); // Resize the vector to have place for all the frames 
            // This loop goes trough all the filenames and loads the frame image to it's surface 
            for(int k=0; k<m_animations.at(i).frames.size(); k++)
            {
                fin>>temps>>tempi; // Read the filename and the delay for the frame 
                std::string img; // Another temporary string to load the image 
                img=dir+"/"+m_animations.at(i).name+"/"+temps; // We do the same thing as above, make a whole path for the file 
                m_animations.at(i).frames.at(k).img=load_IMG(img); // Load the image and convert it to the format SDL uses 
                m_animations.at(i).frames.at(k).delay=tempi; // Set the delay for the frame 
            } 
            fin.close(); // We are done with the file 
        } 
        m_rect.x=0; // Set the x value to 0 
        m_rect.y=0; // Set the y value to 0 
        // Set the width and height of the sprite to the width and height of the first frame of the first animation 
        m_rect.w=m_animations.at(0).frames.at(0).img->w; 
        m_rect.h=m_animations.at(0).frames.at(0).img->h; 
        m_animation=0; // Use the first animation 
    } 
    return true; 
} 

// This function draws the sprite, it animates automatically, if it isn't paused 
void sprite::draw() 
{ 
    if(m_paused==false) // Check if the sprite animation is paused 
    { 
        // Check if the time we showed the last frame + the delay of this frame is greater or equal to SDL_GetTicks() 
        if(m_lastframe+m_animations.at(m_animation).frames.at(m_animations.at(m_animation).framenr).delay>=SDL_GetTicks()) 
        { 
            // If ot is change to the next frame 
            m_animations.at(m_animation).framenr++; 
            // If it is the last frame of the animation we'll loop it, setting the framenr to the the first frame 
            if(m_animations.at(m_animation).framenr==m_animations.at(m_animation).frames.size()) 
            { 
                m_animations.at(m_animation).framenr=0; 
            } 
            m_lastframe=SDL_GetTicks(); // We showed this frame now, save the time 
        } 
    } 
    
    // Blit the frame to the surface we assigned the sprite to 
    SDL_BlitSurface(m_animations.at(m_animation).frames.at(m_animations.at(m_animation).framenr).img, NULL, m_dest, &m_rect); 
} 
// Animation controls 
void sprite::pause() 
{ 
    m_paused=true; 
} 

void sprite::resume() 
{ 
    m_paused=false; 
} 

void sprite::reset() 
{ 
    m_animations.at(m_animation).framenr=0; 
} 

// Load a image and convert it to the format SDL use 
SDL_Surface* load_IMG(std::string file) 
{ SDL_Surface *temp1, *temp2; temp1 = IMG_Load(file.c_str()); 
temp2 = SDL_DisplayFormat(temp1); 
SDL_FreeSurface(temp1); 
return temp2; 
} 

Any help finding this error is appreciated //walle
Advertisement
Have you tried stepping through it with your debugger?
Yes...But I don't get any debug information, the error message just comes up directly. and when you press the error message the application shuts down.

//walle
You're not checking the return values from any of the SDL*/IMG* functions. It is possible that one of them is failing.

Also, you might try sprinkling some debug output through your code to help narrow down where the error occurs.
Well I don't do that in this code, but I've been trying to solve this my self, and the image isn't pointing to NULL.

I'll add some debug statements, but I don't really know where to do that...I'll just put them where it feels right.

//walle
OK, been playing with this and have found the first problem. You are not clearing the ifstream before opening another file. Try this in sprite::load.

fin.close();   // Close this filefin.clear();   // We are done with this file


As it stands everytime you recall fin.open it is reopening the same file (animations.info).

Unfortunately, this doesn't solve the problem with the error you are receiving. Still looking into it. The sprite::draw function is bugging out at the the following line though from what I can make out.

if(m_lastframe+m_animations.at(m_animation).frames.at(m_animations.at(m_animation).framenr).delay>=SDL_GetTicks()) 


The 'Microsoft Visual C++ Runtime Library' makes me think it is a problem with the SDL_Image library but not 100%.

Going to keep debuggin, this is a nice challenge for a newbie like myself. :)
Quote:Original post by JWindebank
OK, been playing with this and have found the first problem. You are not clearing the ifstream before opening another file. Try this in sprite::load.

*** Source Snippet Removed ***

As it stands everytime you recall fin.open it is reopening the same file (animations.info).



Thanks, didn't know that you have to clear the fstream handle (is it called that?) That explaines why the width of the sprite is 0...

Quote:Original post by JWindebank
Going to keep debuggin, this is a nice challenge for a newbie like myself. :)


Well who isn't a noob :D


//walle


Ok I've come closer to the problem I think. When I read from animations.info I don't jump down one row after I've read in the number of animations...

Fixed part of the source:
 // Read by the "Animations:" string in the beginning of the file then read in how many animations there are to tempi         fin>>temps>>tempi;         m_animations.resize(tempi); // Resize the vector to the number of animations there are         getline(fin,temps);        for(int i=0; i<m_animations.size(); i++)        {            getline(fin,temps);    // Read the folder name for the animation            m_animations.at(i).name=temps;   // Assign the folder name to the animations name            std::cout<<"Animation "<<i<<". "<<m_animations.at(i).name<<std::endl;        }        std::cout<<std::endl;


I've also added some debug text that goes to stdout.txt... I'll post the new load and draw functions...

One thing...now I only get the error 2/10 times, I don't know why. But even when I don't get the error nothing gets draw on the screen...
I also tested comment out the animation part of the drawing function an tried to draw the first image of the first animation, but it didn't work.

Here's the new functions:
// This function loads a sprite from a directory, first it checks how many different animations // the sprite has, then loads the frames of every animation. The width and height of the sprite // is decided by the first of the first animation bool sprite::load(std::string dir, SDL_Surface *dest) {     // Temporary variables to receive the file input     std::string temps;     int tempi;     // Make a string by adding the dir and a specified file     std::string file=dir+"/animations.info";     std::ifstream fin; // Our fstream object, read only     fin.open(file.c_str()); // Open the file     if(fin==NULL) { return false; } // Return if the file don't exist or some other error     else     {         // Read by the "Animations:" string in the beginning of the file then read in how many animations there are to tempi         fin>>temps>>tempi;         m_animations.resize(tempi); // Resize the vector to the number of animations there are         getline(fin,temps);        for(int i=0; i<m_animations.size(); i++)        {            getline(fin,temps);    // Read the folder name for the animation            m_animations.at(i).name=temps;   // Assign the folder name to the animations name            std::cout<<"Animation "<<i<<". "<<m_animations.at(i).name<<std::endl;        }        std::cout<<std::endl;    }        fin.close();   // We are done with this file    fin.clear();    // We clear the handle to be able to open a new file with the same handle        // In this loop we'll load the frames of every animation    for(int i=0; i<m_animations.size(); i++)     {         // Make a filename of the dir and the name of the animation and the file containing the frames data         // Example: dir="gfx/sprites/hero" animation name="walkleft" then the whole path becomes:         // "gfx/sprites/hero/walkleft/frames.info"         file=dir+"/"+m_animations.at(i).name+"/frames.info";         fin.open(file.c_str()); // Open the file         if(fin==NULL) { return false; } // Exit if something unexpected happens         else         {             fin>>temps>>tempi; // Read past the "Frames:" string in the file and read the number of frames this animation has             m_animations.at(i).frames.resize(tempi); // Resize the vector to have place for all the frames             // This loop goes trough all the filenames and loads the frame image to it's surface             for(int k=0; k<m_animations.at(i).frames.size(); k++)            {                fin>>temps>>tempi; // Read the filename and the delay for the frame                 std::string img; // Another temporary string to load the image                 img=dir+"/"+m_animations.at(i).name+"/"+temps; // We do the same thing as above, make a whole path for the file                 m_animations.at(i).frames.at(k).img=load_IMG(img); // Load the image and convert it to the format SDL uses                std::cout<<"Frame "<<k<<" of animation "<<i<<" has the path: "<<img<<" and the delay "<<tempi<<std::endl;                if(m_animations.at(i).frames.at(k).img==NULL) { std::cerr<<"The frame: "<<img<<" couldn't be loaded!\n"; }                m_animations.at(i).frames.at(k).delay=tempi; // Set the delay for the frame             }             fin.close(); // We are done with the file             fin.clear();  // We clear the handle to be able to open a new file with the same handle        }         std::cout<<std::endl;    }     m_rect.x=0; // Set the x value to 0     m_rect.y=0; // Set the y value to 0     // Set the width and height of the sprite to the width and height of the first frame of the first animation     m_rect.w=m_animations.at(0).frames.at(0).img->w;     m_rect.h=m_animations.at(0).frames.at(0).img->h;     m_animation=0; // Use the first animation     std::cout<<"\nThe width of the first frame of the first animation: "<<m_rect.w<<std::endl;    return true; } // This function draws the sprite, it animates automatically, if it isn't paused void sprite::draw() {     if(m_paused==false) // Check if the sprite animation is paused     {         // Check if the time we showed the last frame + the delay of this frame is greater or equal to SDL_GetTicks()         if(m_lastframe+m_animations.at(m_animation).frames.at(m_animations.at(m_animation).framenr).delay>=SDL_GetTicks())         {             // If ot is change to the next frame             m_animations.at(m_animation).framenr++;             // If it is the last frame of the animation we'll loop it, setting the framenr to the the first frame             if(m_animations.at(m_animation).framenr==m_animations.at(m_animation).frames.size())             {                 m_animations.at(m_animation).framenr=0;             }             m_lastframe=SDL_GetTicks(); // We showed this frame now, save the time         }     }         // Blit the frame to the surface we assigned the sprite to     SDL_BlitSurface(m_animations.at(m_animation).frames.at(m_animations.at(m_animation).framenr).img, NULL, m_dest, &m_rect); } 


By the way, JWindebank I could mail you my sprite file if you'd like.

//walle
Ok...I figured out the problem...and I just want to say: I'm an idiot!
In the line where I check if the frame is the last one...I check if the frame number is equal to the size of the frames vector...
But there aren't a last frame, so you have to check for size()-1...

Well it only took a complete rewrite to figure that out...

Now only to get a decent timing on the sprites...becuse this one doesn't work...

This topic is closed to new replies.

Advertisement