Sign in to follow this  
rpgman15

Animating a Sprite in SDL

Recommended Posts

rpgman15    122
Ok I read the tutorial on it cone3d.gamedev.net. So do the sprites I want to animate have to be a animated gif or can it be a series of bitmaps?

Share this post


Link to post
Share on other sites
Drew_Benton    1861
Quote:
Original post by DevLiquidKnight
A series of bitmaps is how I would do it. Its much easier then extracing the gif data.


In addition, not just a series of seperate files, but rather a BMP sheet of the sprites could be an effecient way to do it as well. With SDL, it's really easy to use sprite sheets too.

Share this post


Link to post
Share on other sites
Icefox    238
To elaborate a bit on the previous post, for a sprite sheet all you need to do to animate it is, instead of resetting the sprite variable or iterating through an array, just make a class or structure so that telling it to switch to a different frame results in the image's src_rect being moved to the appropriate section of the image.

Share this post


Link to post
Share on other sites
Icefox    238
Ummmm. The only example of actual code I have on hand is in O'Caml. I would translate it to C, but knowing me I'd mess something up and only confuse you more. But here goes...


class sprite =
object (self)
val img = loadBMP "image.bmp"
val imgw = (* whatever the total image width is *)
val imgh = (* whatever the total image height is *)

val mutable numframes = (* However many frames you have *)
val mutable currentframe = 0

(* This is the SDL_rect that tells us where on img we want to blit from
We're going to "scroll" this back and forth along the image's X axis to change
frames in the animation *)
val frame = {r_x = 0; r_y = 0; r_w = 0; r_h = 0}

(* This is the SDL_rect that tells us where on whatever surface we want to blit to *)
val dest = {r_x = 0; r_y = 0; r_w = 0; r_h = 0}

(* We initialize the rects with the correct width and height... we're only going to
be moving the frame along the X axis, since it's simpler *)
initializer
frame.r_w <- imgw / numframes;
frame.r_h <- imgh;
dest.r_w <- frame.r_w;
dest.r_h <- imgh;

(* Methods to get current animation index and to move the frame to a new animation *)
method getCurrentFrame = currentframe
method setFrame n =
currentframe <- n mod numframes;
frame.r_x <- currentframe * frame.r_w

method nextFrame =
currentframe <- (currentframe + 1) mod numframes;
self#setFrame currentframe

method prevFrame =
currentframe <- abs ((currentframe - 1) mod numframes);
self#setFrame currentframe

(* Sets the destination rect to wherever we want *)
method moveTo x y =
dest.r_x <- x;
dest.r_y <- y

(* This actually blits our image to the given surface *)
method blit dst =
(* We make sure the destination coordinates are actually inside the surface *)
let dstw, dsth, _ = (surface_dims dst) in
if dest.r_x > dstw || dest.r_y > dsth then
()
else
blit_surface ~src: img ~src_rect: frame ~dst: dst ~dst_rect: dest ();
end;;




So to actually use this we'd do something like

(* Set up video mode, yadda yadda *)
Sdl.init [`EVERYTHING];
let oursprite = new sprite in
let screen = Sdlvideo.set_video_mode ~w: 640 ~h: 480 ~bpp: 16 [] in
oursprite#moveto 100 100;
while true do
oursprite#nextFrame;
oursprite#blit screen;
flip screen;
done;
Sdl.quit ();




Then image.bmp is just a single bitmap that has all the different animation frames you want for whatever particular effect.

Hope that helps. I apologize for the obscure code, but I can't stand C++.

Share this post


Link to post
Share on other sites
Rob Loach    1504
If you take a look at the SDL_BlitSurface function, you notice a couple things:
int SDL_BlitSurface(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect);
See the srcrect parameter? You can use this to denote where on the surface you are going to be blitting from. If you make a new rectangle object and set it to the individual tile on the surface, you can effectively make it blit only one segment of the sprite tile sheet. I'll let you figure out your own implementation of it as that's the only way you'll truly learn [smile].

Share this post


Link to post
Share on other sites
Will F    1069
Awhile ago I wrote a c++ class for dealing with SDL_Surface. You can take a look at it here. It comes with a example app showing a basic way to do some animation with a sprite sheet.

It only has a Makefile, so if you're not using some form of unix you'll probably need to create your own project in whatever dev environment you're using.

I'm also planning on cleaning it up at some point in the future - the graphics could definately use some work, but feel free to learn from it or use it.

Share this post


Link to post
Share on other sites
rpgman15    122
@WillF

I tried to compile your code but when it exucutes it just flashes the window and closes itself

@Rob I tried what you said and got it to blit each frame if I changed the CURRENT_FRAME value manually before I compile but when I try to advance the frame in my while loop it does nothing. I'm going to post my code to see if you can see what's wrong.




#include stdio.h;
#include stdlib.h;

#include SDL/SDL.h;

SDL_Surface *back;
SDL_Surface *screen;




#define NUM_FRAMES 10
int CURRENT_FRAME = 0;
int xpos2 = 27 * CURRENT_FRAME;
int ypos2 = 3;
int xpos = 300;
int ypos = 400;





int InitImages()
{
back = SDL_LoadBMP("Gfx/A_WALK_D.bmp");





return 0;
}




void DrawIMG(SDL_Surface *img, int x, int y, int w, int h, int x2, int y2)
{
SDL_Rect dest;
dest.x = x;
dest.y = y;
SDL_Rect dest2;
dest2.x = x2;
dest2.y = y2;
dest2.w = w;
dest2.h = h;
SDL_BlitSurface(back, &dest2, screen, &dest);
}



void DrawSprite()
{

DrawIMG(back, xpos, ypos,27,46,xpos2,ypos2);


}

void AdvanceFrame()
{
CURRENT_FRAME plusplus;

if(CURRENT_FRAME &gt; 10) { CURRENT_FRAME = 0;}
}

void DrawScene()
{
DrawSprite();

AdvanceFrame();
}

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



InitImages();





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

SDL_WM_SetCaption("DarkSword","darksword");


screen=SDL_SetVideoMode(800,600,32,SDL_HWSURFACE|SDL_DOUBLEBUF);
if ( screen == NULL )
{
printf("Unable to set 800x600 video: %s\n", SDL_GetError());
exit(1);
}


DrawScene();


int done=0;


SDL_Event event;

while(done == 0)










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; }




}

DrawScene();

}






return 0;
}
[/code]


[Edited by - rpgman15 on July 20, 2005 5:17:22 PM]

Share this post


Link to post
Share on other sites
rpgman15    122
It's been a couple days and I still haven't figured it out. It has me burnt out trying to figure it out on my own so I would appreciate a lil more guidance.

Share this post


Link to post
Share on other sites
Rob Loach    1504
Forgive me for writing in C#. It'll give you an opportunity to actually learn this stuff...
SDL_Rect srcRect = new SDL_Rect( [TileX] * [TileWidth], [TileY] * [TileHeight], [TileWidth], [TileHeight] );
SDL_Rect dstRect = new SDL_Rect( [x], [y], [TileWidth], [TileHeight] );
SDL_BlitSurface( [Surface] , srcRect, [Screen], dstRect);
Then just after each frame when you're application is running, increment the TileX (or TileY) and you'll have a steady animation using one surface for each sprite animation frame.

Share this post


Link to post
Share on other sites
rpgman15    122
Okay I get the idea but I just don't know if I'm doing the checking if one frame has passed part. I'm going to post my UpdateFrames function along with my DrawScene,DrawIMG functions and while loop.




void DrawIMG(SDL_Surface *img, int x, int y)
{
SDL_Rect dest;
dest.x = x;
dest.y = y;



SDL_Rect srcrect;
srcrect.x = CURRENT_FRAME*spritew;
srcrect.y = 0;
srcrect.w = spritew;
srcrect.h = spriteh;
SDL_BlitSurface(sprite, &srcrect, screen, &dest);
}



void DrawSprite()
{

DrawIMG(sprite, xpos, ypos);


}

void AdvanceFrame()
{



if(CURRENT_FRAME > 10)
{
CURRENT_FRAME = 0;


}
else
{
CURRENT_FRAME++;
}

}

void DrawScene()
{

DrawSprite();


AdvanceFrame();




}




Here's my while loop



while(running)

{


while ( SDL_PollEvent(&event) )
{
if ( event.type == SDL_QUIT ) { running = false; }

if ( event.type == SDL_KEYDOWN )

{

if ( event.key.keysym.sym == SDLK_ESCAPE ) { running = false; }


}



}


DrawScene();


}




Notes: My sprite sheet is 297x46 making it a 11 frame animation with each frame being 27x46. I also have to call my DrawSprite function before the loop or else it won't load the image while it's running.






Share this post


Link to post
Share on other sites
Rob Loach    1504
Are you loading the image properly? That's a common mistake when dealing with this [smile]. Otherwise, I don't see anything wrong with it. It's good that you understand what I'm talking about!

Share this post


Link to post
Share on other sites
Will F    1069
Quote:
Original post by rpgman15
@WillF

I tried to compile your code but when it exucutes it just flashes the window and closes itself


Hmmm ... what OS are you using? I've only tested it on Linux.

Share this post


Link to post
Share on other sites
rpgman15    122
Okay I'm going to post whole source code and a link to the sprite sheet for you guys to compile and see what I'm doing wrong.





#include <stdio.h>
#include <stdlib.h>

#include <SDL/SDL.h>

SDL_Surface *sprite;
SDL_Surface *screen;




int CURRENT_FRAME = 0;
int xpos = 300;
int ypos = 400;
int spritew = 27;
int spriteh = 47;





void InitImages()
{
sprite = SDL_LoadBMP("Gfx/A_WALK_D.bmp");


}




void DrawIMG(SDL_Surface *img, int x, int y)
{
SDL_Rect dest;
dest.x = x;
dest.y = y;



SDL_Rect srcrect;
srcrect.x = CURRENT_FRAME*spritew;
srcrect.y = 0;
srcrect.w = spritew;
srcrect.h = spriteh;
SDL_BlitSurface(sprite, &srcrect, screen, &dest);
}



void DrawSprite()
{

DrawIMG(sprite, xpos, ypos);


}

void AdvanceFrame()
{



if(CURRENT_FRAME > 10)
{
CURRENT_FRAME = 0;


}
else
{
CURRENT_FRAME++;
}

}

void DrawScene()
{

DrawSprite();


AdvanceFrame();




}

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



InitImages();





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

SDL_WM_SetCaption("DarkSword","darksword");


screen=SDL_SetVideoMode(800,600,32,SDL_HWSURFACE|SDL_DOUBLEBUF);
if ( screen == NULL )
{
printf("Unable to set 800x600 video: %s\n", SDL_GetError());
exit(1);
}




DrawSprite();




bool running = true;


SDL_Event event;





while(running)

{


while ( SDL_PollEvent(&event) )
{
if ( event.type == SDL_QUIT ) { running = false; }

if ( event.type == SDL_KEYDOWN )

{

if ( event.key.keysym.sym == SDLK_ESCAPE ) { running = false; }


}



}


DrawScene();


}






return 0;
}




You can get the sprite sheet here...

http://www.boomspeed.com/rpgman12/A_WALK_D.bmp

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