パーセプトロン
最近、仕事が忙しい&立て続けに資格試験を受けなくてはならなくてゲーム作りをまったくしていない。今年はOracleSilver12cから始まり、最近はG検定というのを取得した。続いて、E資格というのを取得する必要があるので、勉強のためにゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装という本を読み始めた。 (E資格の前にExam DP-100: Designing and Implementing a Data Science Solution on Azureという資格も取らなくてはならない。今年は勉強の1年だ。。。) DeepLearningは実装にpythonを使うのが主流のようだが、趣味でソースを書く文にはC言語が好きなので本にある実装をC言語に置き換えながら勉強を進めていきたいと思う。で、とりあえず2章パーセプトロンのソースをC言語で書いてみた。ゲーム作りをしていないせいでブログも全く書いていないので、勉強の経過も気が向いたら書こうと思う。
実装
ソースを載せる前にパーセプトロンというのはどんなものかを箇条書きにしておく。
- パーセプトロンは入出力を備えたアルゴリズム。ある入力に対して決まった値が出力される
- パーセプトロンでは、「重み」と「バイアス」をパラメータとして設定する
- 単層パーセプトロンは線形領域しか表現できない。多層パーセプトロンにすると非線形領域を表現できる
と、こういったものらしい。
では、それを踏まえてC言語で書いたのが以下になる。
#include <stdio.h> int AND(int, int); int NAND(int, int); int OR(int, int); int XOR(int, int); int main() { printf("AND(0, 0): %d\n", AND(0, 0)); printf("AND(0, 1): %d\n", AND(0, 1)); printf("AND(1, 0): %d\n", AND(1, 0)); printf("AND(1, 1): %d\n", AND(1, 1)); printf("NAND(0, 0): %d\n", NAND(0, 0)); printf("NAND(0, 1): %d\n", NAND(0, 1)); printf("NAND(1, 0): %d\n", NAND(1, 0)); printf("NAND(1, 1): %d\n", NAND(1, 1)); printf("OR(0, 0): %d\n", OR(0, 0)); printf("OR(0, 1): %d\n", OR(0, 1)); printf("OR(1, 0): %d\n", OR(1, 0)); printf("OR(1, 1): %d\n", OR(1, 1)); printf("XOR(0, 0): %d\n", XOR(0, 0)); printf("XOR(0, 1): %d\n", XOR(0, 1)); printf("XOR(1, 0): %d\n", XOR(1, 0)); printf("XOR(1, 1): %d\n", XOR(1, 1)); return 0; } int AND(int x1, int x2) { double x[2] = {x1, x2}; double w[2] = {0.5, 0.5}; double b = -0.7; double tmp = x[0]*w[0] + x[1]*w[1] + b; if (tmp <= 0) { return 0; } else { return 1; } } int NAND(int x1, int x2) { double x[2] = {x1, x2}; double w[2] = {-0.5, -0.5}; double b = 0.7; double tmp = x[0]*w[0] + x[1]*w[1] + b; if (tmp <= 0) { return 0; } else { return 1; } } int OR(int x1, int x2) { double x[2] = {x1, x2}; double w[2] = {0.5, 0.5}; double b = -0.2; double tmp = x[0]*w[0] + x[1]*w[1] + b; if (tmp <= 0) { return 0; } else { return 1; } } int XOR(int x1, int x2) { int s1 = NAND(x1, x2); int s2 = OR(x1, x2); int y = AND(s1, s2); return y; }
所感
XOR以外はどの回路も重みの違いのみで表現できているのがおもしろい。XORも単層パーセプトロンを重ねて2層にすることで表現できているので、とどのつまり、単層パーセプトロンを重ねていく(たぶんこの辺がDeepということなんだろうか)ことで複雑なものも作れるということのようだ。昔、シーマンという、おそらく推論と探索を駆使したゲームがあったが、それみたいにDeepLearningを学んでゲーム作りに活かせたらおもしろいなぁとおもう。
SDL2でRPGゲームを作る 〜第12回 メッセージボックスの作成 その1〜
前回投稿から時間が立ってしまったが少し進捗があがったので、まとめておこうと思う。
今回は、NPCに話しかけたら、メッセージボックスが表示されて、その中に文字が表示されるといったところまで作っていきたい。では、早速作っていこう。
メッセージボックスを表示する
まず、メッセージボックスを作成するために以下の関数を作成した。
int make_window(SDL_Renderer *renderer, WINDOW window) { int edge_size = 4; SDL_Rect rectangle; SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); rectangle.x = window.rectangle_x; rectangle.y = window.rectangle_y; rectangle.w = window.rectangle_w; rectangle.h = window.rectangle_h; // WHITE SDL_SetRenderDrawColor(renderer, 255, 255, 255, window.blend); SDL_RenderFillRect(renderer, &rectangle); rectangle.x = window.rectangle_x + edge_size; rectangle.y = window.rectangle_y + edge_size; rectangle.w = window.rectangle_w - edge_size * 2; rectangle.h = window.rectangle_h - edge_size * 2; // BLACK SDL_SetRenderDrawColor(renderer, 0, 0, 0, window.blend); SDL_RenderFillRect(renderer, &rectangle); return 0; }
引数に取っているWINDOW構造体
は、こんな感じ。
typedef struct { int rectangle_x; int rectangle_y; int rectangle_w; int rectangle_h; int blend; VISIBLE visible; } WINDOW;
この関数を使用すると、白い四角形の上に黒い四角形が重なる形でメッセージボックスが作成される。黒い四角形は白い四角形より、上下左右4ピクセル小さく作成されるため白い縁がついた見た目の四角形が表示できる。
次に、作成した四角形もといメッセージボックスの表示・非表示を切り替えるの関数を作成した。
int window_engine(SDL_Renderer *renderer, WINDOW window) { if (window.visible == OUT_VISIBLE) { return 0; } make_window(renderer, window); return 0; } int window_update(SDL_Renderer *renderer, TTF_Font *font, SDL_Event e) { if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_SPACE){ if (message_window.visible == OUT_VISIBLE) { message_window.visible = IN_VISIBLE; } else { message_window.visible = OUT_VISIBLE; } } window_engine(renderer, message_window); char *message; get_character_message(e, &message); if (message_window.visible == IN_VISIBLE) { display_character_string(renderer, font, message, 144, 338); } return 0; }
window_update関数
は、メインループから呼ばれる関数で、スペースが押されたかどうかと画面の表示・非表示の状態を判断して
メッセージボックスの表示・非表示の切り替えを行う。また、windows_engine関数
は、画面が表示状態と判断されたときに
メッセージボックスの作成を行う働きをする。
文字を表示する
文字を表示するためには、->ここでまとめている通り、SDL2_ttf
を使うことで比較的容易に表示することができる。NPCと会話するためには、イベントファイルに記載されているメッセージを読み込んで
表示させる必要がある。
まず、イベントファイルに記載されているメッセージを取得するために以前作成したload_npc関数
に次の3行を追加した。
load_npc関数
message_length = strlen(message); message[message_length - 1] = '\0'; sprintf(npc[element_number].message, "%s", message);
これは、メッセージの長さを取得して終端文字を追加し、それをNPCのメッセージとして取得しておくと言った処理になる。
次に、取得したメッセージを表示するためにNPCの持っているメッセージを取得する関数を作成した。プレイヤーの目の前にいるNPCからはメッセージを取得し、誰もいなければ誰もいない
といった文字列を返す。
int get_character_message(SDL_Event e, char **message) { int i; if (player.direction == UP) { if (is_movable(player.map_x, player.map_y - 1) == 1) { for(i = 0;i < number_of_npc_image;i++) { if (npc[i].npc.map_x == player.map_x && npc[i].npc.map_y == player.map_y - 1) { *message = npc[i].message; break; } } } else { *message = "その方向には誰もいない"; } } else if (player.direction == DOWN) { if (is_movable(player.map_x, player.map_y + 1) == 1) { for(i = 0;i < number_of_npc_image;i++) { if (npc[i].npc.map_x == player.map_x && npc[i].npc.map_y == player.map_y + 1) { *message = npc[i].message; break; } } } else { *message = "その方向には誰もいない"; } } else if (player.direction == RIGHT) { if (is_movable(player.map_x + 1, player.map_y) == 1) { for(i = 0;i < number_of_npc_image;i++) { if (npc[i].npc.map_x == player.map_x + 1 && npc[i].npc.map_y == player.map_y) { *message = npc[i].message; break; } } } else { *message = "その方向には誰もいない"; } } else if (player.direction == LEFT) { if (is_movable(player.map_x - 1, player.map_y) == 1) { for(i = 0;i < number_of_npc_image;i++) { if (npc[i].npc.map_x == player.map_x - 1 && npc[i].npc.map_y == player.map_y) { *message = npc[i].message; break; } } } else { *message = "その方向には誰もいない"; } } 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; }
全コード表示
では、全コードを記載する。
window.h
#ifdef _RPGINC #else #include <stdio.h> #include <SDL2/SDL.h> #include <SDL2/SDL_image.h> #include <SDL2/SDL_ttf.h> typedef enum {DOWN, LEFT, RIGHT, UP} DIRECTION; typedef enum {FALSE, TRUE} MOVING; typedef enum {OUT_VISIBLE, IN_VISIBLE} VISIBLE; 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; typedef struct { int rectangle_x; int rectangle_y; int rectangle_w; int rectangle_h; int blend; VISIBLE visible; } WINDOW; 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 *); int make_window(SDL_Renderer *, WINDOW); int window_engine(SDL_Renderer *, WINDOW); int window_update(SDL_Renderer *, TTF_Font *, SDL_Event); int display_character_string(SDL_Renderer *, TTF_Font *, char *, int, int); int get_character_message(SDL_Event, char **); #endif
window.c
#include <stdio.h> #include <stdlib.h> #include <time.h> #include <SDL2/SDL.h> #include <SDL2/SDL_image.h> #include <SDL2/SDL_ttf.h> #include "RPGInC.h" #define FONT_PATH "font/PixelMplus12-Regular.ttf" 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; const int FONT_SIZE = 16; 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; WINDOW message_window = {140, 334, 360, 140, 255, OUT_VISIBLE}; int main (int argc, char *argv[]) { SDL_Window *window = NULL; SDL_Renderer *renderer = NULL; TTF_Font *font = 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); } //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()); } load_mapchip(renderer); load_map("data/field.map"); load_npc(renderer); // main loop while (1) { SDL_Delay(5); 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); window_update(renderer, font, 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] = {0}; sprintf(event_path, "data/%s.evt", MAP_EVENT_NAME); FILE *fp; char event[256] = {0}; char npc_name[256] = {0}; int map_x; int map_y; DIRECTION direction; MOVING moving; int max_step; int message_length; char message[1024] = {0}; char buf[256] = {0}; char npc_path[256] = {0}; 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; message_length = strlen(message); message[message_length - 1] = '\0'; 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] = {0}; sprintf(event_path, "data/%s.evt", MAP_EVENT_NAME); FILE *fp; char event[256] = {0}; int event_point_x; int event_point_y; DIRECTION direction_of_penetration; char buf[256] = {0}; char new_map_name[256] = {0}; char map_path[256] = {0}; 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); break; } } } } } 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] = {0}; char path[256] = {0}; char buf[256] = {0}; 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 2; } if (mapchip[map_array[y*COL+x]].movable == 1) { return 2; } 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; } int make_window(SDL_Renderer *renderer, WINDOW window) { int edge_size = 4; SDL_Rect rectangle; SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); rectangle.x = window.rectangle_x; rectangle.y = window.rectangle_y; rectangle.w = window.rectangle_w; rectangle.h = window.rectangle_h; // WHITE SDL_SetRenderDrawColor(renderer, 255, 255, 255, window.blend); SDL_RenderFillRect(renderer, &rectangle); rectangle.x = window.rectangle_x + edge_size; rectangle.y = window.rectangle_y + edge_size; rectangle.w = window.rectangle_w - edge_size * 2; rectangle.h = window.rectangle_h - edge_size * 2; // BLACK SDL_SetRenderDrawColor(renderer, 0, 0, 0, window.blend); SDL_RenderFillRect(renderer, &rectangle); return 0; } int window_engine(SDL_Renderer *renderer, WINDOW window) { if (window.visible == OUT_VISIBLE) { return 0; } make_window(renderer, window); return 0; } int window_update(SDL_Renderer *renderer, TTF_Font *font, SDL_Event e) { if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_SPACE){ if (message_window.visible == OUT_VISIBLE) { message_window.visible = IN_VISIBLE; } else { message_window.visible = OUT_VISIBLE; } } window_engine(renderer, message_window); char *message; get_character_message(e, &message); if (message_window.visible == IN_VISIBLE) { display_character_string(renderer, font, message, 144, 338); } return 0; } int get_character_message(SDL_Event e, char **message) { int i; if (player.direction == UP) { if (is_movable(player.map_x, player.map_y - 1) == 1) { for(i = 0;i < number_of_npc_image;i++) { if (npc[i].npc.map_x == player.map_x && npc[i].npc.map_y == player.map_y - 1) { *message = npc[i].message; break; } } } else { *message = "その方向には誰もいない"; } } else if (player.direction == DOWN) { if (is_movable(player.map_x, player.map_y + 1) == 1) { for(i = 0;i < number_of_npc_image;i++) { if (npc[i].npc.map_x == player.map_x && npc[i].npc.map_y == player.map_y + 1) { *message = npc[i].message; break; } } } else { *message = "その方向には誰もいない"; } } else if (player.direction == RIGHT) { if (is_movable(player.map_x + 1, player.map_y) == 1) { for(i = 0;i < number_of_npc_image;i++) { if (npc[i].npc.map_x == player.map_x + 1 && npc[i].npc.map_y == player.map_y) { *message = npc[i].message; break; } } } else { *message = "その方向には誰もいない"; } } else if (player.direction == LEFT) { if (is_movable(player.map_x - 1, player.map_y) == 1) { for(i = 0;i < number_of_npc_image;i++) { if (npc[i].npc.map_x == player.map_x - 1 && npc[i].npc.map_y == player.map_y) { *message = npc[i].message; break; } } } else { *message = "その方向には誰もいない"; } } 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; }
実行結果
実行結果はこんな感じ。メッセージボックスが表示された。
終わりに
今回はメッセージボックスの表示ができるようになった。しかしながら、NPCがそっぽむいたままをなおしたり流れるようなメッセージにしたり、改行、改ページ等々改造しどころがたくさんあるので徐々に作っていきたいと思う。
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個以上のマップチップが使えている。
終わりに
今回はバイナリデータの読込みを行った。ソースコードとしては大した変更は入れていないが これで、気合さえあれば凝ったマップが作れるようになった。次は、メッセージボックスの作成を行っていきたいと思う。
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だとわかりづらいが、保存・読込ができるようになった。
終わりに
以上で、マップデータの保存・読込が実装できた。これで、マップはかなり思い通りに作成できるようになった。 後は、細かい部分の使い勝手を向上させていければと思う。
SDL2でMAPEditerを作る 〜第4回 文字入力Windowの作成〜
前回は、マップを作る上で役に立つちょっとした機能の追加を行った。今回は、保存・読込の実装をしたのでその辺りを書いていこうと思ったのだが、保存・読込自体よりファイル名を入力するための文字入力機能を作るほうがパワーを使う結果になったので、今回は文字入力Windowについて、次回で本題の保存・読込について書いていきたいと思う。
文字入力Windowとは?
まず、そもそも文字入力Windowとは一体どのようなものを作ったのかというところなのだが、以下のGIFのような入力Windowを作成した。
大文字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; }
画像だとこんな感じになる。
カーソル座標の表示
続いて、追加してのはカーソル座標の表示である。これは、-> 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を使えば容易に文字を表示できるのは確かだ。まあ、何はともあれ力技ではあるが狙ったところに表示できたので良しとしたい。(マップチップウィンドウの下に表示してみた)
スポイト機能の追加
マップを作っていくうえで、マップチップパレットに切り替えるまでもない修正、例えば隣のマップチップと同じものを 使いたい、といった場面は多々発生すると思われる。その時のために、マップ上のマップチップを設定できるスポイト機能を作成した。コードは以下になる。
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; }
実行結果
今回実装分の見た目はこんな感じ。
終わりに
今回は、マップを書く上での利便性を良くするための機能を追加した。次回辺り、保存・読込といったあたりの実装ができればと思う。
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; }
実行結果
実行結果はこんな感じ。パレットに切り替えてマップチップを取得しキャンパスに配置できるようになった。
終わりに
今回でマップを描くことが可能になった。ただ、まだ描いたマップを保存したり読み込んだりができないので、その辺りを次回は実装していこと思う。また、細かいところでまだ使いづらいので色々といじっていこうと思う。