Sign in to follow this  
JWindebank

Basic SDL movement question

Recommended Posts

Hi guys, what an awesome forum you have here. Have already spent countless hours rerading and soaking up as much as I can. :) Now, I have been programming in C++ only for a short while, mostly just disecting other peoples codes and doing some tutorials. I come mostly from an application development background so games development is a lot different. One thing I am struggling to solve is the problrm of changing the animation/frames of a sprite when different arrow keys are pressed. I can loop through an animation, and can move the sprite around on screen, but changing the animation sequence to use for different directions is stumping me. The following code is a bit of a hack on the tutorial by Marius Andra at http://cone3d.gamedev.net/cgi-bin/index.pl?page=tutorials/gfxsdl/tut3.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <math.h>

#if defined(_MSC_VER)
#include "SDL.h"
#else
#include "SDL/SDL.h"
#endif

// Screen Width
#define SCREENWIDTH 640
// Screen Height
#define SCREENHEIGHT 480
// Game Left
#define GAMELEFT 14
// Game Top
#define GAMETOP 64
// Game Width
#define GAMEWIDTH 612
// Game Height
#define GAMEHEIGHT 404
// Move Speed
#define MOVESPEED 0.5f


struct CSpriteFrame
{
  SDL_Surface *image;
  int pause;
};

class CSpriteBase
{
  public:
  int init(char *dir);
  
  CSpriteFrame *mAnim;
  int mBuilt, mNumframes, mW, mH;
};

int CSpriteBase::init(char *dir)
{
  char buffer[255];
  char filename[255];
  char name[255];
  int pause=0, r=0, g=0, b=0;
  FILE *fp;
  
  sprintf(filename, "%s/info", dir);

  if((fp=fopen(filename, "r")) == NULL)
  {
    printf("ERROR opening file %s\n\n", filename);
    return -1;
  }
  
  fgets(buffer, 255, fp);
  sscanf(buffer, "FILES: %d", &mNumframes);
  mAnim = new CSpriteFrame[mNumframes];
  
  mBuilt = 1;
  int count = 0;
  
  while(!feof(fp) && count<mNumframes)
  {
    fgets(buffer, 255, fp);
    if(buffer[0] != '#' && buffer[0] != '\r' && buffer[0] != '\0' 
                     && buffer[0] != '\n' && strlen(buffer) != 0)
    {
      sscanf(buffer, "%s %d %d %d %d", name, &pause, &r, &g, &b);
      sprintf(filename, "%s/%s", dir, name);
      
      SDL_Surface *temp;
      if((temp = SDL_LoadBMP(filename)) == NULL) return -1;
      
      if(r >= 0) SDL_SetColorKey(temp, SDL_SRCCOLORKEY, 
                    SDL_MapRGB(temp->format, r, g, b));
                    
      mAnim[count].image = SDL_DisplayFormat(temp);
      SDL_FreeSurface(temp);
      
      mAnim[count].pause = pause;
      if(!mW) mW = mAnim[count].image->w; 
      if(!mH) mH = mAnim[count].image->h;

      count++;
    }
  }
 
  fclose(fp);
  return 0;
}

class CSprite
{
  public:
  int init(CSpriteBase *base, SDL_Surface *screen);
  void draw();
  void clearBG();
  void updateBG();
  
  void setFrame(int nr) { mFrame = nr; }
  int getFrame() { return mFrame; }
  
  void setSpeed(float nr) { mSpeed = nr; }
  float getSpeed() { return mSpeed; }
  
  void toggleAnim() { mAnimating = !mAnimating; }
  void startAnim() { mAnimating = 1; }
  void stopAnim() { mAnimating = 0; }
  void rewind() { mFrame = 0; }
  
  void xadd(float nr) { mX+=nr; }
  void yadd(float nr) { mY+=nr; }
  void xset(float nr) { mX=nr; }
  void yset(float nr) { mY=nr; }
  float getX() { return mX; }
  float getY() { return mY; }
  void set(float xx, float yy) { mX=xx; mY=yy; }
  
  private:
  int mFrame;
  float mX, mY, mOldX, mOldY;
  int mAnimating;
  int mDrawn;
  float mSpeed;
  long mLastupdate;
  CSpriteBase *mSpriteBase;
  SDL_Surface *mBackreplacement;
  SDL_Surface *mScreen;
};

int CSprite::init(CSpriteBase *base, SDL_Surface *screen)
{
  mSpriteBase = base;
  if(mSpriteBase->mBuilt)
  {
    if(mSpriteBase->mNumframes>1) mAnimating=1;
    mBackreplacement = 
               SDL_DisplayFormat(mSpriteBase->mAnim[0].image);
  }
  mScreen = screen;
}

void CSprite::clearBG()
{
  if(mDrawn==1)
  {
    SDL_Rect dest;
    dest.x = (int)mOldX;
    dest.y = (int)mOldY;
    dest.w = mSpriteBase->mW;
    dest.h = mSpriteBase->mH;
    SDL_BlitSurface(mBackreplacement, NULL, mScreen, &dest);
  }
}

void CSprite::updateBG()
{
  SDL_Rect srcrect;
  srcrect.w = mSpriteBase->mW;
  srcrect.h = mSpriteBase->mH;
  srcrect.x = (int)mX;
  srcrect.y = (int)mY;
  mOldX=mX;mOldY=mY;
  SDL_BlitSurface(mScreen, &srcrect, mBackreplacement, NULL);
}

void CSprite::draw()
{
  if(mAnimating == 1)
  {
    if(mLastupdate+mSpriteBase->mAnim[mFrame].pause*mSpeed < SDL_GetTicks())
    {
      mFrame++;
      if(mFrame>mSpriteBase->mNumframes-1) mFrame=0;
      mLastupdate = SDL_GetTicks();
    }
  }
  
  if(mDrawn==0) mDrawn=1;

  SDL_Rect dest;
  dest.x = (int)mX; dest.y = (int)mY;
  SDL_BlitSurface(mSpriteBase->mAnim[mFrame].image, NULL, mScreen, &dest);
}

SDL_Surface *screen, *back;
CSpriteBase defaultplayerbase;
CSprite defaultplayer;

SDL_Surface * ImageLoad(char *file)
{
  SDL_Surface *temp1, *temp2;
  temp1 = SDL_LoadBMP(file);
  temp2 = SDL_DisplayFormat(temp1);
  SDL_FreeSurface(temp1);
  return temp2;
}

int InitImages()
{
  back = ImageLoad("resources/background.bmp");
  return 0;
}

void DrawIMG(SDL_Surface *img, int x, int y)
{
  SDL_Rect dest;
  dest.x = x;
  dest.y = y;
  SDL_BlitSurface(img, NULL, screen, &dest);
}

void DrawBG()
{
  DrawIMG(back, 0, 0);
}

void DrawScene()
{
  defaultplayer.clearBG();

  defaultplayer.updateBG();

  defaultplayer.draw();
  SDL_Flip(screen);
}

int WallCollide(float x, float y)
{
  float wallLeft = GAMELEFT;
  float wallRight = GAMELEFT + GAMEWIDTH;
  float wallTop = GAMETOP;
  float wallBottom = GAMETOP + GAMEHEIGHT;
  float playerLeft = 0.0f;
  float playerRight = 64.0f;
  float playerTop = 0.0f;
  float playerBottom = 64.0f;
  
  playerLeft += x;
  playerRight += x;
  playerTop += y;
  playerBottom += y;
  
  if ( playerLeft <= wallLeft )
  {
    return 1; 
  }
  
  if ( playerRight >= wallRight )
  {
    return 1; 
  }
  
  if ( playerTop <= wallTop )
  {
    return 1; 
  }
  
  if ( playerBottom >= wallBottom )
  {
    return 1; 
  }
  
  else
  {
    return 0;
  }
}

int main(int argc, char *argv[])
{
  Uint8* keys;

  if ( SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO) < 0 )
  {
    printf("Unable to init SDL: %s\n", SDL_GetError());
    exit(1);
  }
  atexit(SDL_Quit);

  screen=SDL_SetVideoMode(SCREENWIDTH,SCREENHEIGHT,32,
                      SDL_SWSURFACE/*|SDL_FULLSCREEN*/|SDL_HWPALETTE);
  if ( screen == NULL )
  {
    printf("Unable to set 640x480 video: %s\n", SDL_GetError());
    exit(1);
  }
  
  defaultplayerbase.init("resources/sprite/default");
  
  defaultplayer.init(&defaultplayerbase,screen);
  defaultplayer.set(250,300);
  defaultplayer.setSpeed(3);
  
  SDL_ShowCursor(0);

  InitImages();
  DrawBG();
  
  int done=0;

  while(done == 0)
  {
    SDL_Event event;

    while ( SDL_PollEvent(&event) )
    {
      if ( event.type == SDL_QUIT )  {  done = 1;  }

      if ( event.type == SDL_KEYDOWN )
      {
        if ( event.key.keysym.sym == SDLK_ESCAPE ) { done = 1; }
        if ( event.key.keysym.sym ==SDLK_SPACE){defaultplayer.toggleAnim();}
      }
    }
    keys = SDL_GetKeyState(NULL);
    
    if ( keys[SDLK_UP] )
    {
      if ( !WallCollide(defaultplayer.getX(), defaultplayer.getY() - MOVESPEED) )
      {
        defaultplayer.yadd(-MOVESPEED);
      }
    }
    
    if ( keys[SDLK_DOWN] )
    {
      if ( !WallCollide(defaultplayer.getX(), defaultplayer.getY() + MOVESPEED) )
      {
        defaultplayer.yadd(MOVESPEED);
      }
    }
    
    if ( keys[SDLK_LEFT] )
    {
      if ( !WallCollide(defaultplayer.getX() - MOVESPEED, defaultplayer.getY()) )
      {
        defaultplayer.xadd(-MOVESPEED);
      }
    }
    
    if ( keys[SDLK_RIGHT] )
    {
      if ( !WallCollide(defaultplayer.getX() + MOVESPEED, defaultplayer.getY()) )
      {
        defaultplayer.xadd(MOVESPEED);
      }
    }
    
    DrawScene();
  }
  
  return 0;
}

Thanks for all help. ~ Jordan

Share this post


Link to post
Share on other sites
What you want to do is when an arrow key is pressed, change to a new animation there. So for example:


if ( keys[SDLK_RIGHT] )
{
if ( !WallCollide(defaultplayer.getX() + MOVESPEED, defaultplayer.getY()) )
{
defaultplayer.xadd(MOVESPEED);
// Change animation direction here
}
}




So for that example, you simply add in the code to switch to a new animation. You just have to make sure you have those animations done artwork wise before switching to them. To do it in a rather hackishly way, make an array of CSprite[4] so when up is pressed, you can use CSprite[0], when right CSprite[1], etc...

With what is there now the idea is to simply have multiple arrays of different animations, then switch betweeen those. Ideally though, what you will want is one animation class that can handle multiple different animations, so you can do something like Player.ChangeAnim("WalkLeft") when left is press and Player.ChangeAnim("Jump") when space is pressed.

Just some ideas [smile] Feel free to ask if you need anything cleared up.

Share this post


Link to post
Share on other sites
Hi Drew, thanks for the reply. I agree that having ChangeAnim function in the CSprite class would definitely be the best method to do this, but not exactly sure on where to start. I am assuming this would require quite a significant change in the structure of the code?

From what I can figure out, I would need to create some way of loading in all of the bitmaps when the CSpriteBase is intiialised, and then have a function that swaps out the frames to be used if a certain direction button is pressed? Can you please provide an example of how I should go about this, or point me in the direction of some basic code to show what you are explaining? That would be extremely helpful.

Thanks for the help, will keep battling it here, hopefully will make some progress. :)

Cheers,
Jordan

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this