typedef struct s_data {
char **ber_arr; //map 정보가 담길 2차원 배열들
void *mlx; //mlx_init()의 리턴값이 담길 주소 포인터
void *win; //mlx_new_window()의 리턴값이 담길 주소 포인터
void *grass; //ber_arr[][]의 값이 '0'일 경우 땅 이미지를 넣을 포인터
void *wall; //"" '1'일 경우 벽 이미지를 넣을 포인터
void *player; // "" 'P'일 경우 캐릭터 이미지를 넣을 포인터
void *chest; // "" 'C'일 경우 아이템 이미지를 넣을 포인터
void *door; // "" 'E'일 경우 문 이미지를 넣을 포인터
char *map;
int items; //캐릭터가 이동하며 아이템을 획득할 경우 하나씩 줄어들 아이템의 갯수 (0이 되고 E로 탈출해야 게임 종료)
int walk; //걸음수를 terminal에 출력하기 위한 걸음수 체크 변수
int now_x; //캐릭터의 현재 위치
int now_y; //캐릭터의 현재 위치
int x; //캐릭터의 다음 위치
int y; //캐릭터의 다음 위치
int box_width; //ber파일의 인자값 갯수
int box_height; //ber파일의 인자값 갯수
int img_width; //mlx_xpm_file_to_image의 3,4번째 인자값으로 사용할 이미지의 크기, 따로 설정하지 않아도 사진 크기에 맞게 조절됨 (주의, xpm으로 변환할 png파일들은 크기가 모두 같아야 함.)
int img_height; //""
} t_data;
typedef struct s_dfs {
int items; //dfs에서 검사할 유효한 아이템
int door; //dfs에서 검사할 유효한 문
int **arr; //dfs에서 사용할 ber_arr과 똑같은 이중배열 포인터
} t_dfs;
int main(int ac, char **av)
{
t_dfs tdfs;
t_data data;
int fd;
if (ac == 2)
{
fd = open(av[1], O_RDONLY);
if (fd <= 0)
print_err0("File open fail.\n");
data.mlx = mlx_init();
data.ber_arr = map_read(av[1], &data); //2차원 배열에 .ber파일의 내용들을 개행 기준으로 split해서 담는 함수.
valid_test(&data); //split해서 담은 내용들을 하나씩 흝어 P와 E가 하나만 있고 C가 1개 이상 있는지 검사한다.
data = set_win_img(data); //윈도우 창을 만들고, 사진을 넣는 함수.
dfs(&data, &tdfs); //dfs를 통해 이루어질 수 없는 맵(player가 아이템을 먹을 수 없거나 탈출구로 나갈 수 없는 맵) 검사
mlx_hook(data.win, KEYPRESS, 0, &deal_key, &data); //키보드로 w,a,s,d 입력시 캐릭터가 이동하도록 하는 함수(deal_key)의 주소를 담은 hook 함수
mlx_hook(data.win, 17, 0, &deal_mouse, &data); //마우스로 window의 왼쪽 상단 X를 눌러 종료 할 경우를 위한 deal_mouse 함수의 주소를 담은 hook 함수
mlx_loop(data.mlx);
}
else
print_err0("Map is missing.\n"); //argv의 인자값이 1개보다 더 들어왔을 경우를 위한 에러처리.
return (0);
}
t_data set_win_img(t_data data)
{
int i;
i = 0;
while (data.ber_arr[i])
i++;
data.box_height = i;
data.items = 0;
data.walk = 0;
data.box_width = ft_strlen(data.ber_arr[0]);
data.win = mlx_new_window(data.mlx, data.box_width * 64, \
data.box_height * 64, "my_mlx");
data.wall = mlx_xpm_file_to_image(data.mlx, "./image/walls.xpm", \
&data.img_width, &data.img_height);
data.grass = mlx_xpm_file_to_image(data.mlx, "./image/grass.xpm", \
&data.img_width, &data.img_height);
data.player = mlx_xpm_file_to_image(data.mlx, "./image/player.xpm", \
&data.img_width, &data.img_height);
data.chest = mlx_xpm_file_to_image(data.mlx, "./image/chest_01.xpm", \
&data.img_width, &data.img_height);
data.door = mlx_xpm_file_to_image(data.mlx, "./image/door.xpm", \
&data.img_width, &data.img_height);
put_image(&data);
return (data);
}
각종 void 이미지 포인터에 .xpm 파일을 넣어주는 함수이다.
ㅡ
ㅡ
void put_image(t_data *data)
{
int i;
int j;
int count;
i = -1;
data->y = 0;
count = 0;
while (data->ber_arr[++i])
{
j = -1;
data->x = 0;
while (data->ber_arr[i][++j])
{
check_map(data, i, j);
data->x += 64;
count++;
}
data->y += 64;
}
check_wall(data);
if (count != data->box_height * data->box_width)
print_err("Error\nMap must be rectangular.\n", data);
}
check_map을 통해 이미지를 window에 출력하고 check_wall을 통해 벽으로 둘러쌓여있는지 확인한 후 맵이 사각형이 아니면 에러문 출력 함수로 넘어간다. (free해줌)
ㅡ
ㅡ
void check_map(t_data *data, int i, int j)
{
if (data->ber_arr[i][j] == '1')
mlx_put_image_to_window(data->mlx, data->win, \
data->wall, data->x, data->y);
else if (data->ber_arr[i][j] == '0')
do_mlx_put_image_grass(data);
else if (data->ber_arr[i][j] == 'P')
{
do_mlx_put_image_grass(data);
mlx_put_image_to_window(data->mlx, data->win, \
data->player, data->x, data->y);
data->now_x = j;
data->now_y = i;
}
else if (data->ber_arr[i][j] == 'C')
{
do_mlx_put_image_grass(data);
do_mlx_put_image_chest(data);
data->items++;
}
else if (data->ber_arr[i][j] == 'E')
mlx_put_image_to_window(data->mlx, data->win, \
data->door, data->x, data->y);
else
print_err("Error\nWrong Map\n", data);
}
void check_wall(t_data *data)
{
int i;
i = -1;
while (++i < data->box_width)
{
if (data->ber_arr[0][i] != '1')
print_err("Error\nMap must be closed/surrounded by walls\n", data);
if ((data->ber_arr[data->box_height - 1][i]) != '1')
print_err("Error\nMap must be closed/surrounded by walls\n", data);
}
i = -1;
while (++i < data->box_height)
{
if ((data->ber_arr[i][0]) != '1')
print_err("Error\nMap must be closed/surrounded by walls\n", data);
if ((data->ber_arr[i][data->box_width - 1]) != '1')
print_err("Error\nMap must be closed/surrounded by walls\n", data);
}
}
check_map 함수는 이미지를 넣음과 동시에 1,0,P,C,E가 아닌 문자가 나오면 에러문을 출력한다.
check_wall 함수는 벽으로 둘러쌓여있지 않으면 에러문을 출력한다.
ㅡ
ㅡ
void dfs(t_data *data, t_dfs *tdfs)
{
int x;
int y;
int i;
i = -1;
x = data->now_x; //처음 player의 위치
y = data->now_y; //처음 player의 위치
tdfs->items = data->items; //아이템 전체의 갯수
tdfs->door = 0; //문을 만나면 하나씩 증가하므로 0으로 초기화
init_tdfs(data, tdfs); //tdfs->arr[][]의 값을 모두 정수 0으로 초기화
find_items(data, tdfs, x, y); //맵 전체 아이템 검사
free_tdfs_arr(data, tdfs); //tdfs->arr[][] free
init_tdfs(data, tdfs); //tdfs->arr[][]의 값을 모두 정수 0으로 초기화
find_door(data, tdfs, x, y); //맵 전체 문 검사
free_tdfs_arr(data, tdfs); //tdfs->arr[][] free
if (tdfs->items != 0 || tdfs->door != 1) //아이템이 0이 아니거나 문 갯수가 1이 아니면 에러문 출력
print_err("Error\nInvalidMapFile\n", data);
}
ㅡ
ㅡ
void find_items(t_data *data, t_dfs *tdfs, int x, int y)
{
//상하좌우 한칸씩 이동하며 재귀로 맵 전체 아이템 검사
if (check_items_dfs(data, tdfs, x + 1, y))
find_items(data, tdfs, x + 1, y);
if (check_items_dfs(data, tdfs, x, y + 1))
find_items(data, tdfs, x, y + 1);
if (check_items_dfs(data, tdfs, x - 1, y))
find_items(data, tdfs, x - 1, y);
if (check_items_dfs(data, tdfs, x, y - 1))
find_items(data, tdfs, x, y - 1);
}
ㅡ
ㅡ
int check_items_dfs(t_data *data, t_dfs *tdfs, int x, int y)
{
if (data->ber_arr[y][x] == '1' || tdfs->arr[y][x] || \
data->ber_arr[y][x] == 'E' || !tdfs->items)
return (0); //벽이거나, 이미 방문해서 1이 찍혀있거나, 출구거나 아이템 갯수가 0개면 리턴으로 종료
if (data->ber_arr[y][x] == 'C')
tdfs->items--; //아이템을 만나면 아이템 수 하나씩 감소
tdfs->arr[y][x] = 1; //방문한 흔적을 남기기 위해 0->1로 값 변경
return (1);
}
ㅡ
ㅡ
find_items와 같음
void find_door(t_data *data, t_dfs *tdfs, int x, int y)
{
if (check_doors_dfs(data, tdfs, x + 1, y))
find_door(data, tdfs, x + 1, y);
if (check_doors_dfs(data, tdfs, x, y + 1))
find_door(data, tdfs, x, y + 1);
if (check_doors_dfs(data, tdfs, x - 1, y))
find_door(data, tdfs, x - 1, y);
if (check_doors_dfs(data, tdfs, x, y - 1))
find_door(data, tdfs, x, y - 1);
}
ㅡ
ㅡ
int check_doors_dfs(t_data *data, t_dfs *tdfs, int x, int y)
{
if (data->ber_arr[y][x] == '1' || tdfs->arr[y][x])
return (0); //1이거나 이미 방문했으면 리턴 0으로 종료
if (data->ber_arr[y][x] == 'E')
tdfs->door++; //문을 만나면 1씩 +
tdfs->arr[y][x] = 1; //방문 흔적 남기기
return (1);
}
ㅡ
ㅡ
int deal_key(int key_code, t_data *data)
{
//key_code 방향키에 따른 x, y좌표 변경 인자를 가지고 move_player 함수 실행
if (key_code == ESC)
good_bye(data);
else if (key_code == UP)
move_player(data, data->now_x, data->now_y - 1);
else if (key_code == LEFT)
move_player(data, data->now_x - 1, data->now_y);
else if (key_code == RIGHT)
move_player(data, data->now_x + 1, data->now_y);
else if (key_code == DOWN)
move_player(data, data->now_x, data->now_y + 1);
return (0);
}
ㅡ
ㅡ
void move_player(t_data *data, int x, int y)
{
if (data->ber_arr[y][x] == '1' || \
(data->items && data->ber_arr[y][x] == 'E'))
return ; //벽이거나, 아이템이 남은 상태로 출구에 갈 수 없도록 리턴
if (!data->items && data->ber_arr[y][x] == 'E')
good_bye(data); //아이템을 다 먹고 출구로 가면 게임 종료
if (data->ber_arr[y][x] == 'C')
{
data->items--; //아이템 먹어서 갯수 줄어듦
data->ber_arr[y][x] = 0; //땅을 의미하는 '0'으로 바꾸기
mlx_put_image_to_window(data->mlx, data->win, \
data->player, x * 64, y * 64); //땅 이미지 넣기
}
mlx_put_image_to_window(data->mlx, data->win, \
data->grass, data->now_x * 64, data->now_y * 64); //원래 있던 곳에 풀 이미지 넣기
data->walk++; //걸음 수 증가
ft_printf("%d\n", data->walk); //걸음 수 터미널 출력
mlx_put_image_to_window(data->mlx, data->win, \
data->grass, x * 64, y * 64); //이동하는 방향 풀 이미지 넣기
mlx_put_image_to_window(data->mlx, data->win, \
data->player, x * 64, y * 64); //이동된 방향 플레이어 이미지 넣기
data->now_x = x; //현재 위치 조정
data->now_y = y; //현재 위치 조정
}
ㅡ
ㅡ
int deal_mouse(t_data *data)
{
good_bye(data);
return (0);
}
ㅡ
ㅡ
void good_bye(t_data *data)
{
int i;
i = -1;
while (data->ber_arr[++i])
free(data->ber_arr[i]);
free(data->ber_arr); //ber_arr free
mlx_destroy_window(data->mlx, data->win); //윈도우 종료
exit(1); //exit로 탈출
}
이상 모든 과정과 함수에 대한 설명이 끝났다. 이해가 안되는 부분이 있으면 질문 적어주시면 감사하겠습니다.