画面にキャラクターデータの描画
こんにちは。
プログラムで2Dアニメーションを実装する方法は色々あると思いますが、
今回はSDLのライブラリを使ったアニメーションを、図解で説明したいと
思います。
今回のソースです。
http://www.geocities.jp/finnissy/data/AlphaBlitTest.zip
予めアニメーションの背景となるピクセルデータと、アニメーション
させるキャラクターのピクセルデータを用意しておきます。
この画像を一定の間隔で連続して描画する事で、
アニメーションさせる事が出来ます。
普通の2Dゲームだとフレームレートが60FPSぐらいです。
(1秒間に約60回ぐらい画面を更新します)
毎フレーム、アニメーションさせる画像の座標軸を少しづつ変える事で、
画像が動いて見えます。
今回は3つの実装方法を紹介したいと思います。
1:毎フレーム全画面のバッファを交換する
SDLは低レベルなライブラリですから、ビデオメモリが貧弱 or システム
メモリを使う場合は、めんどくさがって毎フレーム全画面リフレッシュ
すると、かなりのマシンパワーを必要とします。
冗長なロジックで実装
2:キャラクターデータのみ描画する
この方法だと、1に比べれば処理がだいぶ軽くなりますが、
前フレームで描画した内容がそのまま残ってますので、
このままでは使えません。
つまり前フレームで描画した情報を消去する必要があります。
中途半端なロジックで実装
毎フレーム、描画した内容を消さない
3:描画した箇所のみ裏画面・更新処理を行う
最後にキャラクターを描画した箇所を読み込み、
描画した箇所に合わせて背景を描画する。
必要のあるところだけ更新
毎フレーム裏画面を描画
main.h
#ifndef MAIN_H #define MAIN_H #include <SDL/SDL.h> #include <stdio.h> /* ライブラリのインポート */ #pragma comment(lib,"SDL.lib") #pragma comment(lib,"SDLmain.lib") // 0と1でゲームモードを変更 // 0 : 冗長なロジックで処理 // 1 : 最適なロジックで処理 #define GAMEMODE 1 #define BACKDRAW 1 #define FRAME_WIDTH 600 #define FRAME_HEIGHT 600 #define FRAME_BPP 32 #define UPDATE_MAX (100) #define INIT_FLAG_SUBSYSTEM (SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) #define INIT_FLAG_VIDEO (SDL_SWSURFACE) typedef SDL_bool Bool; #define FALSE (SDL_FALSE) #define TRUE (SDL_TRUE) #define BACKSCREEN_PATH ("./data/backpic.bmp") #define CURSOR_ICON_PATH ("./data/cursor.bmp") #define CURSOR_ICON_PATH2 ("./data/cursor2.bmp") #endif |
main.c
#include "main.h" static Bool init(); static void run(); static void quit(); static void set_cursor(SDL_Surface *img); static void blit_cursor(void); static void push(SDL_Rect *src); static void update(void); static SDL_Surface *load_image(const char *path, int a_flg, Uint32 a_color); static void blit(SDL_Surface *s,int x,int y); static void blitR(SDL_Surface *s,SDL_Rect *r); static SDL_Rect *get_oldUpdate(void); static void set_oldUpdate(SDL_Surface *s, int x,int y); SDL_Surface *screen; SDL_Surface *backScreen; SDL_Surface *backBuffer; SDL_Surface *cursor; SDL_Surface *cursor2; SDL_Surface *now_cursor; int totalRect; SDL_Rect rects[UPDATE_MAX]; SDL_Rect oldUpdate; static int mouseX,mouseY; int main(int argc, char *argv[]) { if( init() ) run(); quit(); return 0; } static Bool init(void) { /* ライブラリを初期化する */ 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("VideoInitialize Error"); return FALSE; } /* 背景画像の読み込み */ backScreen = SDL_LoadBMP( BACKSCREEN_PATH ); if( backScreen == NULL ){ printf("error BackImage Load \n"); return FALSE; } #if GAMEMODE >= 1 /* 空のサーフェースを確保 */ backBuffer = SDL_AllocSurface( screen->flags, //元のビデオフラグ screen->w, //幅 screen->h, //高さ screen->format->BitsPerPixel, //ピクセルのビット深度 screen->format->Rmask, //赤のマスク screen->format->Gmask, //緑のマスク screen->format->Bmask, //青のマスク screen->format->Amask //透過色のマスク ); /* バックバッファーに転送 */ SDL_BlitSurface(backScreen,NULL,backBuffer,NULL); #endif cursor = load_image(CURSOR_ICON_PATH,1,0x000000); if(cursor == NULL) return FALSE; cursor2 = load_image(CURSOR_ICON_PATH2,1,0x000000); if(cursor2 == NULL) return FALSE; /* カーソルのセット */ set_cursor( cursor ); /* マウスカーソルを非表示 */ SDL_ShowCursor( 0 ); return TRUE; } static void run(void) { SDL_Event ev; blit(backScreen,0,0); ev.type = SDL_MOUSEMOTION; SDL_PushEvent( &ev ); /****************************************/ /* 次のイベントが来るまで無限に待機 */ /****************************************/ while( SDL_WaitEvent(&ev) ) { switch(ev.type) { /*ウィンドウの×ボタン*/ case SDL_QUIT: printf("QUIT\n"); return; /*キーダウン*/ case SDL_KEYDOWN: printf("KeyDonw\n"); if(ev.key.keysym.sym == SDLK_ESCAPE) return; break; /* マウスアクションイベント */ case SDL_MOUSEBUTTONDOWN: printf("MouseDonw\n"); set_cursor( cursor2 ); // mouse cursor change blit_cursor(); update(); break; case SDL_MOUSEBUTTONUP: printf("MouseUp\n"); set_cursor( cursor ); // mouse cursor change blit_cursor(); update(); break; case SDL_MOUSEMOTION: printf("mouse move\n"); blit_cursor(); update(); break; default: break; } } } static void quit(void) { if(screen != NULL) SDL_FreeSurface( screen ); if(backScreen != NULL) SDL_FreeSurface( backScreen ); if(cursor != NULL) SDL_FreeSurface( cursor ); if(cursor2 != NULL) SDL_FreeSurface( cursor2 ); SDL_Quit(); } static void set_cursor(SDL_Surface *img) { now_cursor = img; } static void blit_cursor(void) { #if GAMEMODE >= 1 #else SDL_Rect rect; #endif /* カーソル画像がセットされてない */ if(now_cursor == NULL) return; #if GAMEMODE >= 1 #if BACKDRAW > 0 SDL_SetAlpha(backBuffer,0,0); blitR( backBuffer, get_oldUpdate() ); #endif /* マウスを描画 */ SDL_GetMouseState( &mouseX, &mouseY ); blit(now_cursor,mouseX,mouseY); set_oldUpdate(now_cursor,mouseX,mouseY); #else /* 背景を描画 */ SDL_BlitSurface(backScreen,NULL,screen,NULL); /* マウスを描画 */ SDL_GetMouseState( &mouseX, &mouseY ); rect.x = mouseX; rect.y = mouseY; rect.w = 0; //使わない rect.h = 0; //使わない SDL_BlitSurface(now_cursor,NULL,screen,&rect); #endif } static void push(SDL_Rect *src) { if(totalRect < UPDATE_MAX) { int i = totalRect; rects[i] = (*src); printf("push Rect X:%d\tY:%d\tW:%d\tH:%d\n",src->x,src->y,src->w,src->h); /* 更新する矩形の数をインクリメント */ totalRect++; } } static void update(void) { #if GAMEMODE >= 1 /********************************************/ /* 与えられた矩形情報を元に画面の更新 */ /********************************************/ SDL_UpdateRects(screen, totalRect, rects); /********************************************/ /* 矩形情報をクリアする */ /********************************************/ memset(rects,0,sizeof(rects)); totalRect = 0; #else //全体を更新する SDL_UpdateRect(screen, 0, 0, 0, 0); #endif } static SDL_Surface * load_image(const char *path, int a_flg, Uint32 a_color) { /* カーソル画像の読み込み */ SDL_Surface *r; SDL_Surface *t = SDL_LoadBMP( path ); if( t == NULL ){ printf("Image Load Error! \n"); return NULL; } if( a_flg ) { /* 黒を透過色としてセット */ SDL_SetColorKey(t,SDL_SRCCOLORKEY,a_color); r = SDL_DisplayFormat(t); SDL_FreeSurface(t); return r; } return t; } static void blit(SDL_Surface *s,int x,int y) { SDL_Rect rect,t; int r; if(s == NULL) return; rect.w = s->w; rect.h = s->h; rect.x = x; rect.y = y; t = rect; printf("blit Rect X:%d\tY:%d\tW:%d\tH:%d\n",rect.x,rect.y,rect.w,rect.h); r = SDL_BlitSurface(s,NULL,screen,&t); if(r < 0) { printf("error BlitSurface\n"); } else { printf("BlitSurface Ok\n"); push( &rect ); } } static void blitR(SDL_Surface *s,SDL_Rect *r) { SDL_Rect rect,t; int ret; if(s == NULL) return; rect = (*r); t = rect; printf("blit Rect X:%d\tY:%d\tW:%d\tH:%d\n",rect.x,rect.y,rect.w,rect.h); ret = SDL_BlitSurface(s,&t,screen,&t); if(ret < 0) { printf("error BlitSurface\n"); } else { printf("BlitSurface Ok\n"); push( &rect ); } } static SDL_Rect *get_oldUpdate(void) { return &oldUpdate; } static void set_oldUpdate(SDL_Surface *s, int x,int y) { oldUpdate.w = (s->w+1) % FRAME_WIDTH; oldUpdate.h = (s->h+1) % FRAME_HEIGHT; oldUpdate.x = x < 0 ? 0 : x; oldUpdate.y = y < 0 ? 0 : y; } |