Title: Sprite Animation
1Sprite Animation
- Lecture 5
- Abdennour El Rhalibi
2Architectural Overview of DirectDraw
- DirectDraw enables a much higher level of
efficiency and speed in graphics-intensive
applications for Windows than is possible with
GDI, while maintaining device independence. - DirectDraw provides tools to perform such key
tasks as - Manipulating multiple display surfaces
- Accessing the video memory directly
- Page flipping
- Back buffering
- Managing the palette
- Clipping
- The DirectDraw component provides services
through COM-based interfaces. In the most recent
iteration, these interfaces are IDirectDraw7,
IDirectDrawSurface7, IDirectDrawPalette,
IDirectDrawClipper, and IDirectDrawVideoPort.
3The Interfaces of DirectDraw
4Using Interface Together
- Create DirectDraw object
- Retrieve a IDirectDraw7 interface
- Create at least a primary surface using
IDirectDrawSurface - Create a palette using IDrectDrawPalette
- Initialize palette
- Attach it to the surface
- Create a single clipper and size it.
- Draw on the primary surface.
5DirectDraw Clipper
6Creating a DirectDraw Object
- LPDIRECTDRAW lpdd NULL
- if (FAILED(DirectDrawCreate(NULL, lpdd, NULL))
- // error
-
- DirectDrawCreate parameters
- lpGUID GUID of the display driver that we want
use. - Usually, NULL the default hardware.
- lpdd Pointer to a pointer receiving IDirectDraw
interface pointer. - pUnkOuter advanced.. Just NULL
7Getting an Interface Lift
- LPDIRECTDRAW lpdd NULL
- LPDIRECTDRAW lpdd7 NULL
- if (FAILED(DirectDrawCreate(NULL, lpdd, NULL)))
- // error
-
- if (FAILED(lpdd-gtQueryInterface(IID_IDirectDraw7,
(LPVOID )lpdd7))) - // error
-
- lpdd-gtRelease( )
- lpdd NULL
- // use lpdd7
8Cooperating with Windows - 1
- Two modes
- Full-screen mode
- like an old DOS program
- Entire screen is for DirectDraw
- No GDI can be used
- Easy to handle
- Windowed mode
- Game is one of the clients on Windows Desktop
- Rather hard to control
- Still have full access to 2D and 3D acceleration
9Cooperating with Windows - 2
- IDirectDraw7SetCooperativeLevel( )
- HRESULT SetCooperativeLevel(HWND hWnd, DWORD
dwFlags) - Control Flags
- For full screen
- DDSCL_ALLOWMODEX 320x200, 320x240, 320x400
- DDSCL_ALLOWREBOOT CtrlAltDel rebooting
- DDSCL_EXCLUSIVE
- DDSCL_FULLSCREEN
- For Windowed Mode
- DDSCL_NORMAL
10Cooperating with Windows - 3
- Example
- Game_Init( ) and Game_Shutdown( ).
- int Game_Init(void parms NULL, int num_parms
0) - if (FAILED(DirectDrawCreate(NULL, lpdd,
NULL))) - if (FAILED(lpdd-gtQueryInterface(IID_IDirectDr
aw4, ) - lpdd4-gtSetCooperativeLevel(main_window_handle
, DDSCL_NORMAL) -
- int Game_Shutdown(void parms NULL, )
- if (lpdd4)
- lpdd4-gtRelease( )
- lpdd4 NULL
-
11Getting into the Mode of Things
- SetDisplayMode(DWORD dwWidth,
- DWORD dwHeight,
- DWORD dwBPP, // bits per pixel 8, 16,
24, 32 - DWORD dwRefreshRate, // 0 for default
- DWORD dwFlags) // extra flags (advanced)
0 for default - // 640x480 with 256 colors
- lpdd4-gtSetDisplayMode(640, 480, 8, 0, 0)
- // 800x600 with 16bit colors
- lpdd4-gtSetDisplayMode(800, 600, 16, 0, 0)
12Steps for 256 Color Mode
- Create palette 256 PALETTENTRY
- Create IDirectDrawPalette object
- Attach the palette object to a drawing surface.
- (Optional) You can change the palette entries.
13Palette Data Structure
- PALETTEENTRY palette256
- // Black at palette0
- palette0.peRed 0
- palette0.peGreen 0
- palette0.peBlue 0
- palette0.peFlags PC_NOCOLLAPSE //
preventing optimization by DirectX -
. - // White at palette255
- palette255.peRed 255
- palette255.peGreen 255
- palette255.peBlue 255
- palette255.peFlags PC_NOCOLLAPSE
- .
14IDirectDrawPalette
- Using IDirectDraw7CreatePalette( )
- LPDIRECTDRAWPALETTE lpddpal NULL // palette
interface - if (FAILED(lpdd4-gtCreatePalette(DDPCAPS_8BIT
DDPCAPS_INITIALIZE DDPCAPS_ALLOW256, - palette, lpddpal, NULL)))
- // error
-
15Building a Display Surface
16Sprite animation
17New goodies
- perform full-screen animation using 3 frames of
animation - make the crow from example look like its flying
- Use a timer to control rate at which frames are
displayed
18What is needed
- add the following libraries
- ddraw.lib
- winmm.lib
- Need 3 images
- Middle.bmp
- Up.bmp
- Down.bmp
19The Timer
- class is CTimer, with constructor Ctimer()
- file is Timer.h
- int m_nStartTime records start time
- virtual void start() starts the timer
- virtual int time() returns time in ms
- BOOL elapsed(int start, int interval)
- returns TRUE if interval ms have elapsed since
start - returns FALSE otherwise
20The Timer class
class CTimer //game timer class protected
int m_nStartTime //time that timer was started
public CTimer() //constructor virtual
void start() //start the timer virtual int
time() //return the time in ms //has
interval ms elapsed since start? BOOL
elapsed(int start,int interval)
21windows API function
include "timer.h" CTimerCTimer()
m_nStartTime(0) void CTimerstart()
m_nStartTimetimeGetTime() int
CTimertime() return timeGetTime()-m_nStartTime
int CTimerelapsed(int start, int
interval) //has interval elapsed from start?
int current_timetime() if(current_timegtstart
interval) start current_time return TRUE
else return FALSE
elapsed_time ?
22Main.cpp
CBmpFileReader frame0,frame1,frame2 int
current_frame0 //current frame of
animation CTimer Timer //game timer BOOL
LoadImages() //load graphics from files to
surfaces if(!frame0.load("Right.bmp"))return
FALSE frame0.draw(lpPrimary) //draw to
primary surface if(!frame0.setpalette(lpPrimaryP
alette))return FALSE if(!frame0.setpalette(lpSe
condaryPalette))return FALSE
if(!frame1.load("Middle.bmp"))return FALSE
if(!frame2.load("Left.bmp"))return FALSE
return TRUE
all 3 images use same palette
23Example1 vs Example2
while(TRUE) if(PeekMessage(msg,NULL,0,0,PM_N
OREMOVE)) if(!GetMessage(msg,NULL,0,0))ret
urn msg.wParam TranslateMessage(msg)
DispatchMessage(msg) else
if(!ActiveApp)WaitMessage()
while(TRUE) if(PeekMessage(msg,NULL,0,0,PM_N
OREMOVE)) if(!GetMessage(msg,NULL,0,0))ret
urn msg.wParam TranslateMessage(msg)
DispatchMessage(msg) else
if(ActiveApp) ProcessFrame() else
WaitMessage() //WinMain
24ProcessFrame()
BOOL ProcessFrame() //process a frame of
animation ComposeFrame() //compose a frame in
secondary surface return PageFlip() //flip
video memory surfaces //ProcessFrame
25BOOL ComposeFrame() static int
last_timeTimer.time() //Check that
current_frame stay in the range
if(current_framelt0)current_frame0
if(current_framegt3)current_frame3 //show a
frame switch(current_frame) case 0
frame0.draw(lpSecondary) break case 1
frame1.draw(lpSecondary) break case 2
frame2.draw(lpSecondary) break case 3
frame1.draw(lpSecondary) break //go to
next frame if(Timer.elapsed(last_time,100))
if(current_framegt4)current_frame0 return
TRUE
Why 4?
26Oops
BOOL ProcessFrame() ComposeFrame() return
PageFlip() //flip video memory surfaces
switch(current_frame) case 0
frame0.draw(lpSecondary) break case 1
frame1.draw(lpSecondary) break case 2
frame2.draw(lpSecondary) break case 3
frame1.draw(lpSecondary) break
How does the page get to lpPrimay?
27Sprite Animation
28New Goodies
- What is a sprite
- how to set up a sprite file
- what is blitting
- transparent blitting in DirectDraw
- separating an image from its properties
- how to load draw multi-frame sprites
- What happens if you try to draw outside the screen
29sprite animation
- a sprite is a small piece of artwork that moves
across the screen - sprite animation is animating a sprite
- sprites can have several frames of animation
30blitting
- drawing a sprite on a background
- DirectDraw permits transparent blitting
- designate one or more palette positions as
transparent - draw sprites with holes in them so we can see the
background
31A Sprite file
multiple sprites in a single file allow the
artist to compare frames for consistency. All
artwork must be drawn with the same palette.
32The Palette
- Sprites.bmp uses same palette as Bckgnd.bmp
- all of the artwork in a game must use the same
palette - background color for Sprites.bmp is magenta
(255,0,255) - important thing is not color but palette position
must use same palette position to indicate
transparent pixels
33Messing up the transparency color
If the artwork is inconsistent with the program,
the sprite will be drawn with a block around it.
The same art tool used on two different
platforms may reverse the palettes on their
images.
34transparent palette position
- doesnt matter what color you put in transparent
palette position since pixels are never drawn - best to use a color that isnt used elsewhere in
the game so that the artist/tool doesnt use that
palette position, expecting it to be drawn
35The bounding box
- The smallest rectangle that can fit around the
sprite - in Sprites.bmp, all frames for a multi-frame
sprite have same height/width - have to measure each sprite frame
- for 640?480, top left pixel is 0, bottom right
pixel is (639, 479) - bounding box
- easier to identify top left bottom right
- easier to see if you mess up!
36This sprite is too large
If the sprite is too large, it may mess up a
neighboring sprite. if sprite is too small,
the sprite will be clipped along one or more
edges.
37reading sprites from a file
CBmpFileReader
base class reads an image from a file. Derived
class reads a sprite (a rectangle) from a sprite
file the sprite file has multiple frames of
an image!
CBmpSpriteFileReader
38draw first difference
class CBmpSpriteFileReader public
CBmpFileReader public //reuse everything in
base except draw() BOOL draw(LPDIRECTDRAWSURFA
CE surface, int width,int ht,int x,int y)
//draw sprite found in Sbmp.cpp similar to
CBmpFileReaderdraw, except src is set to
point y rows up from the bottom of the file, and
x pixels across. bmp files are store
upside-down src m_cImage
((m_BMPFileInfo.biHeight-1-y)m_BMPFileInfo.biWidt
hx)
39draw second difference
BOOL draw(LPDIRECTDRAWSURFACE surface, int
width ,int ht, int x,int y) //draw
sprite Second difference for loop in draw only
moves ht rows of data, and each row has width
width, for(int i0 ilt ht i) //for each
row memcpy(dest,src,width) //copy
destddsd.lPitch src-m_BMPFileInfo.biWidth
//next row
40BOOL CBmpFileReaderdraw (LPDIRECTDRAWSURFACE
surface) DDSURFACEDESC ddsd //direct draw
surface descriptor BYTE dest,src
//destination and source pointers int
src_width //width of source memset(ddsd,0,sizeof
(DDSURFACEDESC)) ddsd.dwSizesizeof(ddsd) if(F
AILED(surface-gtLock(NULL,ddsd,DDLOCK_WAIT,NULL)))
return FALSE dest(BYTE)ddsd.lpSurface
//destination srcm_cImage
((m_BMPFileInfo.biHeight-1)m_BMPFileInfo.biWidth)
//trim bmp if too wide if(m_BMPFileInfo.biWi
dthgtddsd.lPitch)src_widthddsd.lPitch else
src_widthm_BMPFileInfo.biWidth //move data to
surface for(int i0 iltm_BMPFileInfo.biHeight
i) memcpy(dest,src,src_width)
destddsd.lPitch src-src_width
surface-gtUnlock(NULL) return TRUE //draw
41class CBaseSprite //simplest sprite
protected LPDIRECTDRAWSURFACE m_lpDDImage
//sprite image int m_nFrameCount //how many
frames used in sprite int m_nWidth,m_nHeight
//dimensions of sprite images void
CreateSurfaces() //create surfaces public
CBaseSprite(int frames,int w,int h)
//constructor CBaseSprite() //destructor
BOOL load(CBmpSpriteFileReader image,int frame,
int x,int y) virtual void draw(int
frame,int x,int y, LPDIRECTDRAWSURFACE
destination) //draw sprite void Release()
//release when game is over BOOL Restore()
//restore if user de-iconifies int
frame_count() //return number of frames int
height() //return height int width()
//return width
42The constructor/destructor
extern LPDIRECTDRAW lpDirectDrawObject
CBaseSpriteCBaseSprite (int frames, int
width, int height) m_nFrameCount(frames),
m_nWidth(width), m_nHeight(height)
m_lpDDImagenew LPDIRECTDRAWSURFACEframes
for(int i0 iltframes i) m_lpDDImageiNULL
//create surfaces for sprite images
CreateSurfaces() CBaseSpriteCBaseSprite ()
deletem_lpDDImage
43The plane example is 1 frame!This example is
from Main.cpp
BOOL LoadImages() //load graphics from files to
surfaces if(!background.load("bckgnd.bmp"))return
FALSE background.draw(lpBackground) //set
palettes in all surfaces if(!background.setpalet
te(lpPrimaryPalette))return FALSE
if(!background.setpalette(lpSecondaryPalette))retu
rn FALSE //load the plane sprite
if(!g_cSpriteImages.load("sprites.bmp"))return
FALSE planespritenew CBaseSprite(1,121,72)
if(!LoadPlaneSprite())return FALSE //load plane
images return TRUE
44void CBaseSpriteCreateSurfaces()
DDSURFACEDESC ddsd //direct draw surface
descriptor ddsd.dwSizesizeof(ddsd) //required
field ddsd.dwFlagsDDSD_CAPSDDSD_HEIGHTDDSD_WI
DTH ddsd.ddsCaps.dwCapsDDSCAPS_OFFSCREENPLAIN
//offscreen ddsd.dwHeightm_nHeight
ddsd.dwWidthm_nWidth DDCOLORKEY ddck
//direct draw color descriptor
ddck.dwColorSpaceLowValue ddck.dwColorSpaceHighVa
lue TRANSPARENT_COLOR //set in Defines.h
for(int i0 iltm_nFrameCount i) //for each
frame if(FAILED(lpDirectDrawObject-gt
CreateSurface(ddsd,(m_lpDDImagei),NULL))) m_
lpDDImageiNULL //set the transparent
color m_lpDDImagei-gtSetColorKey(DDCKEY_SRCB
LT,ddck)
45The Object class
- Each object in the game is describe by Cobject
- the class encapsulates everything about the
object, including - its physical characteristics
- its image
- header file Objects.h
46include "bsprite.h" class CObject //class for
a moving object private int m_nX,m_nY
//current location int m_nXspeed,m_nYspeed
//current speed int m_nLastXMoveTime //last
time the object moved CBaseSprite m_pSprite
//pointer to sprite public CObject()
void draw(LPDIRECTDRAWSURFACE surface) void
create(int x,int y,int xspeed,int yspeed,
CBaseSprite sprite) //create instance void
accelerate(int xdelta,int ydelta0) //change
speed void move() //make a move depending on
time and speed
47The draw in CObject
// The object knows where to draw itself void
CObjectdraw(LPDIRECTDRAWSURFACE
surface) m_pSprite-gtdraw(0,m_nX,m_nY,surface)
48The constructor
extern CTimer Timer CObjectCObject()
m_nXm_nYm_nXspeedm_nYspeed0
m_pSpriteNULL m_nLastXMoveTime0
49Object creation
void CObjectcreate(int x,int y,int xspeed,int
yspeed, CBaseSprite
sprite) m_nLastXMoveTime0 //time m_nXx
m_nYy //location m_nXspeedxspeed
m_nYspeedyspeed //speed m_pSpritesprite
//image
50Speeding up the object
void CObjectaccelerate(int xdelta,int ydelta)
m_nXspeedxdelta m_nYspeedydelta
51void CObjectmove() //move object const int
XSCALE8 //to scale back horizontal motion
const int MARGIN100 //margin on outside of
page int xdelta //change in position int
timeTimer.time() //current time //horizontal
motion int tfactortime-m_nLastXMoveTime
//time since last move xdelta(m_nXspeedtfactor
)/XSCALE //x distance moved m_nXxdelta //x
motion if(xdeltam_nXspeed0)
m_nLastXMoveTimetime //wrap left
if(m_nXlt-MARGIN)m_nXSCREEN_WIDTHMARGIN
//wrap right if(m_nXgtSCREEN_WIDTHMARGIN)m_nX-M
ARGIN
52ComposeFrame()
BOOL ComposeFrame() //compose a frame of
animation //draw background
lpSecondary-gtBltFast(0,0,lpBackground,NULL,
DDBLTFAST_NOCOLORKEYDDBLTFAST_WAIT) //move
objects plane.move() //draw objects
plane.draw(lpSecondary) return TRUE
//ComposeFrame