SDLでボン○ーマンを作ってみる

今回から数回に分けてボン○ーマン風ゲームを、

少しずつ作成していきたいと思います。



まずは簡単な処理から。

キャラクターデータの表示と、FPSの表示を行います。





サンプル画像

SDL_ttf


ソースコードはこちら

http://www.geocities.jp/finnissy/data/sample.zip



main.c

#include "main.h"
#include "import.h"

static boolean init(void);
static void run(void);
static void proc_event(void);
static void key_event(SDLKey key);
static void notify_end(void);
static void quit(void);
static void wait(void);
static void fps(void);

extern void blit(int x,int y,SDL_Surface *surface);
static void push(int x,int y,Uint16 w,Uint16 h);
static void update(void);
static void move_abs(int x,int y,int mval);
static void move_pos(int x,int y);


static SDL_Surface *screen;         //ウィンドウ画像ポインタ
static SDL_Surface *back_buffer;    //バックバッファー

static SDL_Rect rects[UPDATE_MAX];  //更新情報テーブル

static struct {                     //前フレーム更新情報
    SDL_Rect rect;
    Uint32 address;
} old_draw[UPDATE_MAX];

static struct {                         //矩形情報の合計
    int now;
    int old;
} numrect;

static boolean active;              //ゲームフラグ
static Uint32 elapsed_ms;           //経過ミリ秒
static Uint32 fps_rate;             //fps計算用
static Uint16 fps_cnt;              //fpsカウント
static SDLKey key_state;            //Key Code
static Sint16 screenX = FRAME_WIDTH/2;  //横軸
static Sint16 screenY = FRAME_HEIGHT/2; //縦軸
static boolean pressed;             //押下

#define MVAL    (2)                 //移動量
#define FPS_X   (0)
#define FPS_Y   (0)
#define FPS_W   (FRAME_WIDTH/2)
#define FPS_H   (FONT_SIZE)

/*  イメージ構造体  */
struct Images{
    SDL_Surface *back;
    SDL_Surface *ball;
    //必要になればここに追加する
}images;


int main(int argc, char *argv[])
{
    if(init())
        run();
    quit();

    return 0;
}

static
boolean init(void)
{
    int x,y;

    /****************************/
    /*  ライブラリを初期化する  */
    /****************************/
    if(SDL_Init(INIT_FLAG_SUBSYSTEM) < 0) {
        printf("初期化に失敗");
        return FALSE;
    }

    /****************************/
    /*      スクリーンの初期化  */
    /****************************/
    screen = SDL_SetVideoMode(
        FRAME_WIDTH,FRAME_HEIGHT,FRAME_BPP,INIT_FLAG_VIDEO
    );
    
    if(screen == NULL)  {
        printf("Error get video memory");
        return FALSE;
    }
    
    /********************************/
    /*  バックバッファーの初期化    */
    /********************************/
    back_buffer = SDL_AllocSurface(
                    screen->flags,
                    screen->w,
                    screen->h,
                    screen->format->BitsPerPixel,
                    screen->format->Rmask,
                    screen->format->Gmask,
                    screen->format->Bmask,
                    screen->format->Amask
                );
    if(back_buffer == NULL) {
        printf("Error get video memory");
        return FALSE;
    }

    /********************************/
    /*      フォントの初期化        */
    /********************************/
    if( !ttf_init(screen,PATH_FONT,FONT_SIZE, NULL) ){
        printf("Error open fontimage");
        return FALSE;
    }

    /************************************/
    /*          画像の読み込み          */
    /************************************/
    images.back = SDL_LoadBMP(PATH_IMAGE01);
    images.ball = SDL_LoadBMP(PATH_IMAGE02);
    if(!images.back || !images.ball)
    {
        printf("Error open picture image");
        return FALSE;
    }
    /*  透過色を設定する    */
    // TODO:マルチプラットフォーム対応なら
    //      画像からピクセル値を取得する方が良い
    SDL_SetColorKey(images.ball, SDL_SRCCOLORKEY, 0x000000);

    /************************/
    /*      背景の用意      */
    /************************/
    for(y = 0;y < WIDTH_SIZE;y++)
    {
        for(x = 0;x < HEIGHT_SIZE;x++)
        {
            SDL_Rect r;
            r.x = x*BLOCK_SIZE;
            r.y = y*BLOCK_SIZE;

            SDL_BlitSurface(images.back,NULL,back_buffer, &r);
        }
    }
    //  screen Blit Image
    SDL_BlitSurface(back_buffer,NULL,screen,NULL);
    blit(screenX, screenY, images.ball);

    rects[0].x = 0;
    rects[0].y = 0;
    rects[0].w = screen->w;
    rects[0].h = screen->h;
    numrect.now++;
    

    return TRUE;
}


static
void run(void)
{
    active = TRUE;

    while(active)
    {
        proc_event();
        if(pressed)
        {
            key_event(key_state);
            blit(screenX, screenY, images.ball);
        }
        wait();
        fps();
        update();
    }
}

static
void proc_event(void)
{
    SDL_Event ev;

    while( SDL_PollEvent(&ev) )
    {
        switch(ev.type)
        {
        case SDL_QUIT:      /*ウィンドウの[×]ボタン*/
            printf("QUIT\n");
            notify_end();
            break;
        case SDL_KEYDOWN:   /*キーダウン*/
            printf("KeyDonw\n");
            pressed = TRUE;
            key_state = ev.key.keysym.sym;
            break;
        case SDL_KEYUP:
            printf("KeyUp\n");
            pressed = FALSE;
            break;
        case SDL_MOUSEBUTTONDOWN:   /*マウスダウン*/
            printf("MouseDonw\n");
            break;
        default:
            break;
        }
    }
}

static
void key_event(SDLKey key)
{
    if(key == SDLK_ESCAPE)              //終了
        notify_end();           else
    if(key == SDLK_RIGHT)               //右移動
        move_abs(1,0,MVAL);     else
    if(key == SDLK_LEFT)                //左移動
        move_abs(-1,0,MVAL);    else
    if(key == SDLK_UP)                  //上移動
        move_abs(0,-1,MVAL);    else
    if(key == SDLK_DOWN)                //下移動
        move_abs(0,1,MVAL);     else
    ;
}

static void move_abs(int x,int y,int mval)
{
    screenX = (screenX + x*mval) <= -BLOCK_SIZE ?
        FRAME_WIDTH - BLOCK_SIZE    :
        (screenX + x*mval) % FRAME_WIDTH;

    screenY = (screenY + y*mval) <= -BLOCK_SIZE ?
        FRAME_HEIGHT - BLOCK_SIZE   :
        (screenY + y*mval) % FRAME_HEIGHT;
}

static void move_pos(int x,int y)
{
    screenX = (x < 0) ?
        0   :   x % FRAME_WIDTH;

    screenY = (y < 0) ? 
        0   :   y % FRAME_HEIGHT;
}

static
void notify_end(void)
{
    active = FALSE;
}

static
void wait(void)
{
    Uint32 ms;
    const Uint8 frate = 16;

    ms = SDL_GetTicks() - elapsed_ms;

    if(ms < frate)
    {
        SDL_Delay(frate - ms);
        //printf("wait() : %d\n", frate - ms);
    }

    elapsed_ms = SDL_GetTicks();
}

static
void fps(void)
{
    if( !fps_cnt )
    {
        fps_rate = SDL_GetTicks();
        fps_cnt++;
    } else
    if(++fps_cnt >= FRAME_RATE)
    {
        Uint32 elapsed = SDL_GetTicks() - fps_rate;
        Uint32 fps = (Uint32)(1000.0f/((double)elapsed/(double)FRAME_RATE));
        SDL_Rect r = { FPS_X,FPS_Y,FPS_W,FPS_H };
        
        SDL_BlitSurface(back_buffer, &r, screen, &r);
        r = ttf_puta_color(FPS_X,FPS_Y,0x000000,"FPS : %.2d",fps);
        push(FPS_X,FPS_Y,FPS_W,FPS_H);
        
        fps_rate = 0;   //60回分の処理時間をリセット
        fps_cnt = 0;    //1秒間の処理回数カウントをリセット
    }
}

static
void quit(void)
{
    if(screen != NULL)
        SDL_FreeSurface( screen );

    SDL_Quit();
}

static
void blit(int x,int y,SDL_Surface *surface)
{
    SDL_Rect tmp;
    int old;

    old = -1;
    if(numrect.old < UPDATE_MAX)
    {
        int i;

        for(i = 0;i < numrect.old;i++)
        {
            if(old_draw[i].address == (Uint32)surface)
            {
                /****************************/
                /*      裏画面を描画        */
                /****************************/
                SDL_Rect tmp = old_draw[i].rect;
                push(tmp.x,tmp.y,tmp.w,tmp.h);
                SDL_BlitSurface(back_buffer, &tmp, screen, &tmp);
                old = i;
                break;
            }
        }
    }

    tmp.w = surface->w;
    tmp.h = surface->h;
    tmp.x = x;
    tmp.y = y;

    /*  画像データを描画    */
    SDL_BlitSurface(surface, NULL, screen, &tmp);
    push(tmp.x,tmp.y,tmp.w,tmp.h);

    /*  描画情報をコピー    */
    if(old != -1)
    {
        old_draw[old].rect = tmp;
    } else {
        old_draw[numrect.old].address = (Uint32)surface;
        old_draw[numrect.old].rect = tmp;
        numrect.old++;
    }
}

static
void push(int x,int y,Uint16 w,Uint16 h)
{
    if(numrect.now < UPDATE_MAX)
    {
        int i = numrect.now;
#if 0
        rects[i].x = x < 0 ?    0 : x%FRAME_WIDTH;
        rects[i].y = y < 0 ?    0 : y%FRAME_HEIGHT;
        rects[i].w = w % (FRAME_WIDTH - rects[i].x);
        rects[i].h = h % (FRAME_HEIGHT - rects[i].y);
#else
        rects[i].x = x;
        rects[i].y = y;
        rects[i].w = w;
        rects[i].h = h;
#endif
        numrect.now++;
    }
}

static
void update(void)
{
    /********************************************/
    /*  与えられた矩形情報を元に画面の更新      */
    /********************************************/
    SDL_UpdateRects(screen, numrect.now, rects);

    /********************************************/
    /*          矩形情報をクリアする            */
    /********************************************/
    memset(rects,0,sizeof(rects));
    numrect.now = 0;
}