2D sprite rotation and movement - steven_basic - 02-15-2005
The following is an update to the sprite animation / movement code that used the rotozoomer in sdl_gfx to "animate" a sprite turning 360 degrees. The sprite then will move in the direction it is facing based on some simple calculations.
However, simple or not, I seem to have run into a bit of a problem with the directional calculations
Code: ship1.x = ship1.x - (ship1.speed * sin((ship1.facing * 3.14159) / 180))
ship1.y = ship1.y - (ship1.speed * cos((ship1.facing * 3.14159) / 180))
used to determine forward movement of the "ship". I get good rotation of the sprite, but seem to only get forward movement in 8 directions only. The result is a sprite rotated at say 60 degrees but only moving at a 45 degree angle (like its sliding).
I have attempted looking up calculations for sprite movement this way, but have not found anything outside of what I have come up with.
Any help on this?
To test the complete code use the following:
Code: option explicit
' include the sdl header in order to easily use the sdl.dll
'$include: "sdl/sdl.bi"
'$include: "sdl/sdl_image.bi"
'$include: "sdl/sdl_gfx_rotozoom.bi"
' some defines to make code easier to read/maintain
#define FALSE 0
#define TRUE NOT FALSE
#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 480
' declare subs
declare sub screen_render()
' define a sprite type to encapsulate the different variables used
' to control our simple sprite
type gamesprite
image as SDL_Surface ptr
facing as double
x as long
y as long
speed as long
end type
' declare a variable for our sprite as a simple sprite type
dim shared ship1 as gamesprite
' declare a variable for our main screen
dim shared screen1 as SDL_Surface ptr
' declare a variable to track events (keyboard, etc.)
dim event as SDL_Event
' declare a general use variable to catch returning information from called functions
dim result as unsigned integer
' declare a "flag" for tracking gamestate
' this level the state of the game is either TRUE or FALSE
dim gamestate as integer
dim keys as byte ptr
' initialize video mode subsystem
result = SDL_Init(SDL_INIT_VIDEO)
' check for success/failure
if result <> 0 then
end 1
end if
' create an sdl window
screen1 = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, 32, SDL_ANYFORMAT )
' check for success/failure
if screen1 = 0 then
SDL_Quit
end 2
end if
' load bitmap image into "sprite"
ship1.image = IMG_Load("sdl_ship02.png")
' check for success/failure
if ship1.image = 0 then
SDL_Quit
end 3
end if
' set the transparent color in our sprite image
SDL_SetColorKey(ship1.image, SDL_SRCCOLORKEY, SDL_MapRGB(ship1.image->format, 255, 0, 255))
' set beginning values for sprite information
ship1.facing = 0
ship1.x = 300
ship1.y = 200
ship1.speed = 1
' set game state
gamestate = TRUE
' enter main game loop
do
' process events
while SDL_PollEvent( @event ) <> 0
' check for user exit
if event.type = SDL_QUIT_ then
gamestate = FALSE
end if
' check user keypress
if event.type = SDL_KEYDOWN then
' escape key to exit
if event.key.keysym.sym = SDLK_ESCAPE then gamestate = FALSE
end if
wend
' get current keyboard state (keys held down)
keys = SDL_GetKeyState(NULL)
if keys[SDLK_UP] then
' Angle is changed from degrees to radians since QBasic SIN and COS functions operate with radians.
' Angle Radians = (ship1.facing * 3.14159) / 180
ship1.x = ship1.x - (ship1.speed * sin((ship1.facing * 3.14159) / 180))
ship1.y = ship1.y - (ship1.speed * cos((ship1.facing * 3.14159) / 180))
end if
if keys[SDLK_LEFT] then
ship1.facing = ship1.facing + 1
if ship1.facing > 359 then ship1.facing = 0
end if
if keys[SDLK_RIGHT] then
ship1.facing = ship1.facing - 1
if ship1.facing < 0 then ship1.facing = 359
end if
' call screen render routine
screen_render
loop until gamestate = FALSE
SDL_FreeSurface( ship1.image )
SDL_Quit
end
' screen_render
' subroutine to handle the graphics updates
sub screen_render()
dim blitdst as SDL_Rect
dim scratch as SDL_Surface ptr
' lock surface if needed
if SDL_MUSTLOCK( screen1 ) then
if SDL_LockSurface( screen1 ) < 0 then
exit sub
end if
end if
' perform graphics updates here
' clear entire screen
SDL_FillRect(screen1, 0, 0)
scratch = rotozoomSurface( ship1.image, ship1.facing, 1, SMOOTHING_ON)
' set screen "outline" to recieve sprite frame from main bitmap
blitdst.x = ship1.x - (scratch->w / 2)
blitdst.y = ship1.y - (scratch->h / 2)
blitdst.w = 31
blitdst.h = 31
' draw the sprite
SDL_BlitSurface(scratch, 0, screen1, @blitdst)
' end graphics updates
' unlock surface if needed
if SDL_MUSTLOCK( screen1 ) then
SDL_UnlockSurface( screen1 )
end if
' force entire screen update
SDL_UpdateRect( screen1, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT )
end sub
2D sprite rotation and movement - steven_basic - 02-16-2005
I have also tried this "precalc" setup for determining angle and movement velocity. This code resembles one used in a VB game that controlled race cars around a track. I merely cut out some of the "enhanced control" included in the calculations for tire quality and track condiditons.
This new code acts pretty much the same as the original. It only moves in 45 degree increments.
Code: ' SDL_SHIP
option explicit
' include the sdl header in order to easily use the sdl.dll
'$include: "sdl/sdl.bi"
'$include: "sdl/sdl_image.bi"
'$include: "sdl/sdl_gfx_rotozoom.bi"
' some defines to make code easier to read/maintain
#define FALSE 0
#define TRUE NOT FALSE
#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 480
#define PI 3.14159265358979
' declare subs
declare sub screen_render()
' precalced tables
dim shared Sine(0 To 35) As double
dim shared Cosine(0 To 35) As double
const PIdiv18 = PI / 18
' define a sprite type to encapsulate the different variables used
' to control our simple sprite
type gamesprite
image as SDL_Surface ptr
facing as double
speed as double
x as long
y as long
end type
' declare a variable for our sprite as a simple sprite type
dim shared ship1 as gamesprite
' declare a variable for our main screen
dim shared screen1 as SDL_Surface ptr
' declare a variable to track events (keyboard, etc.)
dim event as SDL_Event
' declare a general use variable to catch returning information from called functions
dim result as unsigned integer
' declare a "flag" for tracking gamestate
' this level the state of the game is either TRUE or FALSE
dim gamestate as integer
dim keys as byte ptr
' initialize video mode subsystem
result = SDL_Init(SDL_INIT_VIDEO)
' check for success/failure
if result <> 0 then
end 1
end if
' create an sdl window
screen1 = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, 32, SDL_ANYFORMAT )
' check for success/failure
if screen1 = 0 then
SDL_Quit
end 2
end if
' load bitmap image into "sprite"
ship1.image = IMG_Load("sdl_ship02.png")
' check for success/failure
if ship1.image = 0 then
SDL_Quit
end 3
end if
' set the transparent color in our sprite image
SDL_SetColorKey(ship1.image, SDL_SRCCOLORKEY, SDL_MapRGB(ship1.image->format, 255, 0, 255))
' calculate sine / cosine tables
dim counter as long
for counter = 0 to 35
sine(counter) = sin(counter * PIdiv18)
print sine(counter); " : ";
cosine(counter) = cos(counter * PIdiv18)
print cosine(counter)
next
' set beginning values for sprite information
ship1.facing = 0
ship1.x = 300
ship1.y = 200
ship1.speed = 1
' set game state
gamestate = TRUE
' enter main game loop
do
' process events
while SDL_PollEvent( @event ) <> 0
' check for user exit
if event.type = SDL_QUIT_ then
gamestate = FALSE
end if
' check user keypress
if event.type = SDL_KEYDOWN then
' escape key to exit
if event.key.keysym.sym = SDLK_ESCAPE then gamestate = FALSE
end if
wend
' get current keyboard state (keys held down)
keys = SDL_GetKeyState(NULL)
if keys[SDLK_UP] then
' Angle is changed from degrees to radians since QBasic SIN and COS functions operate with radians.
' Angle Radians = ship1.facing * 3.14159 / 180
' ship1.x = ship1.x - (ship1.speed * (sin(ship1.facing * 3.14159 / 180)))
' ship1.y = ship1.y - (ship1.speed * (cos(ship1.facing * 3.14159 / 180)))
'work out the direction of travel
ship1.x = (ship1.x - (ship1.speed * Sine(ship1.facing)))
ship1.y = (ship1.y - (ship1.speed * Cosine(ship1.facing)))
end if
if keys[SDLK_LEFT] then
ship1.facing = ship1.facing + 1
if ship1.facing > 35 then ship1.facing = 0
end if
if keys[SDLK_RIGHT] then
ship1.facing = ship1.facing - 1
if ship1.facing < 0 then ship1.facing = 35
end if
' call screen render routine
screen_render
loop until gamestate = FALSE
SDL_FreeSurface( ship1.image )
SDL_Quit
end
' screen_render
' subroutine to handle the graphics updates
sub screen_render()
dim blitdst as SDL_Rect
dim scratch as SDL_Surface ptr
' lock surface if needed
if SDL_MUSTLOCK( screen1 ) then
if SDL_LockSurface( screen1 ) < 0 then
exit sub
end if
end if
' perform graphics updates here
' clear entire screen
SDL_FillRect(screen1, 0, 0)
scratch = rotozoomSurface( ship1.image, ship1.facing * 10, 1, SMOOTHING_ON)
' set screen "outline" to recieve sprite frame from main bitmap
blitdst.x = ship1.x - (scratch->w / 2)
blitdst.y = ship1.y - (scratch->h / 2)
blitdst.w = 31
blitdst.h = 31
' draw the sprite
SDL_BlitSurface(scratch, 0, screen1, @blitdst)
' end graphics updates
' unlock surface if needed
if SDL_MUSTLOCK( screen1 ) then
SDL_UnlockSurface( screen1 )
end if
' force entire screen update
SDL_UpdateRect( screen1, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT )
end sub
|