ゲームを作りたい!

プログラミングをしているからには、いつかはゲームを作ってみたい。勉強したことを備忘録的に綴るBLOG。

SDL2でアニメーションを表示する

今回はSDL2を使用してアニメーションを表示していこうと思う。
基本的には-> ここの記事のコードをベースにしている。

アニメーションさせる画像の準備

今回アニメーションに使う画像は以下である。
f:id:K38:20181031233811j:plain:w100
16×16の画像が4枚横につながっていて16×64の画像になっている。
これを、4分割に切り取って順番に表示させることでアニメーションになる。

ソースコードと画像ファイルの階層は以下のようになっている。

sdl2_test/
  |-animation.c
  |-walkcat.bmp

アニメーションさせる

今回は新たなSDL2の関数は使用していないので関数へのリンクは無し。
ただし、アニメーションさせるために導入した式と使用したSDL2の関数の説明を
次の項目で説明してみようと思う。
ちなみに、SDL_InitSDL_CreateWindowはIf文の入れ子にしないで同じ深さになるようにしてみた。

#include <stdio.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>

const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
const int IMAGE_WIDTH = 16; 
const int IMAGE_HEIGHT = 16; 
const int MAGNIFICATION = 4;

int main (int argc, char *argv[]) {
    SDL_Window* window = NULL;
    SDL_Renderer *renderer = NULL;
    SDL_Surface *image = NULL;
    SDL_Texture *image_texture = NULL;

    //Initialize SDL
    if( SDL_Init( SDL_INIT_VIDEO ) < 0 ) { 
        printf( "SDL could not initialize! SDL_Error: %s\n", SDL_GetError() );
        return 1;
    }   

    window = SDL_CreateWindow( "DRAW IMAGE TEST", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIG
    if( window == NULL ) {
        printf( "Window could not be created! SDL_Error: %s\n", SDL_GetError() );
        return 1;
    } else {
        renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
    }


    // 画像の読み込み
    image = IMG_Load("walkcat.bmp");
    if(!image) {
        printf("IMG_Load: %s\n", IMG_GetError());
        return 1;
    }

    // 透過色の設定
    SDL_SetColorKey( image, SDL_TRUE, SDL_MapRGB(image->format, 255, 0, 255));
    image_texture = SDL_CreateTextureFromSurface(renderer, image);

    SDL_SetRenderDrawColor(renderer, 200, 200, 200, 255);

    int iw,ih;     
    SDL_QueryTexture(image_texture, NULL, NULL, &iw, &ih);


    int animecycle = 60;
    int frame = 0;

    // main loop
    while (frame < 3000) {

        int x = ((frame / animecycle) % 4) * 16;
        SDL_Rect imageRect=(SDL_Rect){x,0,IMAGE_WIDTH,IMAGE_HEIGHT};
        SDL_Rect drawRect=(SDL_Rect){300,220,IMAGE_WIDTH*MAGNIFICATION,IMAGE_HEIGHT*MAGNIFICATION};

        SDL_RenderClear(renderer);
        SDL_RenderCopy(renderer, image_texture, &imageRect, &drawRect);
        SDL_RenderPresent(renderer);

        frame += 1;

    }

    IMG_Quit();
    SDL_FreeSurface(image);
    SDL_DestroyTexture(image_texture);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);

    SDL_Quit();

    return 0;

}

アニメーション部分の説明

アニメーションは、読み込んだ画像を次々と切り替えることで実現することができる。
これは、今回で言うと4分割した画像をなんらかのタイミングで切り替えて表示することができれば
アニメーションになるということだ。

その、画像の分割とタイミングを決めているのがこの部分。

    int animecycle = 60;
    int frame = 0;

    // main loop
    while (frame < 3000) {

        int x = ((frame / animecycle) % 4) * 16;
        SDL_Rect imageRect=(SDL_Rect){x,0,IMAGE_WIDTH,IMAGE_HEIGHT};
        SDL_Rect drawRect=(SDL_Rect){300,220,IMAGE_WIDTH*MAGNIFICATION,IMAGE_HEIGHT*MAGNIFICATION};

        SDL_RenderClear(renderer);
        SDL_RenderCopy(renderer, image_texture, &imageRect, &drawRect);
        SDL_RenderPresent(renderer);

        frame += 1;

    }

imageRectdrawRectは、SDL_RenderCopyに渡すSDL_Rect構造体になる。
SDL_Rect構造体とは、左上を基点とした長方形を定義する構造体のこと。
imageRectはコピー元のSDL_Rectで、drawRectはコピー先のSDL_Rectだ。
SDL_Rect構造体は、

int     x       長方形の左上のX座標
int     y       長方形の左上のY座標
int     w       長方形の幅
int     h       長方形の高さ

で構成されている。
よって、コピー元の画像を4分割してコピー先に渡すためには
x座標の位置を変えて画像を切り取っていけば良い。
今回使用している画像は一枚16×16の画像になるのでx座標の位置を
0, 16, 32, 48と変化させれば良いことになる。

x座標の位置を変化させるために使用している式がこの式。

int x = ((frame / animecycle) % 4) * 16;

(frame / animecycle) % 4の部分で、0〜3までの数値を算出している。
そこに16をかけることでx座標の位置がでる。
また、この式を使うとframeがanimecycle分増加することで次の画像へと移り変わるので
アニメーションの間隔を自分で設定することができる。
数値の切り替わりは以下のようになっている。

frame=0   (  0 / 60) % 4 = 0
frame=60  ( 60 / 60) % 4 = 1
frame=120 (120 / 60) % 4 = 2
frame=180 (180 / 60) % 4 = 3
frame=240 (240 / 60) % 4 = 0
frame=300 (300 / 60) % 4 = 1
frame=360 (360 / 60) % 4 = 2
frame=420 (420 / 60) % 4 = 3

以下同様に続く

以上が、アニメーションの説明になる。

もうひとつ説明して起きたいのがこの部分。

SDL_Rect drawRect=(SDL_Rect){300,220,IMAGE_WIDTH*MAGNIFICATION,IMAGE_HEIGHT*MAGNIFICATION};

ここで定義しているMAGNIFICATIONは画像の倍率。
というのも、SDL_RenderCopyはコピー先で指定された幅と高さに画像を自動で拡大縮小してくれる。
MAGNIFICATIONを定義することで、好きな倍率に設定できるようにした。
今回はMAGNIFICATIONの部分を4にしているので、実際の画像より4倍大きく拡張して表示を行っている。

実行結果

実行結果はこんな感じ。ちゃんと、アニメーションになっている。
f:id:K38:20181031234641g:plain:w300

終わりに

今回はSDL2を使用してアニメーションを表示してみた。
この部分をうまく関数化すれば色々使いまわせるようになるんだろうなぁと思ったり。
次回は、SDL2を使ったキーボード入力の検知の方法を調べようと思う。

どうでもいいんだけど説明するっていうのは難しい。
頑張ってまとめてみたけど、後で見返した時に頭に???が浮かびそう。
文章力も鍛えてかないと記事書くのがコード書くより時間かかってしまうなぁ。