[cub3d](14)제대로된 맵 파싱하기



1️⃣ 목표

  • get_next_line함수를 이용하여 저장한 맵배열을 보정하여 새로운 변수에 저장할 예정입니다.
  • 굳이 새로운 변수를 만들어 옮기는 이유는 다음과 같습니다.

1. 미니맵을 그릴 때 최대 길이를 기준으로 항상 직사각형형태로 배열을 읽습니다. 하지만 get_next_line함수로 저장한 맵배열은 각각의 줄의 길이가 다를 수있습니다. 그럴경우 쓰래기값(할당되지않은 메모리)를 읽게됩니다. 예를 들면 다음과 같은 경우입니다. /* 기대한 출력 */
1 1 1 1 1 1 1
1 1 1 1
1 1 1 1
/* 쓰래기 값이 출력 */
1 1 1 1 1 1 1
1 1 1 1   t
1 1 1 1   1 // 우연히 쓰래기값이 1

만약 쓰래기값이 우연히 1과 같은 값으로 저장되어 있다면 으로 인식하게 될 것입니다. 이러한 버그가 일어나지않게 하기위함입니다.

2. 저장된 맵row(줄),col(열)의 수치는 렌더링을 할때 필요한 수치를 계산하기위해 이곳저곳에서 쓰이게 됩니다. 심지어 창의 가로, 세로길이도 이 값에 의존적입니다. 하지만 픽셀을 이용하는 mlx함수 특성상 int(정수)로 강제 케스팅되어 사용하는 경우가 많습니다.
–>만약 저장된 맵row(줄),col(열)을 짝수가 되게끔 보정한다면 int(정수)로 강제 케스팅 때문에 생기는 오차를 조금이라도 줄일 수 있습니다.


2️⃣ map 구조체 선언, 옮기기

(1) map 구조체 선언

typedef struct s_map
{
    int map_rows;
    int map_cols;
    int window_width;
    int window_height;
    int ray_count;
    char **map;
}               t_map;
  • 기존에 매크로로 정의했던 윈도우 너비, 윈도우 높이, 광선의 수 들은 cub파일의 맵에 따라 달라지게 되기 때문에 변수로 만들어 map구조체에 저장해 주었습니다.

(2) set_map함수

void    set_map(t_god *god)
{
    int y;
    int row;
    int col;

    god->map.map = (char **)malloc(sizeof(char *) * god->map.map_rows);
    y = -1;
    while (++y < god->map.map_rows)
        god->map.map[y] = (char *)malloc(sizeof(char) * (god->map.map_cols + 1));
    row = -1;
    while (++row < god->parse.row)
    {
        col = -1;
        while (god->parse.map[row][++col] != '\0')
            god->map.map[row][col] = god->parse.map[row][col];
		col--;
		while (++col < god->map.map_cols)
        	god->map.map[row][col] = ' ';
        god->map.map[row][col] = '\0';
    }
    free_array_memory((void **)god->parse.map, god->parse.row);
}
  • 기존에 parse->map변수에 ft_split함수를 이용하여 요소를 저장했었습니다. 이것을 최대 열길이에 맞춰서 2차원 동적할당을 해주었습니다.
  • 그후 조건문을 통해 뒤쪽의 쓰래기값 대신 공백이 저장해 주었습니다.


3️⃣ 모서리가 통과되는 버그

(1) 버그 발견

  • 벽의 구조가 다음과 같을 때 광선(ray)가 통과하는 현상이 발견되었습니다. 당연히 player도 통과가 되었습니다.
/* 문제가 되는 맵의 좌표 */
0 1
1 0

bug

  • 어짜피 player의 부피를 서브젝트가 정의해주지 않았기때문에 모서리를 통과해도 괜찮지 않을까 생각을 했지만 그래도 통과하지 못하도록 막는 것이 상식적으로 맞을 것 같습니다.
  • 먼저 문제의 원인은 제가 구현한 렌더링함수픽셀단위로 모든 벽을 탐색하지 않습니다.
  • 우연히 꼭지점을 탐색하더라도 꼭지점 기준 왼쪽 상단의 좌표로 인식할 것입니다.

(2) 버그 해결하기

  • 벽을 탐색하는 함수인 is_wall()함수를 수정해줄 필요가 있습니다.
int check_edge(t_god *god, double x1, double x2, double y1, double y2)
{
    int dx;
    int dy;

    dx = (int)(x1 / TILE_SIZE) - (int)(x2 / TILE_SIZE);
    dy = (int)(y1 / TILE_SIZE) - (int)(y2 / TILE_SIZE);
    int dx2 = (int)(x1 / TILE_SIZE);
    int dy2 = (int)(y1 / TILE_SIZE);
    
    if (dx == 1 && dy == 1)
        return (ft_strchr("0NSEW", god->map.map[dy2 - dy][dx2]) == NULL) && (ft_strchr("0NSEW", god->map.map[dy2][dx2 - dx]) == NULL);
    if (dx == 1 && dy == -1)
        return (ft_strchr("0NSEW", god->map.map[dy2 - dy][dx2]) == NULL) && (ft_strchr("0NSEW", god->map.map[dy2][dx2 - dx]) == NULL);
    if (dx == -1 && dy == 1)
        return (ft_strchr("0NSEW", god->map.map[dy2 - dy][dx2]) == NULL) && (ft_strchr("0NSEW", god->map.map[dy2][dx2 - dx]) == NULL);
    if (dx == -1 && dy == -1)
        return (ft_strchr("0NSEW", god->map.map[dy2 - dy][dx2]) == NULL) && (ft_strchr("0NSEW", god->map.map[dy2][dx2 - dx]) == NULL);
    return FALSE;
}
  • check_dege()함수를 이용하여 is_wall()함수로 벽을 확인할때 이 함수를 추가로 사용하여 검사해주도록 하였습니다. resolve_bug

  • 이제 광선(ray)player모두 통과하지 않습니다.





© 2021.02. by kirim

Powered by kkrim