ゲームを作りたい!

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

SDL2でRPGゲームを作る 〜第三回 キャラクターに進行方向を向かせる〜

前回はマップとの当たり判定を実装して猫を草地に閉じ込めることができた。しかしながら、どの方向に動いても 常に右向きの状態で移動していた。今回はキャラクターに進行方向を向かせる方法を考えたい。

表示画像の変更

まず、今まで使用していた画像は以下。
f:id:K38:20181212005929j:plain
このように、16×64の右方向を向いている画像だけを使っていた。
今回は、上下左右どの方向も向けるように以下のように64×64の画像を用意した。
f:id:K38:20181212010111j:plain
この画像を使用して進行方向を向かせていく。
まず、前回から変更した部分のコードを示す。前回から大きくは変わっていないが キャラクターに向きの概念を導入した。

typedef enum {DOWN, LEFT, RIGHT, UP} DIRECTION;
DIRECTION direction = DOWN;

// event handling
SDL_Event e;
if ( SDL_PollEvent(&e) ) {
    if (e.type == SDL_QUIT){
        break;
    } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE){
        break;
    } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_UP){
        direction = UP;
        if (is_movable(mx, my - 1) == 0) {
            my = my - 1;
        }
    } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_DOWN){
        direction = DOWN;
        if (is_movable(mx, my + 1) == 0) {
            my = my + 1;
        }
    } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_RIGHT){
        direction = RIGHT;
        if (is_movable(mx + 1, my) == 0) {
            mx = mx + 1;
        }
    } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_LEFT){
        direction = LEFT;
        if (is_movable(mx - 1, my) == 0) {
            mx = mx - 1;
        }
    }
}
int character_animation(SDL_Renderer *renderer, DIRECTION direction, int mx, int my) {

    SDL_Texture *cat_image = NULL;

    load_image(renderer, &cat_image, "image/charachip/black_cat.bmp");

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

    SDL_Rect imageRect=(SDL_Rect){x, y, IMAGE_WIDTH, IMAGE_HEIGHT};
    SDL_Rect drawRect=(SDL_Rect){mx * GRID_SIZE, my * GRID_SIZE, IMAGE_WIDTH*MAGNIFICATION, IMAGE_HEIGHT*MAGNIFICATION};

    SDL_RenderCopy(renderer, cat_image, &imageRect, &drawRect);

    if (frame <= animecycle * 4) {
        frame += 1;
    } else{
        frame = 0;
    }

    SDL_DestroyTexture(cat_image);

    return 0;
}

キー入力時に上下左右の状態を取得し、character_animationで画像を表示するときに画像のY座標の位置を int y = direction * IMAGE_HEIGHT;
で求めている。
こうすることで、進行方向の画像を入力に合わせて表示することができた。

以下に、全コードをのせる。

#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 = 2;
const int ROW = 15;
const int COL = 20;
const int GRID_SIZE = 32;
int animecycle = 60;
int frame = 0;

typedef enum {DOWN, LEFT, RIGHT, UP} DIRECTION;

int load_image(SDL_Renderer *, SDL_Texture **, char *);
int character_animation(SDL_Renderer *, DIRECTION, int, int);
int draw_map(SDL_Renderer *);
int is_movable(int, int);

int map[300] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
                1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
                1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
                1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
                1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
                1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
                1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
                1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
                1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
                1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
                1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
                1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
                1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
                1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};


int main (int argc, char *argv[]) {

    SDL_Window *window = NULL;
    SDL_Renderer *renderer = 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);
    }

    int mx = 10;
    int my = 8;
    DIRECTION direction = DOWN;

    // main loop
    while (1) {
        SDL_RenderClear(renderer);
        draw_map(renderer);
        character_animation(renderer, direction, mx, my);
        SDL_RenderPresent(renderer);

        // event handling
        SDL_Event e;
        if ( SDL_PollEvent(&e) ) {
            if (e.type == SDL_QUIT){
                break;
            } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE){
                break;
            } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_UP){
                direction = UP;
                if (is_movable(mx, my - 1) == 0) {
                    my = my - 1;
                }
            } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_DOWN){
                direction = DOWN;
                if (is_movable(mx, my + 1) == 0) {
                    my = my + 1;
                }
            } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_RIGHT){
                direction = RIGHT;
                if (is_movable(mx + 1, my) == 0) {
                    mx = mx + 1;
                }
            } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_LEFT){
                direction = LEFT;
                if (is_movable(mx - 1, my) == 0) {
                    mx = mx - 1;
                }
            }
        }
    }

    IMG_Quit();
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);

    SDL_Quit();

    return 0;

}

int load_image(SDL_Renderer *renderer, SDL_Texture **image_texture, char *filename) {

    SDL_Surface *image = NULL;

    // 画像の読み込み
    image = IMG_Load(filename);
    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_FreeSurface(image);

    return 0;
}

int character_animation(SDL_Renderer *renderer, DIRECTION direction, int mx, int my) {

    SDL_Texture *cat_image = NULL;

    load_image(renderer, &cat_image, "image/charachip/black_cat.bmp");

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

    SDL_Rect imageRect=(SDL_Rect){x, y, IMAGE_WIDTH, IMAGE_HEIGHT};
    SDL_Rect drawRect=(SDL_Rect){mx * GRID_SIZE, my * GRID_SIZE, IMAGE_WIDTH*MAGNIFICATION, IMAGE_HEIGHT*MAGNIFICATION};

    SDL_RenderCopy(renderer, cat_image, &imageRect, &drawRect);

    if (frame <= animecycle * 4) {
        frame += 1;
    } else{
        frame = 0;
    }

    SDL_DestroyTexture(cat_image);

    return 0;
}

int draw_map(SDL_Renderer *renderer){

    SDL_Texture *grass_image = NULL;
    SDL_Texture *water_image = NULL;

    load_image(renderer, &water_image, "image/mapchip/water.bmp");
    load_image(renderer, &grass_image, "image/mapchip/grass.bmp");

    int x, y;
    for(y = 0;y < ROW;y++){
        for(x = 0; x < COL;x++){

            SDL_Rect imageRect=(SDL_Rect){0, 0, IMAGE_WIDTH, IMAGE_HEIGHT};
            SDL_Rect drawRect=(SDL_Rect){x * GRID_SIZE, y * GRID_SIZE, IMAGE_WIDTH*MAGNIFICATION, IMAGE_HEIGHT*MAGNIFICATION}

            if (map[y*COL+x] == 1) {
                SDL_RenderCopy(renderer, water_image, &imageRect, &drawRect);
            } else if (map[y*COL+x] == 0) {
                SDL_RenderCopy(renderer, grass_image, &imageRect, &drawRect);
            }

        }
    }

    SDL_DestroyTexture(grass_image);
    SDL_DestroyTexture(water_image);

    return 0;
}


int is_movable(int x, int y) {

    if ( x < 0 || x > COL - 1 || y  < 0 || y > ROW - 1) {
        return 1;
    }

    if (map[y*COL+x] == 1 ){
        return 1;
    }

    return 0;
}

実行結果

今回実行した結果は以下。
f:id:K38:20181212010533g:plain
このように猫に進行方向を向かせることができた。

終わりに

猫が進行方向を向いたことで移動はなんとなくいい感じになってきた。
しかしながら、ドラクエなどを思い返すと主人公は常に画面の真ん中に表示されてたように思う。
次回は、そのような表示をさせる方法について考えてみたいと思う。