Public Group

I have a problem with my sprite engine

This topic is 4804 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

Recommended Posts

#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

// 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

#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
{
// 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 *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

Share on other sites
Have you tried stepping through it with your debugger?

Share on other sites
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

Share on other sites
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.

Share on other sites
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

Share on other sites
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. :)

Share on other sites
Quote:
 Original post by JWindebankOK, 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 JWindebankGoing to keep debuggin, this is a nice challenge for a newbie like myself. :)

Well who isn't a noob :D

//walle

Share on other sites
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

Share on other sites
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...

1. 1
2. 2
3. 3
frob
15
4. 4
5. 5

• 20
• 12
• 13
• 14
• 80
• Forum Statistics

• Total Topics
632144
• Total Posts
3004401

×