ゲームを作りたい!

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

SDL2でマウス入力を検知する

今回はSDL2を使用してマウス入力の検知をする。 キーボードに次いでマウスからの入力ができればゲームの操作性が色々と考えられるようになって来るから楽しくなってくるよね。 では、早速マウス入力の検知はどのようにすればできるの調べていこう。

マウスイベントを検知する

まず、マウスイベントを検知するためには次の関数を使用するようだ。
-> SDL_Point
-> SDL_MouseButtonEvent
詳細は、リンク先を参照してもらうことにして、ざっくり説明すると
SDL_Pointを使用するとウィンドウ上のマウスの座標を得ることができて、
SDL_MouseButtonEventを使用することで右クリック左クリック等の識別ができる。

実にさらっとマウス入力の検知ができるようなので、前回のキーボート入力の検知のソース(-> ここを参照)をちょっと書き換えてマウス入力の検知を行った。

#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;

typedef enum {right, left} CONTROL;

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

    const int IMAGE_WIDTH_SENTER = IMAGE_WIDTH * MAGNIFICATION / 2;
    const int IMAGE_HEIGHT_SENTER = IMAGE_HEIGHT * MAGNIFICATION / 2;

    SDL_Window *window = NULL;
    SDL_Renderer *renderer = NULL;
    SDL_Surface *image = NULL;
    SDL_Texture *image_texture = NULL;

    // ウィンドウの位置
    SDL_Point window_position = {
        SDL_WINDOWPOS_CENTERED,
        SDL_WINDOWPOS_CENTERED
    };

    // ウィンドウのサイズ
    SDL_Point window_size = {SCREEN_WIDTH, SCREEN_HEIGHT};

    // マウスの座標    
    SDL_Point mouse_position = {IMAGE_WIDTH_SENTER, IMAGE_WIDTH_SENTER};

    //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( "KEY EVENT TEST", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
                                                 SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN );

    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;

    CONTROL status = right;

    // main loop
    while (1) {

        int x = ((frame / animecycle) % 4) * 16;
        SDL_Rect imageRect=(SDL_Rect){x,0,IMAGE_WIDTH,IMAGE_HEIGHT};
        SDL_Rect drawRect=(SDL_Rect){mouse_position.x - (IMAGE_WIDTH_SENTER),
                                     mouse_position.y - (IMAGE_HEIGHT_SENTER),
                                     IMAGE_WIDTH*MAGNIFICATION,IMAGE_HEIGHT*MAGNIFICATION};

        SDL_RenderClear(renderer);
        if (status == right) {
            SDL_RenderCopy(renderer, image_texture, &imageRect, &drawRect);
        } else if (status == left) {
            SDL_RenderCopyEx(renderer, image_texture, &imageRect, &drawRect,0 , NULL ,SDL_FLIP_HORIZONTAL);
        }
        SDL_RenderPresent(renderer);

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

        // 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_MOUSEBUTTONDOWN && e.button.button == SDL_BUTTON_RIGHT){
                // マウスのウィンドウ上の座標を得る
                SDL_GetMouseState(&mouse_position.x, &mouse_position.y);
                status = right;
            } else if (e.type == SDL_MOUSEBUTTONDOWN && e.button.button == SDL_BUTTON_LEFT){
                SDL_GetMouseState(&mouse_position.x, &mouse_position.y);
                status = left;
            }
        }
    }

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

    SDL_Quit();

    return 0;

}

このコードを実行すると、クリックした位置に画像が移動する。
また、右左どちらのクリックを検知しているかわかりやすくするために、
左クリックだと画像が左向き、右クリックだと画像が右向きになるようにしてみた。

マウス検知のために追加した部分

今回のマウス検知で新たに追加されたのは以下の部分。
SDL_Pointでウィンドウの位置やサイズを設定して、あとはSDL_MouseButtonEventを使用してクリックした箇所の座標を拾っている。

// ウィンドウの位置
SDL_Point window_position = {
    SDL_WINDOWPOS_CENTERED,
    SDL_WINDOWPOS_CENTERED
};

// ウィンドウのサイズ
SDL_Point window_size = {SCREEN_WIDTH, SCREEN_HEIGHT};

// マウスの座標    
SDL_Point mouse_position = {IMAGE_WIDTH_SENTER, IMAGE_WIDTH_SENTER};
// 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_MOUSEBUTTONDOWN && e.button.button == SDL_BUTTON_RIGHT){
        // マウスのウィンドウ上の座標を得る
        SDL_GetMouseState(&mouse_position.x, &mouse_position.y);
        status = right;
    } else if (e.type == SDL_MOUSEBUTTONDOWN && e.button.button == SDL_BUTTON_LEFT){
        SDL_GetMouseState(&mouse_position.x, &mouse_position.y);
        status = left;
    }
}

その他の追加した部分

以下の部分では画像の貼り付け先の左上のx座標・y座標を指定してるが、マウスから取得した座標そのままだと
クリックした位置の右下に画像が出力されるのでクリックした位置が画像の中央になるように位置合わせをしている。

SDL_Rect drawRect=(SDL_Rect){mouse_position.x - (IMAGE_WIDTH_SENTER),
                             mouse_position.y - (IMAGE_HEIGHT_SENTER),
                             IMAGE_WIDTH*MAGNIFICATION,IMAGE_HEIGHT*MAGNIFICATION};

以下の部分では今まで使ったことのない新たな関数を使用している。
これは、左クリックの時にキャラクターに左を向かせるために画像を反転させるために使用している。
詳細は以下のリンクで確認できる。
-> SDL_RenderCopyEx
-> SDL_RendererFlip

SDL_RenderClear(renderer);
if (status == right) {
    SDL_RenderCopy(renderer, image_texture, &imageRect, &drawRect);
} else if (status == left) {
    SDL_RenderCopyEx(renderer, image_texture, &imageRect, &drawRect, 0, NULL ,SDL_FLIP_HORIZONTAL);
}

実行結果

こんな感じでクリックに合わせて画像が移動する。
f:id:K38:20181113232916g:plain

終わりに

これで、だいたいこれからゲームを作るにあたって基本的な所は調べたかなと思う。
あとは、どんなゲームを作るかによって色々と調べることが変わるだろうからどんなゲーム作るか少し考えてみようかな。とりあえず、今回はこんな感じで終わります。それではまた次回。