ゲームを作りたい!

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

SDL2でRPGゲームを作る 〜第11回 バイナリデータを読み込む〜

前回投稿してから約1ヶ月半経過しての投稿になる。 平成から令和に元号が変わるにあたって色々と忙しくなり、なかなか投稿することができなかった。
加えて、資格を取る必要がありOracle silver 12cという資格を取得したのだが、勉強中は趣味の時間が取れず プログラムを書くことができなかった。しかし、また、少しずつ投稿できればと思う。
※次は、AI関連の資格を取れと言われているからまた趣味の時間が取れなくなりそうだ。。。

さて、前回はNPCを動かした。そして、手の混んだフィールドを作ろうとしたが、マップチップが10個しか使えないという事実に気がついたところで止まっていた。だから、今回はマップチップをバイナリデータで保持することで、マップチップの使用上限を増やすということをしたい。
実際は、こちらのMAPEditerで作成したバイナリデータを読み込む処理を作っていきたい。

バイナリデータを読み込む

さて、いきなりだが変更した箇所を示したいと思う。

変更前

int load_map(char *map_name) {
    FILE *fp;
    int map_num;

    fp = fopen(map_name, "r");
    if (fp == NULL) {
        printf("file open error. %d\n", __LINE__);
        return 1;
    }

    fscanf(fp, "%d%d", &COL, &ROW);
    fscanf(fp, "%d", &OUT_OF_MAP);

    int i = 0;
    while((map_num = fgetc(fp)) != EOF){
        if(map_num != 0x0d){
            if(map_num != 0x0a){
                map_array[i] = map_num - 48;
                i++;
            }
       }
    }


    return 0;
}

変更後

int load_map(char *map_name) {
    FILE *fp;
    int i = 0;
    if ((fp = fopen(map_name, "rb")) == NULL) {
        return 1;
    }

    fread(&COL, sizeof(int), 1, fp);
    fread(&ROW, sizeof(int), 1, fp);
    fread(&OUT_OF_MAP, sizeof(int), 1, fp);

    map_array = realloc(map_array, sizeof(int) * COL * ROW);

    while(!feof(fp)) {
        fread(&map_array[i++], sizeof(int), 1, fp);
    }

    fclose(fp);

    return 0;
}

変更前と変更後の違いは、文字列を読み込むためにfscanfを使うか、バイナリを読み込むためにfreadを使うかの違いである。文字列を読み込んでいるときは改行コードで1行の読込みを判断していたが、バイナリデータになったことで単純に行×列のサイズを読み込めばマップを読み込めるようになった。

全コード表示

では、全コードを記載する。

load.h

#ifdef _RPGINC
#else

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

typedef enum {DOWN, LEFT, RIGHT, UP} DIRECTION;
typedef enum {FALSE, TRUE} MOVING;

typedef struct {
    int map_x;
    int map_y;
    int pixel_x;
    int pixel_y;
    int offset_x;
    int offset_y;
    int velocity_x;
    int velocity_y;
    DIRECTION direction;
    MOVING moving;
} CARACTER;

typedef struct {
    CARACTER npc;
    char message[1024];
    SDL_Texture *npc_image;
    MOVING npc_move;
} NPC;

typedef struct {
    int mapchip_id;
    char mapchip_name[256];
    int movable;
    int change_locate;
    SDL_Texture *map_image;
} MAPCHIP;


int clac_offset(int, int, int *, int *);
int load_image(SDL_Renderer *, SDL_Texture **, char *);
int player_animation(SDL_Renderer *);
int player_update(SDL_Renderer *, SDL_Event);
int player_move(SDL_Event);

int load_npc(SDL_Renderer *);
int npc_animation(SDL_Renderer *);
int npc_update(SDL_Renderer *renderer, int);
int npc_move(DIRECTION, int);

int load_map_image(SDL_Renderer *, SDL_Texture **);
int load_mapchip(SDL_Renderer *);
int load_move(SDL_Renderer *);
int load_map(char *);
int draw_map(SDL_Renderer *);
int is_movable(int, int);
int fade_out(SDL_Renderer *);

#endif

load.c

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include "RPGInC.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 GRID_SIZE = 32;
int ROW = 15;
int COL = 20;
int OUT_OF_MAP = 0;
char MAP_EVENT_NAME[256] = "field";

int animecycle = 24;
int speed = 2;
int frame = 0;
int number_of_map_image = 0;
int number_of_npc_image = 0;

CARACTER player = {1, 1, 32, 32, 0, 0, 0, 0, DOWN, FALSE};
NPC npc[256] = {0};

MAPCHIP mapchip[256] = {0};

int *map_array;


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_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);
    }

    load_mapchip(renderer);
    load_map("data/field.map");

    load_npc(renderer);

    // main loop
    while (1) {
        SDL_Event e;

        clac_offset(player.pixel_x, player.pixel_y, &player.offset_x, &player.offset_y);

        SDL_RenderClear(renderer);
        draw_map(renderer);

        npc_animation(renderer);

        player_animation(renderer);
        player_update(renderer, e);
        SDL_RenderPresent(renderer);

        // event handling
        if ( SDL_PollEvent(&e) ) {
            if (e.type == SDL_QUIT){
                break;
            } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE){
                break;
            }
        }

    }

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

    int i;
    for (i = 0;i < number_of_map_image;i++) {
        SDL_DestroyTexture(mapchip[i].map_image);
    }

    for (i = 0;i < number_of_npc_image;i++) {
        SDL_DestroyTexture(npc[i].npc_image);
    }

    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 player_animation(SDL_Renderer *renderer) {

    SDL_Texture *cat_image = NULL;
    load_image(renderer, &cat_image, "image/charachip/black_cat.bmp");
    // load_image(renderer, &cat_image, "image/charachip/white_cat.bmp");

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

    SDL_Rect imageRect=(SDL_Rect){x, y, IMAGE_WIDTH, IMAGE_HEIGHT};
    SDL_Rect drawRect=(SDL_Rect){player.pixel_x - player.offset_x, player.pixel_y - player.offset_y,
                                 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 player_update(SDL_Renderer *renderer, SDL_Event e) {

    if (player.moving == TRUE) {
        player.pixel_x = player.pixel_x + player.velocity_x;
        player.pixel_y = player.pixel_y + player.velocity_y;

        if (player.pixel_x % GRID_SIZE == 0 && player.pixel_y % GRID_SIZE == 0){
            player.moving = FALSE;
            player.map_x = player.pixel_x / GRID_SIZE;
            player.map_y = player.pixel_y / GRID_SIZE;

            load_move(renderer);
            player_move(e);
        }

    } else {
        player_move(e);
    }

}

int player_move(SDL_Event e) {

    if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_UP){
        player.direction = UP;
        if (is_movable(player.map_x, player.map_y - 1) == 0) {
            player.velocity_x = 0;
            player.velocity_y = -speed;
            player.moving = TRUE;
        }
    } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_DOWN){
        player.direction = DOWN;
        if (is_movable(player.map_x, player.map_y + 1) == 0) {
            player.velocity_x = 0;
            player.velocity_y = speed;
            player.moving = TRUE;
        }
    } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_RIGHT){
        player.direction = RIGHT;
        if (is_movable(player.map_x + 1, player.map_y) == 0) {
            player.velocity_x = speed;
            player.velocity_y = 0;
            player.moving = TRUE;
        }
    } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_LEFT){
        player.direction = LEFT;
        if (is_movable(player.map_x - 1, player.map_y) == 0) {
            player.velocity_x = -speed;
            player.velocity_y = 0;
            player.moving = TRUE;
        }
    }

    return 0;

}

int load_npc(SDL_Renderer *renderer) {
    char event_path[256];

    sprintf(event_path, "data/%s.evt", MAP_EVENT_NAME);

    FILE *fp;
    char event[256];
    char npc_name[256];
    int map_x;
    int map_y;
    DIRECTION direction;
    MOVING moving;
    int max_step;
    char message[1024];

    char buf[256];
    char npc_path[256];
    int i = 0;
    int element_number = 0;

    fp = fopen(event_path, "r");
    if (fp == NULL) {
        printf("file open error. %d\n", __LINE__);
        return 1;
    }

    for (i = 0;i < number_of_npc_image;i++) {
        npc[i].npc.map_x = 0;
        npc[i].npc.map_y = 0;
        npc[i].npc.pixel_x = 0;
        npc[i].npc.pixel_y = 0;
        npc[i].npc.direction = 0;
        npc[i].npc.moving = 0;
        SDL_DestroyTexture(npc[i].npc_image);
        sprintf(npc[i].message, "%s", '\0');
    }

    for(i = 0;fgets(buf, sizeof(buf), fp) != NULL;i++) {

        if (strncmp(buf, "#", 1) != 0){
            if (strncmp(buf, "CHARA", 5) == 0) {
                sscanf(buf,
                   "%[^,],%[^,],%d,%d,%d,%d,%[^,]",
                       event, npc_name, &map_x, &map_y, &direction, &moving, message);

                sprintf(npc_path, "image/charachip/%s.bmp", npc_name);
                load_image(renderer, &npc[element_number].npc_image, npc_path);

                npc[element_number].npc.map_x = map_x;
                npc[element_number].npc.map_y = map_y;
                npc[element_number].npc.pixel_x = map_x * GRID_SIZE;
                npc[element_number].npc.pixel_y = map_y * GRID_SIZE;
                npc[element_number].npc.direction = direction;
                npc[element_number].npc_move = moving;

                sprintf(npc[element_number].message, "%s", message);

                element_number += 1;
            }
        }
    }

    number_of_npc_image = element_number;

    fclose(fp);

    return 0;
}

int npc_animation(SDL_Renderer *renderer) {

    int i;
    for(i = 0; number_of_npc_image >= i;i++) {

        int x = ((frame / animecycle) % 4) * 16;
        int y = npc[i].npc.direction * IMAGE_HEIGHT;

        SDL_Rect imageRect=(SDL_Rect){x/4, y/4, IMAGE_WIDTH/4, IMAGE_HEIGHT/4};
        SDL_Rect drawRect=(SDL_Rect){npc[i].npc.pixel_x - player.offset_x,
                                     npc[i].npc.pixel_y - player.offset_y,
                                     IMAGE_WIDTH*MAGNIFICATION, IMAGE_HEIGHT*MAGNIFICATION};

        SDL_RenderCopy(renderer, npc[i].npc_image, &imageRect, &drawRect);

        if (npc[i].npc_move == TRUE && number_of_npc_image != 0) {
            npc_update(renderer, i);
        }
    }

    return 0;
}

int npc_update(SDL_Renderer *renderer, int element) {

    srand((unsigned)time(NULL));
    int target = rand()%number_of_npc_image;
    int action = rand()%10;

    if (npc[element].npc_move == TRUE) {
        if (npc[element].npc.moving == TRUE) {
            npc[element].npc.pixel_x = npc[element].npc.pixel_x + npc[element].npc.velocity_x;
            npc[element].npc.pixel_y = npc[element].npc.pixel_y + npc[element].npc.velocity_y;

            if (npc[element].npc.pixel_x % GRID_SIZE == 0 && npc[element].npc.pixel_y % GRID_SIZE == 0) {
                npc[element].npc.moving = FALSE;
                npc[element].npc.map_x = npc[element].npc.pixel_x / GRID_SIZE;
                npc[element].npc.map_y = npc[element].npc.pixel_y / GRID_SIZE;
            }

        } else {
            if (target == element && action < 3) {
                npc_move(rand()%4, element);
            }
        }
    }
}

int npc_move(DIRECTION direction, int element) {

    if (frame == 0) {
        if (direction == UP){
            npc[element].npc.direction = UP;
            if (is_movable(npc[element].npc.map_x, npc[element].npc.map_y - 1) == 0) {
                npc[element].npc.velocity_x = 0;
                npc[element].npc.velocity_y = -speed;
                npc[element].npc.moving = TRUE;
            }
        } else if (direction == DOWN){
            npc[element].npc.direction = DOWN;
            if (is_movable(npc[element].npc.map_x, npc[element].npc.map_y + 1) == 0) {
                npc[element].npc.velocity_x = 0;
                npc[element].npc.velocity_y = speed;
                npc[element].npc.moving = TRUE;
            }
        } else if (direction == RIGHT){
            npc[element].npc.direction = RIGHT;
            if (is_movable(npc[element].npc.map_x + 1, npc[element].npc.map_y) == 0) {
                npc[element].npc.velocity_x = speed;
                npc[element].npc.velocity_y = 0;
                npc[element].npc.moving = TRUE;
            }
        } else if (direction == LEFT){
            npc[element].npc.direction = LEFT;
            if (is_movable(npc[element].npc.map_x - 1, npc[element].npc.map_y) == 0) {
                npc[element].npc.velocity_x = -speed;
                npc[element].npc.velocity_y = 0;
                npc[element].npc.moving = TRUE;
            }
        }
    }
    return 0;

}

int draw_map(SDL_Renderer *renderer){

    int x, y;
    int start_x = player.offset_x / GRID_SIZE - 1;
    int end_x = start_x + SCREEN_WIDTH / GRID_SIZE + 2;
    int start_y = player.offset_y / GRID_SIZE - 1;
    int end_y = start_y + SCREEN_HEIGHT/ GRID_SIZE + 2;


    for(y = start_y;y < end_y;y++){
        for(x = start_x; x < end_x;x++){

            SDL_Rect imageRect=(SDL_Rect){0, 0, IMAGE_WIDTH, IMAGE_HEIGHT};
            SDL_Rect drawRect=(SDL_Rect){(x * GRID_SIZE) - player.offset_x,
                                         (y * GRID_SIZE) - player.offset_y,
                                         IMAGE_WIDTH*MAGNIFICATION, IMAGE_HEIGHT*MAGNIFICATION};

            if ((x < 0) || (x > COL - 1) || (y < 0) || (y > ROW - 1)){
                SDL_RenderCopy(renderer, mapchip[OUT_OF_MAP].map_image, &imageRect, &drawRect);
            } else {
                SDL_RenderCopy(renderer, mapchip[map_array[y*COL+x]].map_image, &imageRect, &drawRect);
            }

        }
    }

    return 0;
}

int load_move(SDL_Renderer *renderer) {
    char event_path[256];

    sprintf(event_path, "data/%s.evt", MAP_EVENT_NAME);

    FILE *fp;
    char event[256];
    int event_point_x;
    int event_point_y;
    DIRECTION direction_of_penetration;
    char buf[256];
    char new_map_name[256];
    char map_path[256];
    int new_x;
    int new_y;
    int i = 0;

    fp = fopen(event_path, "r");
    if (fp == NULL) {
        printf("file open error. %d\n", __LINE__);
        return 1;
    }

    for(i = 0;fgets(buf, sizeof(buf), fp) != NULL;i++) {

        if (strncmp(buf, "#", 1) != 0){
            if (strncmp(buf, "MOVE", 4) == 0) {
                sscanf(buf,
                   "%[^,],%d,%d,%d,%[^,],%d,%d",
                       event, &event_point_x, &event_point_y, &direction_of_penetration, new_map_name, &new_x, &new_y);

                if (player.map_x == event_point_x && player.map_y == event_point_y) {
                    if (player.direction == direction_of_penetration) {
                        sprintf(MAP_EVENT_NAME, "%s", new_map_name);

                        sprintf(map_path, "data/%s.map", new_map_name);
                        load_map(map_path);

                        player.map_x = new_x;
                        player.map_y = new_y;
                        player.pixel_x = player.map_x * GRID_SIZE;
                        player.pixel_y = player.map_y * GRID_SIZE;

                        load_npc(renderer);

                        fade_out(renderer);
                    }
                }
            }
        }
    }

    fclose(fp);

    return 0;
}

int fade_out(SDL_Renderer *renderer) {

    SDL_Rect rectangle;
    SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);

    rectangle.x = 0;
    rectangle.y = 0;
    rectangle.w = SCREEN_WIDTH;
    rectangle.h = SCREEN_HEIGHT;

    int i = 200;
    int inverse_flg = 0;
    while(1) {
        SDL_SetRenderDrawColor(renderer, 0, 0, 0, i);
        SDL_RenderFillRect(renderer, &rectangle);
        SDL_RenderPresent(renderer);

        if (inverse_flg == 0 && i <= 255) {
            i = i + 5;
            SDL_Delay(80);
        }

        if (i == 255) {
           inverse_flg = 1;
        }

        if (inverse_flg == 1) {
            clac_offset(player.pixel_x, player.pixel_y, &player.offset_x, &player.offset_y);
            draw_map(renderer);
            player_animation(renderer);
            SDL_Delay(20);

            i = i - 5;

            if (i == 200) {
                break;
            }
        }
    }

    return 0;

}

int load_map(char *map_name) {
    FILE *fp;
    int i = 0;
    if ((fp = fopen(map_name, "rb")) == NULL) {
        return 1;
    }

    fread(&COL, sizeof(int), 1, fp);
    fread(&ROW, sizeof(int), 1, fp);
    fread(&OUT_OF_MAP, sizeof(int), 1, fp);

    map_array = realloc(map_array, sizeof(int) * COL * ROW);

    while(!feof(fp)) {
        fread(&map_array[i++], sizeof(int), 1, fp);
    }

    fclose(fp);

    return 0;
}

int load_mapchip(SDL_Renderer *renderer) {

    FILE *fp;
    int x, y, z;
    char n[256];
    char path[256];
    char buf[256];
    int i = 0;

    fp = fopen("data/mapchip.dat", "r");
    if (fp == NULL) {
        printf("file open error. %d\n", __LINE__);
        return 1;
    }

    for(i = 0;fgets(buf, sizeof(buf), fp) != NULL;i++){
        sscanf(buf, "%d,%[^,],%d,%d", &x, n, &y, &z);
        mapchip[i].mapchip_id = x;
        strcpy(mapchip[i].mapchip_name, n);
        mapchip[i].movable = y;
        mapchip[i].change_locate = z;

        sprintf(path, "image/mapchip/%s.bmp", mapchip[i].mapchip_name);
        load_image(renderer, &mapchip[i].map_image, path);

    }

    number_of_map_image = i - 1;

    fclose(fp);

    return 0;

}

int is_movable(int x, int y) {

    int i;
    for(i = 0;i < number_of_npc_image;i++) {
        if (npc[i].npc.map_x == x && npc[i].npc.map_y == y) {
            return 1;
        }
    }

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

    if (mapchip[map_array[y*COL+x]].movable == 1) {
        return 1;
    }

    if(player.map_x == x && player.map_y == y) {
        return 1;
    }

    return 0;
}

int clac_offset(int x, int y, int *offset_x, int *offset_y) {
    *offset_x = x - (SCREEN_WIDTH / 2);
    *offset_y = y - (SCREEN_HEIGHT / 2);

    return 0;
}

実行結果

実行結果はこんな感じ。10個以上のマップチップが使えている。
f:id:K38:20190521203153g:plain

終わりに

今回はバイナリデータの読込みを行った。ソースコードとしては大した変更は入れていないが これで、気合さえあれば凝ったマップが作れるようになった。次は、メッセージボックスの作成を行っていきたいと思う。

SDL2でMAPEditerを作る 〜第5回 保存・読込〜

前回は、保存するときのファイル名を入力するための文字入力Windowを作成したので、後は入力されたファイル名でMAPデータを保存する、そしてそれを読み込む部分を実装する。

バイナリ形式保存

-> 第1回で書いた通りバイナリ形式で保存する。C言語だとバイナリ書込・読込は関数一つでできるので割と簡単に実装できた。
まず、保存部分は以下のように実装した。

int save_file(char *file_name) {
    FILE *fp;
    char file[256] = {0};
    int i = 0;

    sprintf(FILE_NAME, "%s", file_name);
    while (FILE_NAME[i] != '\0') {
        FILE_NAME[i] = toupper((unsigned char)FILE_NAME[i]);
        i++;
    }

    sprintf(file, "data/%s.map", file_name);

    i = 0;
    while (file[i] != '\0') {
        file[i] = tolower((unsigned char)file[i]);
        i++;
    }

    if ((fp = fopen(file, "wb")) == NULL) {
        return 1;
    }

    fwrite(&COL, sizeof(int), 1, fp);
    fwrite(&ROW, sizeof(int), 1, fp);
    fwrite(&OUTER_PERIPHERY, sizeof(int), 1, fp);
    fwrite(map_array, sizeof(int) * COL * ROW, 1, fp);

    fclose(fp);

    return 0;
}

バイナリ書込モードでファイルをオープンして、fwrite関数で行・列・マップの外側・マップデータと順に書き込んでいる。あと、特殊なことをしている言えば入力された文字列を表示では大文字になるように、ファイル名は小文字になるように、toupper tolowerを使用して変換している。これは、なんとなくで実装したのでいらないっちゃあいらない部分でもある。

この関数を、-> 前回の文字入力Windowと組み合わせて以下のように、保存機能を実装した。

int display_save_window(SDL_Event e, SDL_Renderer *renderer, TTF_Font *font) {

    char file_name[21] = {0};
    char *status;
    char *endptr;

    make_box(renderer, 244, 296, 304, 32, 255, WHITE);
    make_box(renderer, 246, 298, 300, 28, 255, BLACK);

    display_character_string(renderer, font, "SAVE?:", 250, 300);

    // FONT_SIZE 22 ,11pt

    SDL_RenderPresent(renderer);

    sprintf(file_name, "%s", FILE_NAME);
    if (accept_character_input(e, renderer, font, file_name, 316, 525) == 0) {

        make_box(renderer, 244, 296, 304, 32, 255, WHITE);
        make_box(renderer, 246, 298, 300, 28, 255, BLACK);

        if(save_file(file_name) == 0) {
            status = "SAVED!";
        } else {
            status = "SAVING FAILD";
        }

        while(1) {
            make_box(renderer, 244, 296, 304, 32, 255, WHITE);
            make_box(renderer, 246, 298, 300, 28, 255, BLACK);
            display_character_string(renderer, font, status, 250, 300);
            SDL_RenderPresent(renderer);

            if ( SDL_PollEvent(&e) ) {
                if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_RETURN) {
                    break;
                }
            }
        }
    }

    return 0;
}

入力された文字列を先ほどのsave_file関数に渡している。
あと、メモとして残しておきたい部分は
* ESCキーでの処理抜けを実装するためにメインループとは別に文字入力window用のループを持っている
* 文字数制限20文字だがfile_name配列は21で宣言していること

file_name配列が21なのはC言語のstrcat関数を文字入力windowで使用していることによる。strcat関数は連結後の文字列に終端文字を自動的に付加してしまうので20文字配列に格納したいときは終端文字分のプラス1が必要になる。

バイナリ形式読込

続いてはバイナリ読込。こちらも、関数一つでできるので割と簡単に実装できた。

int load_file(char *file_name) {
    FILE *fp;
    char file[256] = {0};
    int i = 0;

    sprintf(file, "data/%s.map", file_name);

    while (file_name[i] != '\0') {
        file_name[i] = tolower((unsigned char)file_name[i]);
        i++;
    }

    if ((fp = fopen(file, "rb")) == NULL) {
        return 1;
    }

    sprintf(FILE_NAME, "%s", file_name);

    i = 0;
    while (FILE_NAME[i] != '\0') {
        FILE_NAME[i] = toupper((unsigned char)FILE_NAME[i]);
        i++;
    }

    fread(&COL, sizeof(int), 1, fp);
    fread(&ROW, sizeof(int), 1, fp);
    fread(&OUTER_PERIPHERY, sizeof(int), 1, fp);

    OUT_OF_MAP = 10;

    map_array = realloc(map_array, sizeof(int) * COL * ROW);

    i = 0;
    while(!feof(fp)) {
        fread(&map_array[i++], sizeof(int), 1, fp);
    }

    fclose(fp);

    return 0;
}

こちらは、fread関数を利用してデータを読み込んでいる。

で、呼び出す関数が以下。

int display_load_window(SDL_Event e, SDL_Renderer *renderer, TTF_Font *font) {

    char file_name[21] = {0};
    char *status;

    make_box(renderer, 244, 296, 304, 32, 255, WHITE);
    make_box(renderer, 246, 298, 300, 28, 255, BLACK);

    display_character_string(renderer, font, "LOAD?:", 250, 300);

    // FONT_SIZE 22 ,11pt

    SDL_RenderPresent(renderer);

    if (accept_character_input(e, renderer, font, file_name, 316, 525) == 0) {

        if(load_file(file_name) == 0) {
            status = "LOADED!";
        } else {
            status = "LOADING FAILD";
        }

        while(1) {
            make_box(renderer, 244, 296, 304, 32, 255, WHITE);
            make_box(renderer, 246, 298, 300, 28, 255, BLACK);
            display_character_string(renderer, font, status, 250, 300);
            SDL_RenderPresent(renderer);

            if ( SDL_PollEvent(&e) ) {
                if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_RETURN) {
                    break;
                }
            }
        }
    }

    return 0;
}

全コード記載

以上で、マップデータの保存・読込みが実装できた。以下に、全コードを載せる。

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

#define FONT_PATH "font/PixelMplus12-Bold.ttf"

const int SCREEN_WIDTH = 800;
const int SCREEN_HEIGHT = 600;
const int IMAGE_WIDTH = 16;
const int IMAGE_HEIGHT = 16;
const int MAGNIFICATION = 2;
const int GRID_SIZE = 32;
const int PALLET_ROW = 15;
const int PALLET_COL = 20;
const int FONT_SIZE = 22;

int ROW = 15;
int COL = 20;
int OUT_OF_MAP = 10;
int OUTER_PERIPHERY = 1;
int frame = 0;

typedef enum {WHITE, BLACK} COLOR;
typedef enum {CTRL_OFF, CTRL_ON} CTRL;
typedef enum {PALLET_DISPLAY_OFF, PALLET_DISPLAY_ON} PALLET_DISPLAY;

typedef struct {
    int map_x;
    int map_y;
    int offset_x;
    int offset_y;
} CURSOR;

typedef struct {
    int mapchip_id;
    char mapchip_name[256];
    int movable;
    int change_locate;
    SDL_Texture *map_image;
} MAPCHIP;

int accept_character_input(SDL_Event, SDL_Renderer *, TTF_Font *, char *, int, int);
int display_save_window(SDL_Event, SDL_Renderer *, TTF_Font *);
int display_character_string(SDL_Renderer *, TTF_Font *, char *, int, int);
int make_box(SDL_Renderer *, int, int, int, int, int, COLOR);
int load_file(char *);
int save_file(char *);
int display_save_window(SDL_Event, SDL_Renderer *, TTF_Font *);
int draw_coordinate(SDL_Renderer *, TTF_Font *);
int clac_offset(int, int, int *, int *);
int load_mapchip(SDL_Renderer *);
int draw_map(SDL_Renderer *);
int draw_pallet(SDL_Renderer *);
int cursor_move(SDL_Event, SDL_Renderer *);
int load_image(SDL_Renderer *, SDL_Texture **, char *);
int place_mapchip(SDL_Point, SDL_Renderer *);
int place_mapchip_with_key(SDL_Renderer *);
int get_mapchip(SDL_Point, SDL_Renderer *);
int get_mapchip_right_click(SDL_Point, SDL_Renderer *);
int get_mapchip_with_key(SDL_Renderer *);
int get_mapchip_with_key_a(SDL_Renderer *);
int initialize(SDL_Renderer *);


int *map_array;
int *pallet_array;
int number_of_map_image = 0;
int select_mapchip = 0;

CURSOR cursor = {1, 1, 0, 0};
CURSOR pallet_cursor = {1, 1, 0, 0};
MAPCHIP mapchip[256] = {0};

CTRL ctrl = CTRL_OFF;
PALLET_DISPLAY pallet_display = PALLET_DISPLAY_OFF;

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

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

    SDL_Window *window = NULL;
    SDL_Renderer *renderer = NULL;
    TTF_Font *font = 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( "MAP CREATER", 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);
    }

    //Initialize TTF
    if ( TTF_Init() < 0 ) {
        printf("TTFcould not initialize! TTF_Error: %s\n", TTF_GetError());
    }

    font = TTF_OpenFont(FONT_PATH, FONT_SIZE);
    if ( font == NULL ) {
        printf("TTF_OpenFont: %s\n", TTF_GetError());
    }

    initialize(renderer);

    // main loop
    while (1) {

        SDL_Delay(10);

        SDL_Event e;

        clac_offset(cursor.map_x, cursor.map_y, &cursor.offset_x, &cursor.offset_y);
        clac_offset(pallet_cursor.map_x, pallet_cursor.map_y, &pallet_cursor.offset_x, &pallet_cursor.offset_y);

        SDL_RenderClear(renderer);
        if (pallet_display == PALLET_DISPLAY_ON) {
            draw_pallet(renderer);
            pallet_move(e, renderer);
        } else {
            draw_map(renderer);
            cursor_move(e, renderer);
        }

        draw_selected_mapchip(renderer);

        draw_coordinate(renderer, font);


        SDL_RenderPresent(renderer);

        // event handling
        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_RCTRL || e.key.keysym.sym == SDLK_LCTRL)) {
                ctrl = CTRL_ON;
            } else if (e.type == SDL_KEYUP &&
                      (e.key.keysym.sym == SDLK_RCTRL || e.key.keysym.sym == SDLK_LCTRL)) {
                ctrl = CTRL_OFF;
            } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_s) {
                // save_file();
                if (ctrl == CTRL_ON) {
                    ctrl = CTRL_OFF;
                    display_save_window(e, renderer, font);
                }
            } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_l) {
                if (ctrl == CTRL_ON) {
                    ctrl = CTRL_OFF;
                    display_load_window(e, renderer, font);
                }
            } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_i) {
                if (pallet_display == PALLET_DISPLAY_OFF) {
                    pallet_display = PALLET_DISPLAY_ON;
                } else {
                    pallet_display = PALLET_DISPLAY_OFF;
                }
            } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_a &&
                       pallet_display == PALLET_DISPLAY_OFF) {
                get_mapchip_with_key_a(renderer);
            } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_o) {
                get_mapchip_with_key_o(renderer);
            } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_SPACE &&
                       pallet_display == PALLET_DISPLAY_ON) {
                get_mapchip_with_key(renderer);
            } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_SPACE &&
                       pallet_display == PALLET_DISPLAY_OFF) {
                place_mapchip_with_key(renderer);
            } else if (e.type == SDL_MOUSEBUTTONDOWN && e.button.button == SDL_BUTTON_LEFT &&
                       pallet_display == PALLET_DISPLAY_ON) {
                get_mapchip(mouse_position, renderer);
            } else if (e.type == SDL_MOUSEBUTTONDOWN && e.button.button == SDL_BUTTON_LEFT &&
                       pallet_display == PALLET_DISPLAY_OFF) {
                place_mapchip(mouse_position, renderer);
            } else if (e.type == SDL_MOUSEBUTTONDOWN && e.button.button == SDL_BUTTON_RIGHT &&
                       pallet_display == PALLET_DISPLAY_OFF) {
                get_mapchip_right_click(mouse_position, renderer);
            }
        }

    }

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

    int i;
    for (i = 0;i < number_of_map_image;i++) {
        SDL_DestroyTexture(mapchip[i].map_image);
    }

    SDL_Quit();

    return 0;

}

int initialize(SDL_Renderer *renderer) {


    map_array = malloc(sizeof(int) * COL * ROW);
    pallet_array = malloc(sizeof(int) * PALLET_COL * PALLET_ROW);

    load_mapchip(renderer);

    int i;
    for (i=0;i <= COL*ROW;i++) {
        map_array[i] = 1;
    }

    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 place_mapchip(SDL_Point mouse_position, SDL_Renderer *renderer) {

    SDL_GetMouseState(&mouse_position.x, &mouse_position.y);
    map_array[((cursor.offset_y + mouse_position.y) / GRID_SIZE)*COL +
              ((cursor.offset_x + mouse_position.x) / GRID_SIZE)] = select_mapchip;

    return 0;

}

int place_mapchip_with_key(SDL_Renderer *renderer) {

    map_array[(cursor.map_y / GRID_SIZE) * COL +
              (cursor.map_x / GRID_SIZE)] = select_mapchip;

    return 0;

}

int get_mapchip(SDL_Point mouse_position, SDL_Renderer *renderer) {

    SDL_GetMouseState(&mouse_position.x, &mouse_position.y);
    select_mapchip =
       pallet_array[((pallet_cursor.offset_y + mouse_position.y) / GRID_SIZE) * PALLET_COL +
                    ((pallet_cursor.offset_x + mouse_position.x) / GRID_SIZE)];

    return 0;

}

int get_mapchip_right_click(SDL_Point mouse_position, SDL_Renderer *renderer) {

    SDL_GetMouseState(&mouse_position.x, &mouse_position.y);
    select_mapchip =
       map_array[((cursor.offset_y + mouse_position.y) / GRID_SIZE) * COL +
                    ((cursor.offset_x + mouse_position.x) / GRID_SIZE)];

    return 0;

}

int get_mapchip_with_key(SDL_Renderer *renderer) {

    select_mapchip =
       pallet_array[(pallet_cursor.map_y / GRID_SIZE) * PALLET_COL +
                    (pallet_cursor.map_x / GRID_SIZE)];

    return 0;

}

int get_mapchip_with_key_a(SDL_Renderer *renderer) {

    select_mapchip =
       map_array[(cursor.map_y / GRID_SIZE) * COL +
                    (cursor.map_x / GRID_SIZE)];

    return 0;

}

int get_mapchip_with_key_o(SDL_Renderer *renderer) {

    if (pallet_display == PALLET_DISPLAY_ON) {
        OUTER_PERIPHERY =
           pallet_array[(pallet_cursor.map_y / GRID_SIZE) * PALLET_COL +
                        (pallet_cursor.map_x / GRID_SIZE)];
    } else {
        OUTER_PERIPHERY =
           map_array[(cursor.map_y / GRID_SIZE) * COL +
                        (cursor.map_x / GRID_SIZE)];
    }

    return 0;

}

int draw_selected_mapchip(SDL_Renderer *renderer) {
    make_box(renderer, 700, 30, 62, 62, 255, WHITE);
    make_box(renderer, 702, 32, 58, 58, 255, BLACK);

    SDL_Rect imageRect=(SDL_Rect){0, 0, IMAGE_WIDTH, IMAGE_HEIGHT};
    SDL_Rect drawRect=(SDL_Rect){715,
                                 45,
                                 IMAGE_WIDTH*MAGNIFICATION, IMAGE_HEIGHT*MAGNIFICATION};

    SDL_RenderCopy(renderer, mapchip[select_mapchip].map_image, &imageRect, &drawRect);


    return 0;
}

int cursor_move(SDL_Event e, SDL_Renderer *renderer) {

    int cursor_x = cursor.map_x - cursor.offset_x;
    int cursor_y = cursor.map_y - cursor.offset_y;

    make_box(renderer, cursor_x, cursor_y, GRID_SIZE, GRID_SIZE, 100, BLACK);

    if (frame % 5 == 0) {

        if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_UP){
            cursor.map_y = cursor.map_y - GRID_SIZE;
        } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_DOWN){
            cursor.map_y = cursor.map_y + GRID_SIZE;
        } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_RIGHT){
            cursor.map_x = cursor.map_x + GRID_SIZE;
        } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_LEFT){
            cursor.map_x = cursor.map_x - GRID_SIZE;
        }

     }

    if (frame <= 10000) {
        frame++;
    } else {
        frame = 0;
    }

    if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_k){
        cursor.map_y = cursor.map_y - GRID_SIZE;
    } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_j){
        cursor.map_y = cursor.map_y + GRID_SIZE;
    } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_l){
        cursor.map_x = cursor.map_x + GRID_SIZE;
    } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_h){
        cursor.map_x = cursor.map_x - GRID_SIZE;
    }

    return 0;

}

int pallet_move(SDL_Event e, SDL_Renderer *renderer) {

    int cursor_x = pallet_cursor.map_x - pallet_cursor.offset_x;
    int cursor_y = pallet_cursor.map_y - pallet_cursor.offset_y;

    make_box(renderer, cursor_x, cursor_y, GRID_SIZE, GRID_SIZE, 100, BLACK);

    if (frame % 5 == 0) {

        if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_UP){
            pallet_cursor.map_y = pallet_cursor.map_y - GRID_SIZE;
        } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_DOWN){
            pallet_cursor.map_y = pallet_cursor.map_y + GRID_SIZE;
        } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_RIGHT){
            pallet_cursor.map_x = pallet_cursor.map_x + GRID_SIZE;
        } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_LEFT){
            pallet_cursor.map_x = pallet_cursor.map_x - GRID_SIZE;
        }

     }

    if (frame <= 10000) {
        frame++;
    } else {
        frame = 0;
    }

    if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_k){
        pallet_cursor.map_y = pallet_cursor.map_y - GRID_SIZE;
    } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_j){
        pallet_cursor.map_y = pallet_cursor.map_y + GRID_SIZE;
    } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_l){
        pallet_cursor.map_x = pallet_cursor.map_x + GRID_SIZE;
    } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_h){
        pallet_cursor.map_x = pallet_cursor.map_x - GRID_SIZE;
    }

    return 0;

}

int draw_map(SDL_Renderer *renderer){

    int x, y;
    int start_x = cursor.offset_x / GRID_SIZE - 1;
    int end_x = start_x + SCREEN_WIDTH / GRID_SIZE + 2;
    int start_y = cursor.offset_y / GRID_SIZE - 1;
    int end_y = start_y + SCREEN_HEIGHT/ GRID_SIZE + 2;


    for(y = start_y;y < end_y;y++){
        for(x = start_x; x < end_x;x++){

            SDL_Rect imageRect=(SDL_Rect){0, 0, IMAGE_WIDTH, IMAGE_HEIGHT};
            SDL_Rect drawRect=(SDL_Rect){(x * GRID_SIZE) - cursor.offset_x,
                                         (y * GRID_SIZE) - cursor.offset_y,
                                         IMAGE_WIDTH*MAGNIFICATION, IMAGE_HEIGHT*MAGNIFICATION};

            if ((x < 0) || (x > COL - 1) || (y < 0) || (y > ROW - 1)){
                SDL_RenderCopy(renderer, mapchip[OUT_OF_MAP].map_image, &imageRect, &drawRect);
            } else {
                SDL_RenderCopy(renderer, mapchip[map_array[y*COL+x]].map_image, &imageRect, &drawRect);
            }

        }
    }

    return 0;
}

int draw_pallet(SDL_Renderer *renderer){

    int x, y;
    int start_x = pallet_cursor.offset_x / GRID_SIZE - 1;
    int end_x = start_x + SCREEN_WIDTH / GRID_SIZE + 2;
    int start_y = pallet_cursor.offset_y / GRID_SIZE - 1;
    int end_y = start_y + SCREEN_HEIGHT/ GRID_SIZE + 2;


    for(y = start_y;y < end_y;y++){
        for(x = start_x; x < end_x;x++){

            SDL_Rect imageRect=(SDL_Rect){0, 0, IMAGE_WIDTH, IMAGE_HEIGHT};
            SDL_Rect drawRect=(SDL_Rect){(x * GRID_SIZE) - pallet_cursor.offset_x,
                                         (y * GRID_SIZE) - pallet_cursor.offset_y,
                                         IMAGE_WIDTH*MAGNIFICATION, IMAGE_HEIGHT*MAGNIFICATION};

            if ((x < 0) || (x > PALLET_COL - 1) || (y < 0) || (y > PALLET_ROW - 1)){
                SDL_RenderCopy(renderer, mapchip[OUT_OF_MAP].map_image, &imageRect, &drawRect);
            } else {
                if(number_of_map_image >= y*PALLET_COL+x) {
                    SDL_RenderCopy(renderer, mapchip[pallet_array[y*PALLET_COL+x]].map_image, &imageRect, &drawRect);
                } else {
                    SDL_RenderCopy(renderer, mapchip[OUT_OF_MAP].map_image, &imageRect, &drawRect);
                }
            }

        }
    }

    return 0;
}

int load_mapchip(SDL_Renderer *renderer) {

    FILE *fp;
    int x, y, z;
    char n[256];
    char path[256];
    char buf[256];
    int i = 0;

    fp = fopen("data/mapchip.dat", "r");
    if (fp == NULL) {
        printf("file open error. %d\n", __LINE__);
        return 1;
    }

    for(i = 0;fgets(buf, sizeof(buf), fp) != NULL;i++){
        sscanf(buf, "%d,%[^,],%d,%d", &x, n, &y, &z);
        mapchip[i].mapchip_id = x;
        strcpy(mapchip[i].mapchip_name, n);
        mapchip[i].movable = y;
        mapchip[i].change_locate = z;

        sprintf(path, "image/mapchip/%s.bmp", mapchip[i].mapchip_name);
        load_image(renderer, &mapchip[i].map_image, path);

        pallet_array[i] = x;
    }

    number_of_map_image = i - 1;

    fclose(fp);

    return 0;

}

int clac_offset(int x, int y, int *offset_x, int *offset_y) {
    *offset_x = x - (SCREEN_WIDTH / 2);
    *offset_y = y - (SCREEN_HEIGHT / 2);

    return 0;
}


int draw_coordinate(SDL_Renderer *renderer, TTF_Font *font) {
    char coordinate[10];

    sprintf(coordinate, "%03d %03d", cursor.map_x / GRID_SIZE, cursor.map_y / GRID_SIZE);
    display_character_string(renderer, font, coordinate, 695, 100);

    return 0;

}

int save_file(char *file_name) {
    FILE *fp;
    char file[256] = {0};
    int i = 0;

    sprintf(FILE_NAME, "%s", file_name);
    while (FILE_NAME[i] != '\0') {
        FILE_NAME[i] = toupper((unsigned char)FILE_NAME[i]);
        i++;
    }

    sprintf(file, "data/%s.map", file_name);

    i = 0;
    while (file[i] != '\0') {
        file[i] = tolower((unsigned char)file[i]);
        i++;
    }

    if ((fp = fopen(file, "wb")) == NULL) {
        return 1;
    }

    fwrite(&COL, sizeof(int), 1, fp);
    fwrite(&ROW, sizeof(int), 1, fp);
    fwrite(&OUTER_PERIPHERY, sizeof(int), 1, fp);
    fwrite(map_array, sizeof(int) * COL * ROW, 1, fp);

    fclose(fp);

    return 0;
}

int load_file(char *file_name) {
    FILE *fp;
    char file[256] = {0};
    int i = 0;

    sprintf(file, "data/%s.map", file_name);

    while (file_name[i] != '\0') {
        file_name[i] = tolower((unsigned char)file_name[i]);
        i++;
    }

    if ((fp = fopen(file, "rb")) == NULL) {
        return 1;
    }

    sprintf(FILE_NAME, "%s", file_name);

    i = 0;
    while (FILE_NAME[i] != '\0') {
        FILE_NAME[i] = toupper((unsigned char)FILE_NAME[i]);
        i++;
    }

    fread(&COL, sizeof(int), 1, fp);
    fread(&ROW, sizeof(int), 1, fp);
    fread(&OUTER_PERIPHERY, sizeof(int), 1, fp);

    OUT_OF_MAP = 10;

    map_array = realloc(map_array, sizeof(int) * COL * ROW);

    i = 0;
    while(!feof(fp)) {
        fread(&map_array[i++], sizeof(int), 1, fp);
    }

    fclose(fp);

    return 0;
}

int make_box(SDL_Renderer *renderer, int x, int y, int w, int h, int blend, COLOR color) {

    SDL_Rect rectangle;
    SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);

    rectangle.x = x;
    rectangle.y = y;
    rectangle.w = w;
    rectangle.h = h;

    if (color == WHITE) {
        SDL_SetRenderDrawColor(renderer, 255, 255, 255, blend);
    } else if (color == BLACK) {
        SDL_SetRenderDrawColor(renderer, 0, 0, 0, blend);
    }

    SDL_RenderFillRect(renderer, &rectangle);

    return 0;
}

int display_character_string(SDL_Renderer *renderer, TTF_Font *font, char *string, int x, int y) {
    SDL_Surface *surface;
    SDL_Texture *texture;

    surface = TTF_RenderUTF8_Blended(font, string, (SDL_Color){255,255,255,255});

    texture = SDL_CreateTextureFromSurface(renderer, surface);

    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);

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

    SDL_Rect txtRect=(SDL_Rect){0,0,iw,ih};
    SDL_Rect pasteRect=(SDL_Rect){x,y,iw,ih};

    SDL_RenderCopy(renderer, texture, &txtRect, &pasteRect);

    SDL_FreeSurface(surface);

    return 0;
}

int display_save_window(SDL_Event e, SDL_Renderer *renderer, TTF_Font *font) {

    char file_name[21] = {0};
    char *status;
    char *endptr;

    make_box(renderer, 244, 296, 304, 32, 255, WHITE);
    make_box(renderer, 246, 298, 300, 28, 255, BLACK);

    display_character_string(renderer, font, "SAVE?:", 250, 300);

    // FONT_SIZE 22 ,11pt

    SDL_RenderPresent(renderer);

    sprintf(file_name, "%s", FILE_NAME);
    if (accept_character_input(e, renderer, font, file_name, 316, 525) == 0) {

        make_box(renderer, 244, 296, 304, 32, 255, WHITE);
        make_box(renderer, 246, 298, 300, 28, 255, BLACK);

        if(save_file(file_name) == 0) {
            status = "SAVED!";
        } else {
            status = "SAVING FAILD";
        }

        while(1) {
            make_box(renderer, 244, 296, 304, 32, 255, WHITE);
            make_box(renderer, 246, 298, 300, 28, 255, BLACK);
            display_character_string(renderer, font, status, 250, 300);
            SDL_RenderPresent(renderer);

            if ( SDL_PollEvent(&e) ) {
                if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_RETURN) {
                    break;
                }
            }
        }
    }

    return 0;
}

int display_load_window(SDL_Event e, SDL_Renderer *renderer, TTF_Font *font) {

    char file_name[21] = {0};
    char *status;

    make_box(renderer, 244, 296, 304, 32, 255, WHITE);
    make_box(renderer, 246, 298, 300, 28, 255, BLACK);

    display_character_string(renderer, font, "LOAD?:", 250, 300);

    // FONT_SIZE 22 ,11pt

    SDL_RenderPresent(renderer);

    if (accept_character_input(e, renderer, font, file_name, 316, 525) == 0) {

        if(load_file(file_name) == 0) {
            status = "LOADED!";
        } else {
            status = "LOADING FAILD";
        }

        while(1) {
            make_box(renderer, 244, 296, 304, 32, 255, WHITE);
            make_box(renderer, 246, 298, 300, 28, 255, BLACK);
            display_character_string(renderer, font, status, 250, 300);
            SDL_RenderPresent(renderer);

            if ( SDL_PollEvent(&e) ) {
                if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_RETURN) {
                    break;
                }
            }
        }
    }

    return 0;
}

int accept_character_input(SDL_Event e, SDL_Renderer *renderer, TTF_Font *font,
                           char *file_name, int start_pt, int end_pt) {
    char buf[21] = {0};
    int pt = start_pt;
    int file_name_element = 0;

    while(1) {

        if ( SDL_PollEvent(&e) ) {
            if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE) {
                break;
            }

            if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_RETURN) {
                break;
            }

            if (pt <= end_pt) {
                if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_a) {
                    display_character_string(renderer, font, "A", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "a");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_b) {
                    display_character_string(renderer, font, "B", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "b");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_c) {
                    display_character_string(renderer, font, "C", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "c");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_d) {
                    display_character_string(renderer, font, "D", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "d");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_e) {
                    display_character_string(renderer, font, "E", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "e");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_f) {
                    display_character_string(renderer, font, "F", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "f");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_g) {
                    display_character_string(renderer, font, "G", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "g");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_h) {
                    display_character_string(renderer, font, "H", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "h");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_i) {
                    display_character_string(renderer, font, "I", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "i");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_j) {
                    display_character_string(renderer, font, "J", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "j");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_k) {
                    display_character_string(renderer, font, "K", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "k");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_l) {
                    display_character_string(renderer, font, "L", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "l");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_m) {
                    display_character_string(renderer, font, "M", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "m");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_n) {
                    display_character_string(renderer, font, "N", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "n");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_o) {
                    display_character_string(renderer, font, "O", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "o");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_p) {
                    display_character_string(renderer, font, "P", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "p");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_q) {
                    display_character_string(renderer, font, "Q", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "q");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_r) {
                    display_character_string(renderer, font, "R", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "r");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_s) {
                    display_character_string(renderer, font, "S", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "s");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_t) {
                    display_character_string(renderer, font, "T", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "t");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_u) {
                    display_character_string(renderer, font, "U", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "u");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_v) {
                    display_character_string(renderer, font, "V", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "v");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_w) {
                    display_character_string(renderer, font, "W", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "w");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_x) {
                    display_character_string(renderer, font, "X", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "x");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_y) {
                    display_character_string(renderer, font, "Y", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "y");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_z) {
                    display_character_string(renderer, font, "Z", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "z");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_0) {
                    display_character_string(renderer, font, "0", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "0");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_1) {
                    display_character_string(renderer, font, "1", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "1");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_2) {
                    display_character_string(renderer, font, "2", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "2");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_3) {
                    display_character_string(renderer, font, "3", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "3");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_4) {
                    display_character_string(renderer, font, "4", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "4");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_5) {
                    display_character_string(renderer, font, "5", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "5");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_6) {
                    display_character_string(renderer, font, "6", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "6");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_7) {
                    display_character_string(renderer, font, "7", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "7");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_8) {
                    display_character_string(renderer, font, "8", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "8");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_9) {
                    display_character_string(renderer, font, "9", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "9");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_SLASH) {
                    display_character_string(renderer, font, "_", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "_");
                    file_name_element++;
                }
            }

            if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_BACKSPACE) {
                if (start_pt < pt) {
                    make_box(renderer, pt - 11, 301, 11, 22, 255, BLACK);
                    pt = pt - 11;

                    file_name_element--;
                    strncpy(buf, file_name, file_name_element);
                    memset(file_name, '\0', 20);
                    sprintf(file_name, "%s", buf);
                    memset(buf, '\0', 20);
                }
            }
        }

        SDL_RenderPresent(renderer);

    }

    return 0;
}

実行結果

gifだとわかりづらいが、保存・読込ができるようになった。
f:id:K38:20190325215445g:plain

終わりに

以上で、マップデータの保存・読込が実装できた。これで、マップはかなり思い通りに作成できるようになった。 後は、細かい部分の使い勝手を向上させていければと思う。

SDL2でMAPEditerを作る 〜第4回 文字入力Windowの作成〜

前回は、マップを作る上で役に立つちょっとした機能の追加を行った。今回は、保存・読込の実装をしたのでその辺りを書いていこうと思ったのだが、保存・読込自体よりファイル名を入力するための文字入力機能を作るほうがパワーを使う結果になったので、今回は文字入力Windowについて、次回で本題の保存・読込について書いていきたいと思う。

文字入力Windowとは?

まず、そもそも文字入力Windowとは一体どのようなものを作ったのかというところなのだが、以下のGIFのような入力Windowを作成した。
f:id:K38:20190314234011g:plain

大文字A〜Z、数字0〜9、アンダーバーが入力できて、バックスペースが使える。最大入力文字数は20文字。

文字入力Window作成

文字入力Windowについては、四角形を作る部分を関数化し白い四角形と黒い四角形を組み合わせてWindowにしている。以下がその関数になる。
引数として、四角形の左上頂点のX座標、Y座標、四角形の縦横、アルファ値、四角形の色(黒か白)を入力する。

int make_box(SDL_Renderer *renderer, int x, int y, int w, int h, int blend, COLOR color) {

    SDL_Rect rectangle;
    SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);

    rectangle.x = x;
    rectangle.y = y;
    rectangle.w = w;
    rectangle.h = h;

    if (color == WHITE) {
        SDL_SetRenderDrawColor(renderer, 255, 255, 255, blend);
    } else if (color == BLACK) {
        SDL_SetRenderDrawColor(renderer, 0, 0, 0, blend);
    }

    SDL_RenderFillRect(renderer, &rectangle);

    return 0;
}

文字入力方法

続いて、文字入力Windowの肝となっている文字入力の部分について書いていこうと思う。
まず、考え方として文字表示部分と文字列作成部分に分けて考えている。

文字表示部分

文字表示部分は、-> 前回選択したマップチップを表示するために作成した表示用Window同様に文字入力用のWindowを作成し、その上に入力文字を文字サイズ分ずらしながら表示していく作りになっている。そのため、文字入力ウィンドウのサイズは20文字+説明分(SAVE?などの文字)の入力に合うように作っている。フォントサイズも固定値とし22 ,11ptで固定している。
よって、1文字入力されれば表示位置を右に11ptずらし1文字削除されれれば表示位置を左に11ptずらすように作っている。また、文字の削除、バックスペースだがこれは文字を消しているわけではなく黒い四角形を文字の上に表示して見えなくする処理を行っている。
更に、入力文字の表示位置が文字入力用Windowより右、つまり、はみ出るようであれば入力できないようにしている。 この制御で最大文字数20文字を実現している。

文字列作成部分

文字列作成部分は、文字列保存用の配列を用意して入力があるたびに文字を格納する。それと、合わせて何文字目の入力か入力文字数も合わせて取得する。なぜ、入力文字数を取得するのかというと、バックスペースが押された時に入力文字数 - 1文字の文字列を作成する必要があるため取得している。バックスペースが押されたタイミングで全体文字数 - 1を取得する方法も取れるのかもしれないが、都度入力文字数を把握している方が文字列作成が容易だったので今回のような作りにした。

以下に、文字列部分を担っている関数を載せる。上記の考えをそのままコードに起こしたつもりだ。

int accept_character_input(SDL_Event e, SDL_Renderer *renderer, TTF_Font *font,
                           char *file_name, int start_pt, int end_pt) {
    char buf[21] = {0};
    int pt = start_pt;
    int file_name_element = 0;

    while(1) {

        if ( SDL_PollEvent(&e) ) {
            if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE) {
                break;
            }

            if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_RETURN) {
                break;
            }

            if (pt <= end_pt) {
                if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_a) {
                    display_character_string(renderer, font, "A", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "a");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_b) {
                    display_character_string(renderer, font, "B", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "b");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_c) {
                    display_character_string(renderer, font, "C", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "c");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_d) {
                    display_character_string(renderer, font, "D", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "d");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_e) {
                    display_character_string(renderer, font, "E", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "e");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_f) {
                    display_character_string(renderer, font, "F", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "f");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_g) {
                    display_character_string(renderer, font, "G", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "g");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_h) {
                    display_character_string(renderer, font, "H", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "h");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_i) {
                    display_character_string(renderer, font, "I", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "i");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_j) {
                    display_character_string(renderer, font, "J", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "j");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_k) {
                    display_character_string(renderer, font, "K", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "k");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_l) {
                    display_character_string(renderer, font, "L", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "l");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_m) {
                    display_character_string(renderer, font, "M", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "m");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_n) {
                    display_character_string(renderer, font, "N", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "n");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_o) {
                    display_character_string(renderer, font, "O", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "o");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_p) {
                    display_character_string(renderer, font, "P", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "p");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_q) {
                    display_character_string(renderer, font, "Q", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "q");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_r) {
                    display_character_string(renderer, font, "R", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "r");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_s) {
                    display_character_string(renderer, font, "S", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "s");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_t) {
                    display_character_string(renderer, font, "T", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "t");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_u) {
                    display_character_string(renderer, font, "U", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "u");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_v) {
                    display_character_string(renderer, font, "V", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "v");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_w) {
                    display_character_string(renderer, font, "W", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "w");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_x) {
                    display_character_string(renderer, font, "X", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "x");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_y) {
                    display_character_string(renderer, font, "Y", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "y");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_z) {
                    display_character_string(renderer, font, "Z", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "z");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_0) {
                    display_character_string(renderer, font, "0", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "0");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_1) {
                    display_character_string(renderer, font, "1", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "1");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_2) {
                    display_character_string(renderer, font, "2", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "2");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_3) {
                    display_character_string(renderer, font, "3", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "3");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_4) {
                    display_character_string(renderer, font, "4", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "4");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_5) {
                    display_character_string(renderer, font, "5", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "5");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_6) {
                    display_character_string(renderer, font, "6", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "6");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_7) {
                    display_character_string(renderer, font, "7", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "7");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_8) {
                    display_character_string(renderer, font, "8", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "8");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_9) {
                    display_character_string(renderer, font, "9", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "9");
                    file_name_element++;
                } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_SLASH) {
                    display_character_string(renderer, font, "_", pt, 300);
                    pt = pt + 11;
                    strcat(file_name, "_");
                    file_name_element++;
                }
            }

            if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_BACKSPACE) {
                if (start_pt < pt) {
                    make_box(renderer, pt - 11, 301, 11, 22, 255, BLACK);
                    pt = pt - 11;

                    file_name_element--;
                    strncpy(buf, file_name, file_name_element);
                    memset(file_name, '\0', 20);
                    sprintf(file_name, "%s", buf);
                    memset(buf, '\0', 20);
                }
            }
        }

        SDL_RenderPresent(renderer);

    }

    return 0;
}

終わりに

以上が文字入力Windowについての説明となる。文字が入力できることで、ファイル名を指定することが可能になった。次回は、この機能の延長線上にあるファイルの保存・読込について書いていく。全コードは、次回保存・読込の説明を書いたら載せようと思うので次回もお付き合いいただきたい。

SDL2でMAPEditerを作る 〜第3回 機能の追加(マップチップの表示・カーソル座標の表示・スポイト機能)〜

前回までで、マップチップを選択してキャンパスに配置する所まで作成した。今回は現在選択されているマップチップの表示、カーソル座標の表示、キャンパスに配置したマップチップを選択するためのスポイト機能などを実装していこうと思う。

マップチップの表示

前回までの状態だと、今どのマップチップが選択されているのかがわからない状態だった。このままでも、マップ作成はできるのだが、どのマップチップを選択しているのかわかったほうがストレスが少ない。ということで、右上に選択されているマップチップを表示することにした。

以下のコードで右上に選択されているマップチップを表示している。白い四角形と黒い四角形を重ねてウィンドウを作りその中に選択中のマップチップを表示するようにした。

int draw_selected_mapchip(SDL_Renderer *renderer) {
    SDL_Rect rectangle;
    SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);

    rectangle.x = 700;
    rectangle.y = 30;
    rectangle.w = 62;
    rectangle.h = 62;

    SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
    SDL_RenderFillRect(renderer, &rectangle);

    rectangle.x = 702;
    rectangle.y = 32;
    rectangle.w = 58;
    rectangle.h = 58;

    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
    SDL_RenderFillRect(renderer, &rectangle);

    SDL_Rect imageRect=(SDL_Rect){0, 0, IMAGE_WIDTH, IMAGE_HEIGHT};
    SDL_Rect drawRect=(SDL_Rect){715,
                                 45,
                                 IMAGE_WIDTH*MAGNIFICATION, IMAGE_HEIGHT*MAGNIFICATION};

    SDL_RenderCopy(renderer, mapchip[select_mapchip].map_image, &imageRect, &drawRect);


    return 0;
}

画像だとこんな感じになる。
f:id:K38:20190308011822p:plain

カーソル座標の表示

続いて、追加してのはカーソル座標の表示である。これは、-> SDL2で文字を表示するで試して以来おそらく初めてSDL2を使用して文字の表示を行ったと思う。コードは以下のようになった。

int draw_coordinate(SDL_Renderer *renderer, TTF_Font *font) {
    SDL_Surface *surface;
    SDL_Texture *texture;

    char path[256];

    sprintf(path, "%03d %03d", cursor.map_x / GRID_SIZE, cursor.map_y / GRID_SIZE);
    surface = TTF_RenderUTF8_Blended(font, path, (SDL_Color){255,255,255,255});

    //surfaceからTextureを作る
    texture =SDL_CreateTextureFromSurface(renderer, surface);

    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);

    //文字を描写したTextureのサイズを取得する
    int iw,ih;
    SDL_QueryTexture(texture, NULL, NULL, &iw, &ih);

    SDL_Rect txtRect=(SDL_Rect){0,0,iw,ih};
    SDL_Rect pasteRect=(SDL_Rect){695,100,iw,ih};

    SDL_RenderCopy(renderer, texture, &txtRect, &pasteRect);

    SDL_FreeSurface(surface);

    return 0;

}

文字を表示した感想としては狙ったところに表示させるのがなかなか難しいかな、といったところ。ただ、SDL2を使えば容易に文字を表示できるのは確かだ。まあ、何はともあれ力技ではあるが狙ったところに表示できたので良しとしたい。(マップチップウィンドウの下に表示してみた)
f:id:K38:20190308011905p:plain

スポイト機能の追加

マップを作っていくうえで、マップチップパレットに切り替えるまでもない修正、例えば隣のマップチップと同じものを 使いたい、といった場面は多々発生すると思われる。その時のために、マップ上のマップチップを設定できるスポイト機能を作成した。コードは以下になる。

int get_mapchip_with_key_a(SDL_Renderer *renderer) {

    select_mapchip =
       map_array[(cursor.map_y / GRID_SIZE) * COL +
                    (cursor.map_x / GRID_SIZE)];

    return 0;

}

まぁ、コードとしては大したことなくキャンパス上で「a」キーを押すと、それを選択されているマップチップにしますよ といった、処理になる。ただ、コードは大したことはないがマップを書く上での効率化はものすごいものがあると思う。

全コード記載

では、今回の全コードを記載する。

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

#define FONT_PATH "font/PixelMplus12-Bold.ttf"

const int SCREEN_WIDTH = 800;
const int SCREEN_HEIGHT = 600;
const int IMAGE_WIDTH = 16;
const int IMAGE_HEIGHT = 16;
const int MAGNIFICATION = 2;
const int GRID_SIZE = 32;
const int PALLET_ROW = 15;
const int PALLET_COL = 20;

int ROW = 15;
int COL = 20;
int OUT_OF_MAP = 10;
int frame = 0;

int draw_coordinate(SDL_Renderer *, TTF_Font *);
int clac_offset(int, int, int *, int *);
int load_mapchip(SDL_Renderer *);
int draw_map(SDL_Renderer *);
int draw_pallet(SDL_Renderer *);
int cursor_move(SDL_Event, SDL_Renderer *);
int load_image(SDL_Renderer *, SDL_Texture **, char *);
int place_mapchip(SDL_Point, SDL_Renderer *);
int place_mapchip_with_key(SDL_Renderer *);
int get_mapchip(SDL_Point, SDL_Renderer *);
int get_mapchip_right_click(SDL_Point, SDL_Renderer *);
int get_mapchip_with_key(SDL_Renderer *);
int get_mapchip_with_key_a(SDL_Renderer *);
int initialize(SDL_Renderer *);

typedef struct {
    int map_x;
    int map_y;
    int offset_x;
    int offset_y;
} CURSOR;

typedef struct {
    int mapchip_id;
    char mapchip_name[256];
    int movable;
    int change_locate;
    SDL_Texture *map_image;
} MAPCHIP;

CURSOR cursor = {1, 1, 0, 0};
CURSOR pallet_cursor = {1, 1, 0, 0};
MAPCHIP mapchip[256] = {0};

int *map_array;
int *pallet_array;
int number_of_map_image = 0;
int select_mapchip = 0;


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

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

    SDL_Window *window = NULL;
    SDL_Renderer *renderer = NULL;
    TTF_Font *font = 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( "MAP CREATER", 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);
    }

    //Initialize TTF
    if ( TTF_Init() < 0 ) {
        printf("TTFcould not initialize! TTF_Error: %s\n", TTF_GetError());
    }

    font = TTF_OpenFont(FONT_PATH, 22);
    if ( font == NULL ) {
        printf("TTF_OpenFont: %s\n", TTF_GetError());
    }

    initialize(renderer);

    int pallet_display = 1;

    // main loop
    while (1) {

        SDL_Event e;

        clac_offset(cursor.map_x, cursor.map_y, &cursor.offset_x, &cursor.offset_y);
        clac_offset(pallet_cursor.map_x, pallet_cursor.map_y, &pallet_cursor.offset_x, &pallet_cursor.offset_y);

        SDL_RenderClear(renderer);
        if (pallet_display == 0) {
            draw_pallet(renderer);
            pallet_move(e, renderer);
        } else {
            draw_map(renderer);
            cursor_move(e, renderer);
        }

        draw_selected_mapchip(renderer);

        draw_coordinate(renderer, font);

        SDL_RenderPresent(renderer);

        // event handling
        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_i) {
                if (pallet_display == 0) {
                    pallet_display = 1;
                } else {
                    pallet_display = 0;
                }
            } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_a && pallet_display == 1) {
                get_mapchip_with_key_a(renderer);
            } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_SPACE && pallet_display == 0) {
                get_mapchip_with_key(renderer);
            } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_SPACE && pallet_display == 1) {
                place_mapchip_with_key(renderer);
            } else if (e.type == SDL_MOUSEBUTTONDOWN && e.button.button == SDL_BUTTON_LEFT && pallet_display == 0) {
                get_mapchip(mouse_position, renderer);
            } else if (e.type == SDL_MOUSEBUTTONDOWN && e.button.button == SDL_BUTTON_LEFT && pallet_display == 1) {
                place_mapchip(mouse_position, renderer);
            } else if (e.type == SDL_MOUSEBUTTONDOWN && e.button.button == SDL_BUTTON_RIGHT && pallet_display == 1) {
                get_mapchip_right_click(mouse_position, renderer);
            }
        }

    }

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

    int i;
    for (i = 0;i < number_of_map_image;i++) {
        SDL_DestroyTexture(mapchip[i].map_image);
    }

    SDL_Quit();

    return 0;

}

int initialize(SDL_Renderer *renderer) {


    map_array = malloc(sizeof(int) * COL * ROW);
    pallet_array = malloc(sizeof(int) * PALLET_COL * PALLET_ROW);

    load_mapchip(renderer);

    int i;
    for (i=0;i <= COL*ROW;i++) {
        map_array[i] = 1;
    }

    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 place_mapchip(SDL_Point mouse_position, SDL_Renderer *renderer) {

    SDL_GetMouseState(&mouse_position.x, &mouse_position.y);
    map_array[((cursor.offset_y + mouse_position.y) / GRID_SIZE)*COL +
              ((cursor.offset_x + mouse_position.x) / GRID_SIZE)] = select_mapchip;

    return 0;

}

int place_mapchip_with_key(SDL_Renderer *renderer) {

    map_array[(cursor.map_y / GRID_SIZE) * COL +
              (cursor.map_x / GRID_SIZE)] = select_mapchip;

    return 0;

}

int get_mapchip(SDL_Point mouse_position, SDL_Renderer *renderer) {

    SDL_GetMouseState(&mouse_position.x, &mouse_position.y);
    select_mapchip =
       pallet_array[((pallet_cursor.offset_y + mouse_position.y) / GRID_SIZE) * PALLET_COL +
                    ((pallet_cursor.offset_x + mouse_position.x) / GRID_SIZE)];

    return 0;

}

int get_mapchip_right_click(SDL_Point mouse_position, SDL_Renderer *renderer) {

    SDL_GetMouseState(&mouse_position.x, &mouse_position.y);
    select_mapchip =
       map_array[((cursor.offset_y + mouse_position.y) / GRID_SIZE) * COL +
                    ((cursor.offset_x + mouse_position.x) / GRID_SIZE)];

    return 0;

}

int get_mapchip_with_key(SDL_Renderer *renderer) {

    select_mapchip =
       pallet_array[(pallet_cursor.map_y / GRID_SIZE) * PALLET_COL +
                    (pallet_cursor.map_x / GRID_SIZE)];

    return 0;

}

int get_mapchip_with_key_a(SDL_Renderer *renderer) {

    select_mapchip =
       map_array[(cursor.map_y / GRID_SIZE) * COL +
                    (cursor.map_x / GRID_SIZE)];

    return 0;

}

int draw_selected_mapchip(SDL_Renderer *renderer) {
    SDL_Rect rectangle;
    SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);

    rectangle.x = 700;
    rectangle.y = 30;
    rectangle.w = 62;
    rectangle.h = 62;

    SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
    SDL_RenderFillRect(renderer, &rectangle);

    rectangle.x = 702;
    rectangle.y = 32;
    rectangle.w = 58;
    rectangle.h = 58;

    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
    SDL_RenderFillRect(renderer, &rectangle);

    SDL_Rect imageRect=(SDL_Rect){0, 0, IMAGE_WIDTH, IMAGE_HEIGHT};
    SDL_Rect drawRect=(SDL_Rect){715,
                                 45,
                                 IMAGE_WIDTH*MAGNIFICATION, IMAGE_HEIGHT*MAGNIFICATION};

    SDL_RenderCopy(renderer, mapchip[select_mapchip].map_image, &imageRect, &drawRect);


    return 0;
}

int cursor_move(SDL_Event e, SDL_Renderer *renderer) {

    SDL_Rect rectangle;
    SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);

    rectangle.x = cursor.map_x - cursor.offset_x;
    rectangle.y = cursor.map_y - cursor.offset_y;
    rectangle.w = GRID_SIZE;
    rectangle.h = GRID_SIZE;

    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 100);
    SDL_RenderFillRect(renderer, &rectangle);

    if (frame % 12 == 0) {

        if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_UP){
            cursor.map_y = cursor.map_y - GRID_SIZE;
        } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_DOWN){
            cursor.map_y = cursor.map_y + GRID_SIZE;
        } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_RIGHT){
            cursor.map_x = cursor.map_x + GRID_SIZE;
        } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_LEFT){
            cursor.map_x = cursor.map_x - GRID_SIZE;
        }

     }

    if (frame <= 12000) {
        frame++;
    } else {
        frame = 0;
    }

    if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_k){
        cursor.map_y = cursor.map_y - GRID_SIZE;
    } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_j){
        cursor.map_y = cursor.map_y + GRID_SIZE;
    } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_l){
        cursor.map_x = cursor.map_x + GRID_SIZE;
    } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_h){
        cursor.map_x = cursor.map_x - GRID_SIZE;
    }

    return 0;

}

int pallet_move(SDL_Event e, SDL_Renderer *renderer) {

    SDL_Rect rectangle;
    SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);

    rectangle.x = pallet_cursor.map_x - pallet_cursor.offset_x;
    rectangle.y = pallet_cursor.map_y - pallet_cursor.offset_y;
    rectangle.w = GRID_SIZE;
    rectangle.h = GRID_SIZE;

    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 100);
    SDL_RenderFillRect(renderer, &rectangle);

    if (frame % 12 == 0) {

        if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_UP){
            pallet_cursor.map_y = pallet_cursor.map_y - GRID_SIZE;
        } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_DOWN){
            pallet_cursor.map_y = pallet_cursor.map_y + GRID_SIZE;
        } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_RIGHT){
            pallet_cursor.map_x = pallet_cursor.map_x + GRID_SIZE;
        } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_LEFT){
            pallet_cursor.map_x = pallet_cursor.map_x - GRID_SIZE;
        }

     }

    if (frame <= 12000) {
        frame++;
    } else {
        frame = 0;
    }

    if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_k){
        pallet_cursor.map_y = pallet_cursor.map_y - GRID_SIZE;
    } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_j){
        pallet_cursor.map_y = pallet_cursor.map_y + GRID_SIZE;
    } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_l){
        pallet_cursor.map_x = pallet_cursor.map_x + GRID_SIZE;
    } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_h){
        pallet_cursor.map_x = pallet_cursor.map_x - GRID_SIZE;
    }

    return 0;

}

int draw_map(SDL_Renderer *renderer){

    int x, y;
    int start_x = cursor.offset_x / GRID_SIZE - 1;
    int end_x = start_x + SCREEN_WIDTH / GRID_SIZE + 2;
    int start_y = cursor.offset_y / GRID_SIZE - 1;
    int end_y = start_y + SCREEN_HEIGHT/ GRID_SIZE + 2;


    for(y = start_y;y < end_y;y++){
        for(x = start_x; x < end_x;x++){

            SDL_Rect imageRect=(SDL_Rect){0, 0, IMAGE_WIDTH, IMAGE_HEIGHT};
            SDL_Rect drawRect=(SDL_Rect){(x * GRID_SIZE) - cursor.offset_x,
                                         (y * GRID_SIZE) - cursor.offset_y,
                                         IMAGE_WIDTH*MAGNIFICATION, IMAGE_HEIGHT*MAGNIFICATION};

            if ((x < 0) || (x > COL - 1) || (y < 0) || (y > ROW - 1)){
                SDL_RenderCopy(renderer, mapchip[OUT_OF_MAP].map_image, &imageRect, &drawRect);
            } else {
                SDL_RenderCopy(renderer, mapchip[map_array[y*COL+x]].map_image, &imageRect, &drawRect);
            }

        }
    }

    return 0;
}

int draw_pallet(SDL_Renderer *renderer){

    int x, y;
    int start_x = pallet_cursor.offset_x / GRID_SIZE - 1;
    int end_x = start_x + SCREEN_WIDTH / GRID_SIZE + 2;
    int start_y = pallet_cursor.offset_y / GRID_SIZE - 1;
    int end_y = start_y + SCREEN_HEIGHT/ GRID_SIZE + 2;


    for(y = start_y;y < end_y;y++){
        for(x = start_x; x < end_x;x++){

            SDL_Rect imageRect=(SDL_Rect){0, 0, IMAGE_WIDTH, IMAGE_HEIGHT};
            SDL_Rect drawRect=(SDL_Rect){(x * GRID_SIZE) - pallet_cursor.offset_x,
                                         (y * GRID_SIZE) - pallet_cursor.offset_y,
                                         IMAGE_WIDTH*MAGNIFICATION, IMAGE_HEIGHT*MAGNIFICATION};

            if ((x < 0) || (x > PALLET_COL - 1) || (y < 0) || (y > PALLET_ROW - 1)){
                SDL_RenderCopy(renderer, mapchip[OUT_OF_MAP].map_image, &imageRect, &drawRect);
            } else {
                if(number_of_map_image >= y*PALLET_COL+x) {
                    SDL_RenderCopy(renderer, mapchip[pallet_array[y*PALLET_COL+x]].map_image, &imageRect, &drawRect);
                } else {
                    SDL_RenderCopy(renderer, mapchip[OUT_OF_MAP].map_image, &imageRect, &drawRect);
                }
            }

        }
    }

    return 0;
}

int load_mapchip(SDL_Renderer *renderer) {

    FILE *fp;
    int x, y, z;
    char n[256];
    char path[256];
    char buf[256];
    int i = 0;

    fp = fopen("data/mapchip.dat", "r");
    if (fp == NULL) {
        printf("file open error. %d\n", __LINE__);
        return 1;
    }

    for(i = 0;fgets(buf, sizeof(buf), fp) != NULL;i++){
        sscanf(buf, "%d,%[^,],%d,%d", &x, n, &y, &z);
        mapchip[i].mapchip_id = x;
        strcpy(mapchip[i].mapchip_name, n);
        mapchip[i].movable = y;
        mapchip[i].change_locate = z;

        sprintf(path, "image/mapchip/%s.bmp", mapchip[i].mapchip_name);
        load_image(renderer, &mapchip[i].map_image, path);

        pallet_array[i] = x;
    }

    number_of_map_image = i - 1;

    fclose(fp);

    return 0;

}

int clac_offset(int x, int y, int *offset_x, int *offset_y) {
    *offset_x = x - (SCREEN_WIDTH / 2);
    *offset_y = y - (SCREEN_HEIGHT / 2);

    return 0;
}


int draw_coordinate(SDL_Renderer *renderer, TTF_Font *font) {
    SDL_Surface *surface;
    SDL_Texture *texture;

    char path[256];

    sprintf(path, "%03d %03d", cursor.map_x / GRID_SIZE, cursor.map_y / GRID_SIZE);
    surface = TTF_RenderUTF8_Blended(font, path, (SDL_Color){255,255,255,255});

    //surfaceからTextureを作る
    texture =SDL_CreateTextureFromSurface(renderer, surface);

    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);

    //文字を描写したTextureのサイズを取得する
    int iw,ih;
    SDL_QueryTexture(texture, NULL, NULL, &iw, &ih);

    SDL_Rect txtRect=(SDL_Rect){0,0,iw,ih};
    SDL_Rect pasteRect=(SDL_Rect){695,100,iw,ih};

    SDL_RenderCopy(renderer, texture, &txtRect, &pasteRect);

    SDL_FreeSurface(surface);

    return 0;

}

実行結果

今回実装分の見た目はこんな感じ。
f:id:K38:20190308012043g:plain

終わりに

今回は、マップを書く上での利便性を良くするための機能を追加した。次回辺り、保存・読込といったあたりの実装ができればと思う。

SDL2でMAPEditerを作る 〜第2回 マップチップを配置する〜

前回は、マップを描くためのキャンパスを表示する所まで作った。今回は、そのキャンパスにマップチップを配置 できるようにしていこうと思う。

パレット画面の作成

キャンパスにマップチップを配置するためには、配置するマップチップを選ぶ機能が必要になる。どのように実装するか考えた結果、パレット画面を作成し、そこにあるマップチップを選択してキャンパス画面で配置する、みたいな画面を作ることにした。
キャンパス画面とパレット画面は「i」キーで切り替わるようにし、パレット画面に表示されているマップチップ上で右クリックかスペースを押すことでマップチップを選択する。キャンパス画面に切り替えてこちらで右クリックもしくはスペースを押すとマップチップを配置できる。

コードとしては、キャンパス表示用の構造体とパレット表示表の構造体を用意して別々に処理が行われる。どちらを表示するか切り替えているのが「i」キーとなる。

また、配置するマップチップは ->第6回 マップの読込 で用意したmapchip.datを読み込んで使用するようにする。 こうしておけば、自由にマップチップが増やせるはずだ。

クリックした座標の取得

今回新しく登場した部分が、クリックした座標の取得である。単純に画面上の座標を拾うのは過去に ->SDL2でマウス入力を検知するで実装している。 今回新しいのは画面上の座標ではなくマップ上の座標を取得しなければならないという部分になる。
画面の座標をマップ上の座標に変換する計算は簡単で、
(オフセット座標 + マウスで取得した座標) / グリッドサイズ
で求めることができる。
この計算で求めた座標を元にマップチップを取得するための関数が以下になる。

int get_mapchip(SDL_Point mouse_position, SDL_Renderer *renderer) {

    SDL_GetMouseState(&mouse_position.x, &mouse_position.y);
    select_mapchip =
       pallet_array[((pallet_cursor.offset_y + mouse_position.y) / GRID_SIZE)*PALLET_COL +
                    ((pallet_cursor.offset_x + mouse_position.x) / GRID_SIZE)];

    return 0;

}

マップチップを配置するときもこの計算式で求めた座標に取得したマップチップを置けばOKだ。

全コードの記載

それでは、今回実装したコードを記載する。

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

const int SCREEN_WIDTH = 800;
const int SCREEN_HEIGHT = 600;
const int IMAGE_WIDTH = 16;
const int IMAGE_HEIGHT = 16;
const int MAGNIFICATION = 2;
const int GRID_SIZE = 32;
const int PALLET_ROW = 15;
const int PALLET_COL = 20;

int ROW = 15;
int COL = 20;
int OUT_OF_MAP = 10;
int frame = 0;

int clac_offset(int, int, int *, int *);
int load_mapchip(SDL_Renderer *);
int draw_map(SDL_Renderer *);
int draw_pallet(SDL_Renderer *);
int cursor_move(SDL_Event, SDL_Renderer *);
int load_image(SDL_Renderer *, SDL_Texture **, char *);
int place_mapchip(SDL_Point, SDL_Renderer *);
int place_mapchip_with_key(SDL_Renderer *);
int get_mapchip(SDL_Point, SDL_Renderer *);
int get_mapchip_with_key(SDL_Renderer *);
int initialize(SDL_Renderer *);

typedef struct {
    int map_x;
    int map_y;
    int offset_x;
    int offset_y;
} CURSOR;

typedef struct {
    int mapchip_id;
    char mapchip_name[256];
    int movable;
    int change_locate;
    SDL_Texture *map_image;
} MAPCHIP;

CURSOR cursor = {1, 1, 0, 0};
CURSOR pallet_cursor = {1, 1, 0, 0};
MAPCHIP mapchip[256] = {0};

int *map_array;
int *pallet_array;
int number_of_map_image = 0;
int select_mapchip = 0;


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

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

    SDL_Window *window = NULL;
    SDL_Renderer *renderer = 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( "MAP CREATER", 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);
    }

    initialize(renderer);

    int pallet_display = 1;

    // main loop
    while (1) {

        SDL_Event e;

        clac_offset(cursor.map_x, cursor.map_y, &cursor.offset_x, &cursor.offset_y);
        clac_offset(pallet_cursor.map_x, pallet_cursor.map_y, &pallet_cursor.offset_x, &pallet_cursor.offset_y);

        SDL_RenderClear(renderer);
        if (pallet_display == 0) {
            draw_pallet(renderer);
            pallet_move(e, renderer);
        } else {
            draw_map(renderer);
            cursor_move(e, renderer);
        }
        SDL_RenderPresent(renderer);

        // event handling
        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_i) {
                if (pallet_display == 0) {
                    pallet_display = 1;
                } else {
                    pallet_display = 0;
                }
            } else if (e.type == SDL_MOUSEBUTTONDOWN && e.button.button == SDL_BUTTON_LEFT &&
                         pallet_display == 0) {
                get_mapchip(mouse_position, renderer);
            } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_SPACE && pallet_display == 0) {
                get_mapchip_with_key(renderer);
            } else if (e.type == SDL_MOUSEBUTTONDOWN && e.button.button == SDL_BUTTON_LEFT &&
                         pallet_display == 1) {
                place_mapchip(mouse_position, renderer);
            } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_SPACE && pallet_display == 1) {
                place_mapchip_with_key(renderer);
            } else if (e.type == SDL_MOUSEBUTTONDOWN && e.button.button == SDL_BUTTON_RIGHT){
            }
        }

    }

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

    int i;
    for (i = 0;i < number_of_map_image;i++) {
        SDL_DestroyTexture(mapchip[i].map_image);
    }

    SDL_Quit();

    return 0;

}

int initialize(SDL_Renderer *renderer) {


    map_array = malloc(sizeof(int) * COL * ROW);
    pallet_array = malloc(sizeof(int) * PALLET_COL * PALLET_ROW);

    load_mapchip(renderer);

    int i;
    for (i=0;i <= COL*ROW;i++) {
        map_array[i] = 1;
    }

    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 place_mapchip(SDL_Point mouse_position, SDL_Renderer *renderer) {

    SDL_GetMouseState(&mouse_position.x, &mouse_position.y);
    map_array[((cursor.offset_y + mouse_position.y) / GRID_SIZE)*COL +
              ((cursor.offset_x + mouse_position.x) / GRID_SIZE)] = select_mapchip;

    return 0;

}

int place_mapchip_with_key(SDL_Renderer *renderer) {

    map_array[(cursor.map_y / GRID_SIZE) * COL +
              (cursor.map_x / GRID_SIZE)] = select_mapchip;

    return 0;

}

int get_mapchip(SDL_Point mouse_position, SDL_Renderer *renderer) {

    SDL_GetMouseState(&mouse_position.x, &mouse_position.y);
    select_mapchip =
       pallet_array[((pallet_cursor.offset_y + mouse_position.y) / GRID_SIZE)*PALLET_COL +
                    ((pallet_cursor.offset_x + mouse_position.x) / GRID_SIZE)];

    return 0;

}

int get_mapchip_with_key(SDL_Renderer *renderer) {

    select_mapchip =
       pallet_array[(pallet_cursor.map_y / GRID_SIZE) * PALLET_COL +
                    (pallet_cursor.map_x / GRID_SIZE)];

    return 0;

}

int cursor_move(SDL_Event e, SDL_Renderer *renderer) {

    SDL_Rect rectangle;
    SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);

    rectangle.x = cursor.map_x - cursor.offset_x;
    rectangle.y = cursor.map_y - cursor.offset_y;
    rectangle.w = GRID_SIZE;
    rectangle.h = GRID_SIZE;

    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 100);
    SDL_RenderFillRect(renderer, &rectangle);

    if (frame % 12 == 0) {

        if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_UP){
            cursor.map_y = cursor.map_y - GRID_SIZE;
        } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_DOWN){
            cursor.map_y = cursor.map_y + GRID_SIZE;
        } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_RIGHT){
            cursor.map_x = cursor.map_x + GRID_SIZE;
        } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_LEFT){
            cursor.map_x = cursor.map_x - GRID_SIZE;
        }

     }

    if (frame <= 12000) {
        frame++;
    } else {
        frame = 0;
    }

    if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_k){
        cursor.map_y = cursor.map_y - GRID_SIZE;
    } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_j){
        cursor.map_y = cursor.map_y + GRID_SIZE;
    } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_l){
        cursor.map_x = cursor.map_x + GRID_SIZE;
    } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_h){
        cursor.map_x = cursor.map_x - GRID_SIZE;
    }

    return 0;

}

int pallet_move(SDL_Event e, SDL_Renderer *renderer) {

    SDL_Rect rectangle;
    SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);

    rectangle.x = pallet_cursor.map_x - pallet_cursor.offset_x;
    rectangle.y = pallet_cursor.map_y - pallet_cursor.offset_y;
    rectangle.w = GRID_SIZE;
    rectangle.h = GRID_SIZE;

    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 100);
    SDL_RenderFillRect(renderer, &rectangle);

    if (frame % 12 == 0) {

        if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_UP){
            pallet_cursor.map_y = pallet_cursor.map_y - GRID_SIZE;
        } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_DOWN){
            pallet_cursor.map_y = pallet_cursor.map_y + GRID_SIZE;
        } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_RIGHT){
            pallet_cursor.map_x = pallet_cursor.map_x + GRID_SIZE;
        } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_LEFT){
            pallet_cursor.map_x = pallet_cursor.map_x - GRID_SIZE;
        }

     }

    if (frame <= 12000) {
        frame++;
    } else {
        frame = 0;
    }

    if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_k){
        pallet_cursor.map_y = pallet_cursor.map_y - GRID_SIZE;
    } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_j){
        pallet_cursor.map_y = pallet_cursor.map_y + GRID_SIZE;
    } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_l){
        pallet_cursor.map_x = pallet_cursor.map_x + GRID_SIZE;
    } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_h){
        pallet_cursor.map_x = pallet_cursor.map_x - GRID_SIZE;
    }

    return 0;

}

int draw_map(SDL_Renderer *renderer){

    int x, y;
    int start_x = cursor.offset_x / GRID_SIZE - 1;
    int end_x = start_x + SCREEN_WIDTH / GRID_SIZE + 2;
    int start_y = cursor.offset_y / GRID_SIZE - 1;
    int end_y = start_y + SCREEN_HEIGHT/ GRID_SIZE + 2;


    for(y = start_y;y < end_y;y++){
        for(x = start_x; x < end_x;x++){

            SDL_Rect imageRect=(SDL_Rect){0, 0, IMAGE_WIDTH, IMAGE_HEIGHT};
            SDL_Rect drawRect=(SDL_Rect){(x * GRID_SIZE) - cursor.offset_x,
                                         (y * GRID_SIZE) - cursor.offset_y,
                                         IMAGE_WIDTH*MAGNIFICATION, IMAGE_HEIGHT*MAGNIFICATION};

            if ((x < 0) || (x > COL - 1) || (y < 0) || (y > ROW - 1)){
                SDL_RenderCopy(renderer, mapchip[OUT_OF_MAP].map_image, &imageRect, &drawRect);
            } else {
                SDL_RenderCopy(renderer, mapchip[map_array[y*COL+x]].map_image, &imageRect, &drawRect);
            }

        }
    }

    return 0;
}

int draw_pallet(SDL_Renderer *renderer){

    int x, y;
    int start_x = pallet_cursor.offset_x / GRID_SIZE - 1;
    int end_x = start_x + SCREEN_WIDTH / GRID_SIZE + 2;
    int start_y = pallet_cursor.offset_y / GRID_SIZE - 1;
    int end_y = start_y + SCREEN_HEIGHT/ GRID_SIZE + 2;


    for(y = start_y;y < end_y;y++){
        for(x = start_x; x < end_x;x++){

            SDL_Rect imageRect=(SDL_Rect){0, 0, IMAGE_WIDTH, IMAGE_HEIGHT};
            SDL_Rect drawRect=(SDL_Rect){(x * GRID_SIZE) - pallet_cursor.offset_x,
                                         (y * GRID_SIZE) - pallet_cursor.offset_y,
                                         IMAGE_WIDTH*MAGNIFICATION, IMAGE_HEIGHT*MAGNIFICATION};

            if ((x < 0) || (x > PALLET_COL - 1) || (y < 0) || (y > PALLET_ROW - 1)){
                SDL_RenderCopy(renderer, mapchip[OUT_OF_MAP].map_image, &imageRect, &drawRect);
            } else {
                if(number_of_map_image >= y*PALLET_COL+x) {
                    SDL_RenderCopy(renderer, mapchip[pallet_array[y*PALLET_COL+x]].map_image, &imageRect, &drawRect);
                } else {
                    SDL_RenderCopy(renderer, mapchip[OUT_OF_MAP].map_image, &imageRect, &drawRect);
                }
            }

        }
    }

    return 0;
}

int load_mapchip(SDL_Renderer *renderer) {

    FILE *fp;
    int x, y, z;
    char n[256];
    char path[256];
    char buf[256];
    int i = 0;

    fp = fopen("data/mapchip.dat", "r");
    if (fp == NULL) {
        printf("file open error. %d\n", __LINE__);
        return 1;
    }

    for(i = 0;fgets(buf, sizeof(buf), fp) != NULL;i++){
        sscanf(buf, "%d,%[^,],%d,%d", &x, n, &y, &z);
        mapchip[i].mapchip_id = x;
        strcpy(mapchip[i].mapchip_name, n);
        mapchip[i].movable = y;
        mapchip[i].change_locate = z;

        sprintf(path, "image/mapchip/%s.bmp", mapchip[i].mapchip_name);
        load_image(renderer, &mapchip[i].map_image, path);

        pallet_array[i] = x;
    }

    number_of_map_image = i - 1;

    fclose(fp);

    return 0;

}

int clac_offset(int x, int y, int *offset_x, int *offset_y) {
    *offset_x = x - (SCREEN_WIDTH / 2);
    *offset_y = y - (SCREEN_HEIGHT / 2);

    return 0;
}

実行結果

実行結果はこんな感じ。パレットに切り替えてマップチップを取得しキャンパスに配置できるようになった。
f:id:K38:20190302005703g:plain

終わりに

今回でマップを描くことが可能になった。ただ、まだ描いたマップを保存したり読み込んだりができないので、その辺りを次回は実装していこと思う。また、細かいところでまだ使いづらいので色々といじっていこうと思う。

目次

今まで書いた記事の目次。自分用に作っとく。
ここから辿れば見やすいはず。随時(気が向いたときに)更新していく。

SDL2の使い方

ゲームを作るための環境設定
SDL2で文字を表示する
SDL2で画像を表示する
SDL2で音を鳴らす
SDL2でアニメーションを表示する SDL2でキーボード入力を検知をする
SDL2でマウス入力を検知する

RPGを作りたい

f:id:K38:20190216012600g:plain
第1回 マップを表示する
第2回 マップとの当たり判定を入れる
第3回 キャラクターに進行方向を向かせる
第4回 プレイヤーは常に画面の中心
第5回 滑らかな移動
第6回 マップの読み込み
第7回 マップ間移動
第8回 移動時の暗転処理
第9回 NPCの表示
第10回 NPCを動かす

MAPEditerを作る(RPGを作りたいからの派生)

f:id:K38:20190325215445g:plain
第1回 Editerを表示する
第2回 マップチップを配置する
第3回 機能の追加(マップチップの表示・カーソル座標の表示・スポイト機能)
第4回 文字入力Windowの作成
第5回 保存・読込

SDL2でMAPEditerを作る 〜第1回 Editerを表示する〜

前回マップを作ろうとした所、データの持ち方がよろしくないことが判明した。今の状態のままだと配列と数値の持ちかたの関係でマップチップをひと桁の数値にしか割り当てることができない。
ぱっと思いつく、もっと多くのマップチップを扱う方法(ふた桁以上の数値を扱う方法)はデータをCSV形式で持つか、バイナリで持つ方法。どちらかに変更すれば、ふた桁以上であろうと配列に上手く読み込める(はず)。

そこで、今回から何回かかけてマップのデータの扱いの改善を図っていく。ついでに、テキストファイルを手で修正するのは正直しんどいのでマップを作るためのMAPEditerを作っていこうと思う。 ちなみに、方針はバイナリ形式でデータを扱う方向でいこうと思う(バイナリで扱う時点で手での修正はやりたくない)。では、早速作っていこう。

Editerの表示

まずは、マップを描くためのキャンパスを表示するところまでを作ってみた。方向キーで、マップチップを置くためのカーソルを移動する。基本的にはRPG作成で作ってきたプレイヤーの動作方法と同じ手法でできている。
仕組みの説明は-> ここ(RPG 第4回プレイヤーは常に画面の中心) を参照してほしい。 では、以下に全コードを載せる。

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

const int SCREEN_WIDTH = 800;
const int SCREEN_HEIGHT = 600;
const int IMAGE_WIDTH = 16;
const int IMAGE_HEIGHT = 16;
const int MAGNIFICATION = 2;
const int GRID_SIZE = 32;
int ROW = 15;
int COL = 20;
int OUT_OF_MAP = 10;
int DEFAULT_MAP = 1;
int frame = 0;

int clac_offset(int, int, int *, int *);
int load_mapchip(SDL_Renderer *);
int draw_map(SDL_Renderer *);
int cursor_move(SDL_Event, SDL_Renderer *);
int load_image(SDL_Renderer *, SDL_Texture **, char *);


typedef struct {
    int map_x;
    int map_y;
    int offset_x;
    int offset_y;
} CURSOR;

typedef struct {
    int mapchip_id;
    char mapchip_name[256];
    int movable;
    int change_locate;
    SDL_Texture *map_image;
} MAPCHIP;

CURSOR cursor = {1, 1, 0, 0};
MAPCHIP mapchip[256] = {0};

int map_array[65536] = {0};
int number_of_map_image = 0;


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( "MAP CREATER", 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);
    }

    load_mapchip(renderer);

    // main loop
    while (1) {

        SDL_Delay(10);
        SDL_Event e;

        clac_offset(cursor.map_x, cursor.map_y, &cursor.offset_x, &cursor.offset_y);

        SDL_RenderClear(renderer);
        draw_map(renderer);
        cursor_move(e, renderer);
        SDL_RenderPresent(renderer);

        // event handling
        if ( SDL_PollEvent(&e) ) {
            if (e.type == SDL_QUIT){
                break;
            } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE){
                break;
            }
        }

    }

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

    int i;
    for (i = 0;i < number_of_map_image;i++) {
        SDL_DestroyTexture(mapchip[i].map_image);
    }

    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 cursor_move(SDL_Event e, SDL_Renderer *renderer) {

    SDL_Rect rectangle;
    SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);

    rectangle.x = cursor.map_x - cursor.offset_x;
    rectangle.y = cursor.map_y - cursor.offset_y;
    rectangle.w = GRID_SIZE;
    rectangle.h = GRID_SIZE;

    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 100);
    SDL_RenderFillRect(renderer, &rectangle);

    if (frame % 12 == 0) {

        if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_UP){
            cursor.map_y = cursor.map_y - GRID_SIZE;
        } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_DOWN){
            cursor.map_y = cursor.map_y + GRID_SIZE;
        } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_RIGHT){
            cursor.map_x = cursor.map_x + GRID_SIZE;
        } else if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_LEFT){
            cursor.map_x = cursor.map_x - GRID_SIZE;
        }

     }

    if (frame <= 12000) {
        frame++;
    } else {
        frame = 0;
    }

    return 0;

}

int draw_map(SDL_Renderer *renderer){

    int x, y;
    int start_x = cursor.offset_x / GRID_SIZE - 1;
    int end_x = start_x + SCREEN_WIDTH / GRID_SIZE + 2;
    int start_y = cursor.offset_y / GRID_SIZE - 1;
    int end_y = start_y + SCREEN_HEIGHT/ GRID_SIZE + 2;


    for(y = start_y;y < end_y;y++){
        for(x = start_x; x < end_x;x++){

            SDL_Rect imageRect=(SDL_Rect){0, 0, IMAGE_WIDTH, IMAGE_HEIGHT};
            SDL_Rect drawRect=(SDL_Rect){(x * GRID_SIZE) - cursor.offset_x,
                                         (y * GRID_SIZE) - cursor.offset_y,
                                         IMAGE_WIDTH*MAGNIFICATION, IMAGE_HEIGHT*MAGNIFICATION};

            if ((x < 0) || (x > COL - 1) || (y < 0) || (y > ROW - 1)){
                SDL_RenderCopy(renderer, mapchip[OUT_OF_MAP].map_image, &imageRect, &drawRect);
            } else {
                SDL_RenderCopy(renderer, mapchip[DEFAULT_MAP].map_image, &imageRect, &drawRect);
            }

        }
    }

    return 0;
}

int load_mapchip(SDL_Renderer *renderer) {

    FILE *fp;
    int x, y, z;
    char n[256];
    char path[256];
    char buf[256];
    int i = 0;

    fp = fopen("data/mapchip.dat", "r");
    if (fp == NULL) {
        printf("file open error. %d\n", __LINE__);
        return 1;
    }

    for(i = 0;fgets(buf, sizeof(buf), fp) != NULL;i++){
        sscanf(buf, "%d,%[^,],%d,%d", &x, n, &y, &z);
        mapchip[i].mapchip_id = x;
        strcpy(mapchip[i].mapchip_name, n);
        mapchip[i].movable = y;
        mapchip[i].change_locate = z;

        sprintf(path, "image/mapchip/%s.bmp", mapchip[i].mapchip_name);
        load_image(renderer, &mapchip[i].map_image, path);

    }

    number_of_map_image = i - 1;

    fclose(fp);

    return 0;

}

int clac_offset(int x, int y, int *offset_x, int *offset_y) {
    *offset_x = x - (SCREEN_WIDTH / 2);
    *offset_y = y - (SCREEN_HEIGHT / 2);

    return 0;
}

実行結果

実行結果はこんな感じ。
まだ、キャンパスの上をカーソルが動くだけで何の面白みもないがひとまず表示できた。
f:id:K38:20190222002454g:plain

終わりに

今回はとりあえず表示部分までをつくってみた。次回以降では、マップチップをキャンパスに置いて行く仕組みや、それをバイナリ形式で読んだり書いたりできる仕組みを実装していこうと思う。