From 39e3ed6acc34254ab5c2c347a28f9bc07fd80899 Mon Sep 17 00:00:00 2001 From: thp Date: Tue, 15 May 2007 12:16:00 +0000 Subject: [PATCH] Better handling of display format w/ surface formats Game loop is now timer-based instead of delay-based Add GameState struct that holds the current game state --- data/shadow.png | Bin 236 -> 227 bytes game.c | 465 ++++++++++++++++++++++++++++++-------------------------- game.h | 43 +++++- graphics.c | 14 +- tennix.c | 15 +- 5 files changed, 306 insertions(+), 231 deletions(-) rewrite game.c (82%) diff --git a/data/shadow.png b/data/shadow.png index 77e910c9b200a842f5b2e2a2815c8b0d9ea17906..f1adfa3ba76986a03319b23d2eec64b1dee43705 100644 GIT binary patch delta 152 zcwPaR0B8T~0pkIXQ-2N>1{KP>T!H`q0B}h}K~y-)?a{3b!eA7I;inA>)nG7}YUW^p zZoqKepbK=4YPhBrfvUh4MfgHO(pMgFa(>wOKdQA55qlib;)DYhJaESib7nr@j3Q!( z3(g+~nJ{MNQbk0A>zB>?8eM^jx*9_PqoLeJrC!_kAusBZArWp(Zfkk~0000 -#include - -#include - -#include "tennix.h" -#include "game.h" -#include "graphics.h" -#include "input.h" -#include "sound.h" - -typedef struct { - float x; - float y; - float move_x; - float move_y; - float jump; -} Ball; - -typedef struct { - float x; - float y; - unsigned char state; - unsigned int score; - unsigned char responsible; /* responsible for the next fault (if any) */ -} Player; - -void game() { - int i; - float phase; - int ground_state = 0; - - Ball ball = { GAME_X_MID, GAME_Y_MID, BALL_DEFAULT_SPEED, 0.2, 7.5 }; - Ball ground = { 0, 0, 0, 0, 0 }; - Player player1 = { GAME_X_MIN-4, GAME_Y_MID, 0, 0, 1 }; - Player player2 = { GAME_X_MAX, GAME_Y_MID, 0, 0, 0 }; - - Uint8 *keys; - SDL_Event e; - - clearscr(); - updatescr(); - wait_keypress(); - - sound_audience(); - - while( 1) { - clearscr(); - show_image( GR_COURT, 0, 0, 255); - show_image( GR_SHADOW, ball.x, ball.y, 255); - - show_sprite( GR_RACKET, (!player1.state), 4, player1.x, player1.y, 255); - show_sprite( GR_RACKET, (!player2.state)+2, 4, player2.x, player2.y, 255); - - phase = 2*1.6*ball.jump*get_phase( ball.x, GAME_X_MIN, GAME_X_MAX, ball.move_x); - if( phase < 1.0) { - if( !ground_state) { - sound_ground(); - /* after the ball hit the ground, responsibilities change */ - player1.responsible = !(player2.responsible = !player2.responsible); - } - ground_state = 1; - ground.jump = 3; - ground.x = ball.x; - ground.y = ball.y; - } else { - ground_state = 0; - if( ground.jump && !(i%5)) ground.jump--; - } - - if( ground.jump) { - show_sprite( GR_GROUND, ground.jump-1, 3, ground.x, ground.y, 128); - } - - if( ball.move_x > 0) { - show_sprite( GR_BALL, (i/5)%4, 4, ball.x, ball.y-phase, 255); - } else { - show_sprite( GR_BALL, 3-(i/5)%4, 4, ball.x, ball.y-phase, 255); - } - - show_digit( player1.score/10%10, 140*2, 14, 100); - show_digit( player1.score%10, 148*2, 14, 100); - show_digit( 10, 156*2, 14, 100); - show_digit( player2.score/10%10, 164*2, 14, 100); - show_digit( player2.score%10, 172*2, 14, 100); - - updatescr(); - - if( IS_OUT_X(ball.x) || IS_OUT_Y(ball.y)) { - if( (IS_OUT_Y(ball.y) && player1.responsible) || (!(player1.state && IS_NEAR_Y(player1.y, ball.y+phase)) && ball.x < GAME_X_MIN)) { - player2.score++; - sound_applause(); - player2.responsible = !(player1.responsible = 1); - ball.x = GAME_X_MID; - ball.y = GAME_Y_MID; - ball.move_x = fabsf( ball.move_x); - limit_value( &ball.move_x, ball.move_x, BALL_DEFAULT_SPEED); - ball.move_y = -0.1; - wait_keypress(); - sound_applause_stop(); - } - - if( (IS_OUT_Y(ball.y) && player2.responsible) || (!(player2.state && IS_NEAR_Y(player2.y, ball.y+phase)) && ball.x > GAME_X_MAX)) { - player1.score++; - sound_applause(); - player1.responsible = !(player2.responsible = 1); - ball.x = GAME_X_MID; - ball.y = GAME_Y_MID; - ball.move_x = -fabsf( ball.move_x); - limit_value( &ball.move_x, -BALL_DEFAULT_SPEED, ball.move_x); - ball.move_y = 0.1; - wait_keypress(); - sound_applause_stop(); - } - - if( IS_OUT_X(ball.x)) { - if( ball.move_x < 0) { - ball.x = GAME_X_MIN; - if( player1.state == PLAYER_STATE_MAX) { - ball.move_x = PLAYER_POWERSHOT; - } else { - ball.move_x = 2.5 + 2.0*player1.state/PLAYER_STATE_MAX; - } - ball.move_y = get_move_y( player1.y, ball.y-phase, PLAYER_AREA, ball.move_x, phase); - ball.jump += 1.0-2.0*(player1.state<5); - sound_racket( player1.state == PLAYER_STATE_MAX); - } else { - ball.x = GAME_X_MAX; - if( player2.state == PLAYER_STATE_MAX) { - ball.move_x = -PLAYER_POWERSHOT; - } else { - ball.move_x = -(2.5 + 2.0*player2.state/PLAYER_STATE_MAX); - } - ball.move_y = get_move_y( player2.y, ball.y-phase, PLAYER_AREA, ball.move_x, phase); - ball.jump += 1.0-2.0*(player2.state<5); - sound_racket( player2.state == PLAYER_STATE_MAX); - } - } - } - - ball.x += ball.move_x*2; - ball.y += ball.move_y*2; - - if( player1.state) player1.state--; - if( player2.state) player2.state--; - - SDL_PollEvent( &e); - keys = SDL_GetKeyState( NULL); - - if( keys['2']) player1.y-=8; - if( keys['w']) player1.y-=6; - if( keys['s']) player1.y+=6; - if( keys['x']) player1.y+=8; - if( keys['d'] && !player1.state) player1.state = PLAYER_STATE_MAX; - - if( keys['9']) player2.y-=8; - if( keys['o']) player2.y-=6; - if( keys['l']) player2.y+=6; - if( keys['.']) player2.y+=8; - if( keys['k'] && !player2.state) player2.state = PLAYER_STATE_MAX; - - if( keys['f']) SDL_WM_ToggleFullScreen( screen); - - if( keys[SDLK_ESCAPE] || keys['q']) break; - - limit_value( &player1.y, PLAYER_Y_MIN, PLAYER_Y_MAX); - limit_value( &player2.y, PLAYER_Y_MIN, PLAYER_Y_MAX); - limit_value( &ball.jump, BALL_JUMP_MIN, BALL_JUMP_MAX); - - - SDL_Delay( 10); - i++; - } - -} - -void limit_value( float* value, float min, float max) { - if( *value < min) { - *value = min; - } else if( *value > max) { - *value = max; - } -} - -float get_phase( float x, float min, float max, float direction) { - float pos, fract; - - pos = (direction>0)?(1-GROUND_PHASE):(GROUND_PHASE); - - fract = (x-min)/(max-min); - - if( fract < pos) { - fract = fract/pos; - return fabsf( cosf(PI*fract/2)); - } else { - fract = (pos-fract)/(1-pos); - return fabsf( sinf(PI*fract/2)); - } -} - -float get_move_y( float py, float by, float pa, float move_x, float phase) { - float pct, dest, x_len, y_len; - - /* 0.0 .. 1.0 for racket hit position */ - pct = ((py+pa)-by)/(2*pa); - limit_value( &pct, 0.0, 1.0); - - /* Y destination for ball */ - dest = GAME_Y_MIN + pct*(GAME_Y_MAX-GAME_Y_MIN); - - /* lengths for the ball's journey */ - x_len = GAME_X_MAX - GAME_X_MIN; - y_len = dest - py + phase; - - /* return the should-be value for move_y */ - return (y_len*move_x)/(x_len); -} - +#include +#include + +#include + +#include "tennix.h" +#include "game.h" +#include "graphics.h" +#include "input.h" +#include "sound.h" + +void game() { + GameState s = { + { GAME_X_MID, GAME_Y_MID, BALL_DEFAULT_SPEED, 0.2, 7.5 }, + { 0, 0, 0, 0, 0 }, + { GAME_X_MIN-4, GAME_Y_MID, 0, 0, 1 }, + { GAME_X_MAX, GAME_Y_MID, 0, 0, 0 }, + 0, + 0, + 0, + }; + + Uint32 ot = SDL_GetTicks(); + Uint32 nt; + Uint32 dt = GAME_TICKS; + Uint32 diff; + Uint32 accumulator = 0; + bool quit = false; + + sound_audience(); + + while( !quit) { + nt = SDL_GetTicks(); + diff = nt-ot; + if( diff > 2000) { + diff = 0; + } + + accumulator += diff; + ot = nt; + + while( accumulator >= dt) { + quit = step( &s); + s.time += dt; + accumulator -= dt; + + if( s.was_stopped) { + ot = SDL_GetTicks(); + s.was_stopped = false; + } + } + + render( &s); + } + +} + +bool step( GameState* s) { + Uint8 *keys; + SDL_Event e; + + if( get_phase( s) < 1.0) { + if( !s->ground.jump) { + sound_ground(); + /* after the ball hit the ground, responsibilities change */ + s->player1.responsible = !(s->player2.responsible = !s->player2.responsible); + } + s->ground.jump = 3; + s->ground.x = s->ball.x; + s->ground.y = s->ball.y; + } else { + if( s->ground.jump && !(s->time%5)) s->ground.jump--; + } + + if( IS_OUT_X(s->ball.x) || IS_OUT_Y(s->ball.y)) { + if( (IS_OUT_Y(s->ball.y) && s->player1.responsible) || (!(s->player1.state && IS_NEAR_Y(s->player1.y, s->ball.y+get_phase( s))) && s->ball.x < GAME_X_MIN)) { + s->player2.score++; + sound_applause(); + s->player2.responsible = !(s->player1.responsible = 1); + s->ball.x = GAME_X_MID; + s->ball.y = GAME_Y_MID; + s->ball.move_x = fabsf( s->ball.move_x); + limit_value( &s->ball.move_x, s->ball.move_x, BALL_DEFAULT_SPEED); + s->ball.move_y = -0.1; + wait_keypress(); + s->was_stopped = true; + sound_applause_stop(); + } + + if( (IS_OUT_Y(s->ball.y) && s->player2.responsible) || (!(s->player2.state && IS_NEAR_Y(s->player2.y, s->ball.y+get_phase( s))) && s->ball.x > GAME_X_MAX)) { + s->player1.score++; + sound_applause(); + s->player1.responsible = !(s->player2.responsible = 1); + s->ball.x = GAME_X_MID; + s->ball.y = GAME_Y_MID; + s->ball.move_x = -fabsf( s->ball.move_x); + limit_value( &s->ball.move_x, -BALL_DEFAULT_SPEED, s->ball.move_x); + s->ball.move_y = 0.1; + wait_keypress(); + s->was_stopped = true; + sound_applause_stop(); + } + + if( IS_OUT_X(s->ball.x)) { + if( s->ball.move_x < 0) { + s->ball.x = GAME_X_MIN; + if( s->player1.state == PLAYER_STATE_MAX) { + s->ball.move_x = PLAYER_POWERSHOT; + } else { + s->ball.move_x = 2.5 + 2.0*s->player1.state/PLAYER_STATE_MAX; + } + s->ball.move_y = get_move_y( s, 1); + s->ball.jump += 1.0-2.0*(s->player1.state<5); + sound_racket( s->player1.state == PLAYER_STATE_MAX); + } else { + s->ball.x = GAME_X_MAX; + if( s->player2.state == PLAYER_STATE_MAX) { + s->ball.move_x = -PLAYER_POWERSHOT; + } else { + s->ball.move_x = -(2.5 + 2.0*s->player2.state/PLAYER_STATE_MAX); + } + s->ball.move_y = get_move_y( s, 2); + s->ball.jump += 1.0-2.0*(s->player2.state<5); + sound_racket( s->player2.state == PLAYER_STATE_MAX); + } + } + } + + s->ball.x += s->ball.move_x; + s->ball.y += s->ball.move_y; + + if( s->player1.state) s->player1.state--; + if( s->player2.state) s->player2.state--; + + SDL_PollEvent( &e); + keys = SDL_GetKeyState( NULL); + + if( keys['2']) s->player1.y-=8; + if( keys['w']) s->player1.y-=6; + if( keys['s']) s->player1.y+=6; + if( keys['x']) s->player1.y+=8; + if( keys['d'] && !s->player1.state) s->player1.state = PLAYER_STATE_MAX; + + if( keys['9']) s->player2.y-=8; + if( keys['o']) s->player2.y-=6; + if( keys['l']) s->player2.y+=6; + if( keys['.']) s->player2.y+=8; + if( keys['k'] && !s->player2.state) s->player2.state = PLAYER_STATE_MAX; + + if( keys['f']) SDL_WM_ToggleFullScreen( screen); + if( keys['y']) SDL_SaveBMP( screen, "screenshot.bmp"); + + if( keys[SDLK_ESCAPE] || keys['q']) return true; + + limit_value( &s->player1.y, PLAYER_Y_MIN, PLAYER_Y_MAX); + limit_value( &s->player2.y, PLAYER_Y_MIN, PLAYER_Y_MAX); + limit_value( &s->ball.jump, BALL_JUMP_MIN, BALL_JUMP_MAX); + + return false; +} + +void render( GameState* s) { + clearscr(); + show_image( GR_COURT, 0, 0, 255); + show_image( GR_SHADOW, s->ball.x, s->ball.y, 255); + + show_sprite( GR_RACKET, (!s->player1.state), 4, s->player1.x, s->player1.y, 255); + show_sprite( GR_RACKET, (!s->player2.state)+2, 4, s->player2.x, s->player2.y, 255); + + if( s->ground.jump) { + show_sprite( GR_GROUND, s->ground.jump-1, 3, s->ground.x, s->ground.y, 128); + } + + if( s->ball.move_x > 0) { + show_sprite( GR_BALL, (s->time/1000)%4, 4, s->ball.x, s->ball.y - get_phase( s), 255); + } else { + show_sprite( GR_BALL, 3-(s->time/1000)%4, 4, s->ball.x, s->ball.y - get_phase( s), 255); + } + + show_digit( s->player1.score/10%10, 140*2, 14, 100); + show_digit( s->player1.score%10, 148*2, 14, 100); + show_digit( 10, 156*2, 14, 100); + show_digit( s->player2.score/10%10, 164*2, 14, 100); + show_digit( s->player2.score%10, 172*2, 14, 100); + + updatescr(); +} + +void limit_value( float* value, float min, float max) { + if( *value < min) { + *value = min; + } else if( *value > max) { + *value = max; + } +} + +float get_phase( GameState* s) { + float pos, fract; + float x, min, max, direction; + + x = s->ball.x; + min = GAME_X_MIN; + max = GAME_X_MAX; + direction = s->ball.move_x; + + pos = (direction>0)?(1-GROUND_PHASE):(GROUND_PHASE); + + fract = (x-min)/(max-min); + + if( fract < pos) { + fract = fract/pos; + return fabsf( cosf(PI*fract/2))*PHASE_AMP*s->ball.jump; + } else { + fract = (pos-fract)/(1-pos); + return fabsf( sinf(PI*fract/2))*PHASE_AMP*s->ball.jump; + } +} + +float get_move_y( GameState* s, unsigned char player) { + float pct, dest, x_len, y_len; + float py, by, pa, move_x, phase; + + if( player == 1) { + py = s->player1.y; + } else { + py = s->player2.y; + } + by = s->ball.y - get_phase( s); + pa = PLAYER_AREA; + move_x = s->ball.move_x; + phase = get_phase( s); + + /* 0.0 .. 1.0 for racket hit position */ + pct = ((py+pa)-by)/(2*pa); + limit_value( &pct, 0.0, 1.0); + + /* Y destination for ball */ + dest = GAME_Y_MIN + pct*(GAME_Y_MAX-GAME_Y_MIN); + + /* lengths for the ball's journey */ + x_len = GAME_X_MAX - GAME_X_MIN; + y_len = dest - py + phase; + + /* return the should-be value for move_y */ + return (y_len*move_x)/(x_len); +} + diff --git a/game.h b/game.h index 7769dda..897ed52 100644 --- a/game.h +++ b/game.h @@ -4,9 +4,44 @@ #include #include "tennix.h" +typedef unsigned char bool; +enum { + false, + true +}; + +typedef struct { + float x; + float y; + float move_x; + float move_y; + float jump; +} Ball; + +typedef struct { + float x; + float y; + unsigned char state; + unsigned int score; + unsigned char responsible; /* responsible for the next fault (if any) */ +} Player; + +typedef struct { + Ball ball; + Ball ground; + Player player1; + Player player2; + float phase; + unsigned int time; + bool was_stopped; +} GameState; + #define PI 3.1415 -#define GROUND_PHASE 0.3 +#define GAME_TICKS 15 + +#define GROUND_PHASE 0.4 +#define PHASE_AMP 3.2 #define BALL_JUMP_MIN 4 #define BALL_JUMP_MAX 10 @@ -33,9 +68,11 @@ #define PLAYER_POWERSHOT 8.2 void game(); +void render( GameState*); +bool step( GameState*); void limit_value( float*, float, float); -float get_phase( float, float, float, float); -float get_move_y( float, float, float, float, float); +float get_phase( GameState*); +float get_move_y( GameState*, unsigned char); #endif diff --git a/graphics.c b/graphics.c index 4596924..cc3466d 100644 --- a/graphics.c +++ b/graphics.c @@ -23,11 +23,21 @@ static const char* filenames[] = { void init_graphics() { int i; SDL_Surface* data; + SDL_Surface* tmp; images = (Image*)calloc( GR_COUNT, sizeof( Image)); for( i=0; iformat, 0, 0, 0)); + data = SDL_DisplayFormatAlpha( tmp); + SDL_FreeSurface( tmp); + if( !data) { fprintf( stderr, "Error: %s\n", SDL_GetError()); continue; @@ -55,7 +65,6 @@ void show_sprite( unsigned int id, int pos, int items, int x_offset, int y_offse if( !bitmap) return; SDL_SetAlpha( bitmap, SDL_SRCALPHA | SDL_RLEACCEL, opacity); - SDL_SetColorKey( bitmap, SDL_SRCCOLORKEY, SDL_MapRGB( bitmap->format, 0, 0, 0)); dst.w = src.w = bitmap->w/items; dst.h = src.h = bitmap->h; @@ -111,5 +120,6 @@ void clearscr() { void updatescr() { SDL_UpdateRect( screen, 0, 0, 0, 0); + SDL_Flip( screen); } diff --git a/tennix.c b/tennix.c index 4337533..01aeb01 100644 --- a/tennix.c +++ b/tennix.c @@ -16,7 +16,8 @@ int main( int argc, char** argv) { Uint8 *keys; Uint8 mb; SDL_Event e; - int sdl_flags = SDL_HWSURFACE; + SDL_VideoInfo* vi; + int sdl_flags = SDL_SWSURFACE | SDL_DOUBLEBUF; srand( (unsigned)time( NULL)); @@ -25,16 +26,11 @@ int main( int argc, char** argv) { exit( 1); } - init_sound(); - init_graphics(); - atexit( SDL_Quit); -#ifdef WIN32 - sdl_flags = SDL_SWSURFACE; -#endif + vi = (SDL_VideoInfo*)SDL_GetVideoInfo(); - if( (screen = SDL_SetVideoMode( WIDTH, HEIGHT, 16, sdl_flags)) == NULL) { + if( (screen = SDL_SetVideoMode( WIDTH, HEIGHT, vi->vfmt->BitsPerPixel, sdl_flags)) == NULL) { fprintf( stderr, "Can't set video mode: %s\n", SDL_GetError()); exit( 1); } @@ -42,6 +38,9 @@ int main( int argc, char** argv) { SDL_WM_SetCaption( "Tennix! SDL", "Tennix"); SDL_ShowCursor( SDL_DISABLE); SDL_EnableKeyRepeat (SDL_DEFAULT_REPEAT_DELAY, 1); + + init_sound(); + init_graphics(); while( 1) { SDL_PollEvent( &e); -- 2.11.4.GIT