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


今回は、爆弾を置いて爆弾が消えるまでの処理を追加。


サンプル画像







ソースコードはこちら

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




gfx.c

#include "gfx.h"
#include "import.h"
#include "libttf.h"
#include <stdio.h>

#define ID_BOMM (0x100)
#define ID_BACK (0x200)
#define OVER_COUNT (120)    //16ms * 240count = 4sec

// private method
static void back_graund(SDL_Surface *surface,const char *path);
static void push(SDL_Rect *r);
static void bomm_count(void);
static void add_bomm(int x, int y);
static void del_bomm(int id);
static void clean_screen(int x,int y,int w,int h,int id);
static void bomm_reset(void);

typedef struct bomm_list{
    int x,y;
    int map_x,map_y;
    int id;
    int cnt;
    struct bomm_list *next;
} Bomm_List;

static SDL_Surface *screen;         //ウィンドウ画像ポインタ
static SDL_Surface *back_buffer;    //バックバッファー
static SDL_Rect rects[UPDATE_MAX];  //更新情報
static Uint32 fps_rate;             //fps計算用
static Uint16 fps_cnt;              //fpsカウント
static Sint16 map[HEIGHT_SIZE][WIDTH_SIZE];//2次元表
static Bomm_List *bomm_top;
static Uint32 bomms = ID_BOMM;

static const int FPS_X  =   0;
static const int FPS_Y  =   0;
static const int FPS_W  =   FRAME_WIDTH/2;
static const int FPS_H  =   FONT_SIZE;

static const int FIELD_BOMM = 3;
static const int FIELD_DEATH = 4;

static struct {
    SDL_Rect    r;                  //前フレーム更新情報
    int id;
} old_draw[UPDATE_MAX];
static struct {
    SDL_Surface *surface;
    SDL_Rect    r;                  //更新情報テーブル
    int id;
} now_draw[UPDATE_MAX];
static struct {                     //矩形情報の合計
    int now;
    int old;
    int update;
} numrect;

boolean init_gfx(void)
{
    if(screen != NULL)
        return TRUE;

    /****************************/
    /*      スクリーンの初期化  */
    /****************************/
    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;
    }

    /************************************/
    /*          画像の読み込み          */
    /************************************/
    //TODO:配列にまとめて代入
    images.back = SDL_LoadBMP(PATH_IMAGE01);
    images.ball = SDL_LoadBMP(PATH_IMAGE02);
    images.wall1 = SDL_LoadBMP(PATH_IMAGE03);
    images.wall2 = SDL_LoadBMP(PATH_IMAGE04);
    images.bomm1 = SDL_LoadBMP(PATH_IMAGE05);
    images.bomm2 = SDL_LoadBMP(PATH_IMAGE06);
    images.bomm3 = SDL_LoadBMP(PATH_IMAGE07);
    images.bomm4 = SDL_LoadBMP(PATH_IMAGE08);
    if(!images.back || !images.ball || !images.wall1 || !images.wall2
    ||  !images.bomm1 || !images.bomm2 || !images.bomm3 || !images.bomm4 )
    {
        printf("Error open picture image");
        return FALSE;
    }
    /*  透過色を設定する    */
    // TODO:マルチプラットフォーム対応なら
    //      画像からピクセル値を取得する方が良い
    SDL_SetColorKey(images.ball, SDL_SRCCOLORKEY, 0x000000);
    SDL_SetColorKey(images.wall2, SDL_SRCCOLORKEY, 0x000000);
    SDL_SetColorKey(images.bomm1, SDL_SRCCOLORKEY, 0x000000);
    SDL_SetColorKey(images.bomm2, SDL_SRCCOLORKEY, 0x000000);
    SDL_SetColorKey(images.bomm3, SDL_SRCCOLORKEY, 0x000000);
    SDL_SetColorKey(images.bomm4, SDL_SRCCOLORKEY, 0x000000);

    /************************/
    /*      背景の用意      */
    /************************/
    back_graund(back_buffer,PATH_MAP01);

    //  screen Blit Image
    SDL_BlitSurface(back_buffer,NULL,screen,NULL);

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

    return TRUE;
}

void quit_gfx(void)
{
    int i;
    const int img_size = sizeof(images.arr) / sizeof(SDL_Surface*);

    if(screen != NULL)
        SDL_FreeSurface( screen );

    /*  イメージ構造体を解放する    */
    for(i = 0; i < img_size; i++)
    {
        if(images.arr[i] != NULL)
            SDL_FreeSurface(images.arr[i]);
    }
}

void fps(int elapsed,int frame_rate)
{
    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,0xAABBFF,"FPS : %.2d",fps);
        push( &r );

        fps_rate = 0;   //60回分の処理時間をリセット
        fps_cnt = 0;    //1秒間の処理回数カウントをリセット
    }
}

void blit(int x,int y,SDL_Surface *surface,int id)
{
    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
        now_draw[i].r.x = x;
        now_draw[i].r.y = y;
        now_draw[i].r.w = surface->w;
        now_draw[i].r.h = surface->h;
        now_draw[i].surface = surface;
        now_draw[i].id = id;
#endif
        numrect.now++;
    }
}

boolean map_calc(int x,int y, int state)
{
    int x1 = x / BLOCK_SIZE;
    int y1 = y / BLOCK_SIZE;
    int x2 = (x + BLOCK_SIZE-1) / (BLOCK_SIZE);
    int y2 = (y + BLOCK_SIZE-1) / (BLOCK_SIZE);

    switch( state )
    {
    case MV_LEFT:   return (!map[y1][x1] && !map[y2][x1]);
    case MV_RIGHT:  return (!map[y1][x2] && !map[y2][x2]);
    case MV_UP:     return (!map[y1][x1] && !map[y1][x2]);
    case MV_DOWN:   return (!map[y2][x1] && !map[y2][x2]);
    default:        break;
    }

    printf("move start point\n");
    return TRUE;
}

void draw(void)
{
    boolean flg;
    int i,j;

    /****************************/
    /*      裏画面を描画        */
    /****************************/
    for(i = 0;i < numrect.now;i++)
    {
        flg = FALSE;
        for(j = 0;j < numrect.old;j++)  //前フレームで描画した場所は裏画面を描画
        {
            if(now_draw[i].id == old_draw[j].id)
            {
                SDL_Rect tmp = old_draw[j].r;
                
                push( &tmp );
                SDL_BlitSurface(back_buffer, &tmp, screen, &tmp);
                old_draw[j].r = now_draw[i].r;  //update the Rect Infomation

                flg = TRUE;
                break;
            }
        }
        
        if( !flg && (numrect.old < UPDATE_MAX)) //初登録のID
        {
            old_draw[numrect.old].r = now_draw[i].r;
            old_draw[numrect.old].id = now_draw[i].id;
            numrect.old++;
        }
    }

    /****************************/
    /*      画像を描画          */
    /****************************/
    for(i = 0;i < numrect.now;i++)
    {
        SDL_Rect tmp = now_draw[i].r;

        push( &tmp );
        if(now_draw[i].surface == back_buffer)
        {
            SDL_BlitSurface(now_draw[i].surface, &tmp, screen, &tmp);
        } else {
            SDL_BlitSurface(now_draw[i].surface, NULL, screen, &tmp);
        }
    }
    memset( now_draw, 0, sizeof(now_draw));
    numrect.now = 0;
}


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

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


void bomm_start(int x,int y)
{
    int x1 = (x + (BLOCK_SIZE>>1)) / BLOCK_SIZE;
    int y1 = (y + (BLOCK_SIZE>>1)) / BLOCK_SIZE;

    if(map[y1][x1] != FIELD_BOMM)
    {
        map[y1][x1] = FIELD_BOMM;
        printf("add_bomm\n");
        add_bomm(x1,y1);

        add_func( bomm_count );
    }
}


/************************************************/
/*              プライベートメソッド            */
/************************************************/

static
void back_graund(SDL_Surface *surface,const char *path)
{
    FILE *fp;
    boolean flg;
    int x,y;

    fp = fopen(path,"r");
    if(fp == NULL)
    {
        printf("Error Mapfile loading\n");
        return;
    }

    flg = FALSE;
    for(y = 0;y < WIDTH_SIZE;y++)
    {
        for(x = 0;x < HEIGHT_SIZE;x++)
        {
            SDL_Rect r;
            int c;
            
            r.x = x*BLOCK_SIZE;
            r.y = y*BLOCK_SIZE;
            
            while(!isdigit(c = fgetc(fp)) && !feof(fp) && !ferror(fp));
            
            if(feof(fp) || ferror(fp))
            {
                flg = TRUE;
                break;
            }

            map[y][x] = c = c - '0';
            switch( c )
            {
            case 0: SDL_BlitSurface(images.back,NULL,surface, &r);  break;
            case 1: SDL_BlitSurface(images.wall1,NULL,surface, &r); break;
            case 2:
                    SDL_BlitSurface(images.back,NULL,surface, &r);
                    SDL_BlitSurface(images.wall2,NULL,surface, &r); break;
            default :   break;
            }
        }

        if( flg )
            break;
    }

    fclose(fp);
}


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


static
void bomm_count(void)
{
    Bomm_List *tmp;
    int anim_rate = (FRAME_RATE/2);
    float interval = (FRAME_RATE/8.0f);

    tmp = bomm_top;
    if(tmp == NULL)
    {
        del_func( bomm_count ); //爆弾アニメーションを解除する
        bomm_reset();
    } else {
        do {
            if(++(tmp->cnt) % FRAME_RATE < anim_rate)
            {
                int cnt = tmp->cnt % anim_rate;
                if(cnt < interval)      blit(tmp->x,tmp->y,images.bomm1,tmp->id);   else
                if(cnt < interval*2)    blit(tmp->x,tmp->y,images.bomm2,tmp->id);   else
                if(cnt < interval*3)    blit(tmp->x,tmp->y,images.bomm3,tmp->id);   else
                if(cnt < interval*4)    blit(tmp->x,tmp->y,images.bomm4,tmp->id);
            } else {
                int cnt = tmp->cnt % anim_rate;
                if(cnt < interval)      blit(tmp->x,tmp->y,images.bomm4,tmp->id);   else
                if(cnt < interval*2)    blit(tmp->x,tmp->y,images.bomm3,tmp->id);   else
                if(cnt < interval*3)    blit(tmp->x,tmp->y,images.bomm2,tmp->id);   else
                if(cnt < interval*4)    blit(tmp->x,tmp->y,images.bomm1,tmp->id);
            }

            if(tmp->cnt == OVER_COUNT)  //爆弾カウント時間切れ
            {
                Bomm_List *t = tmp;

                printf("bomm_clear\n");
                printf("ID:%d\n",t->id);
                
                clean_screen
                    (tmp->x,tmp->y,BLOCK_SIZE,BLOCK_SIZE,tmp->id);
                map[tmp->map_y][tmp->map_x] = 0;
                tmp = tmp->next;    //次の爆弾へ
                del_bomm(t->id);
            } else {
                tmp = tmp->next;    //次の爆弾へ
            }
        } while(tmp != NULL);
    }
}

static
void add_bomm(int x, int y)
{
    Bomm_List *bomm = (Bomm_List*)malloc(sizeof(Bomm_List));

    if(bomm == NULL)
        return;

    memset(bomm,0,sizeof(Bomm_List));
    bomm->id = bomms;
    bomm->x = x*BLOCK_SIZE;
    bomm->y = y*BLOCK_SIZE;
    bomm->map_x = x;
    bomm->map_y = y;
    bomm->next = bomm_top;

    bomm_top = bomm;
    printf("id:%d\n",bomm->id);

    bomms++;
    printf("bomm num:%d",bomms);
}

static
void del_bomm(int id)
{
    Bomm_List *tmp;
    Bomm_List *prev;

    tmp = bomm_top;
    prev = NULL;
    while(tmp != NULL)
    {
        if(tmp->id == id)
        {
            if(prev != NULL)
            {
                prev->next = tmp->next;
                free(tmp);
                break;
            } else {
                free(tmp);
                bomm_top = NULL;
                break;
            }
        }
        prev = tmp;
        tmp = tmp->next;
    }
}

static
void clean_screen(int x,int y,int w,int h,int id)
{
#if 0
    SDL_Rect r;

    r.x = (x < 0) ? 0 : x%(back_buffer->w - x);
    r.y = (y < 0) ? 0 : y%(back_buffer->h - y);
    r.w = w;
    r.h = h;

    push( &r );
    SDL_BlitSurface(back_buffer, &r, screen, &r);
#else
    if(numrect.now < UPDATE_MAX)
    {
        int i = numrect.now;

        now_draw[i].r.x = (x < 0) ? 0 : x%FRAME_WIDTH;
        now_draw[i].r.y = (y < 0) ? 0 : y%FRAME_HEIGHT;
        now_draw[i].r.w = w;
        now_draw[i].r.h = h;
        now_draw[i].surface = back_buffer;
        now_draw[i].id = id;

        numrect.now++;
    }
#endif
}

static
void bomm_reset(void)
{
    bomms = ID_BOMM;
}