[cub3d](5)player 구현하기(삽질의 과정)



1️⃣ 지도위에 player 구현하기

  • 지도를 완성했으니 그 위에 player를 구현할 차례입니다.

(1) t_player 구조체 구현하기

  • 먼저 player구조체를 생성해 주었습니다.
  • 나중에 player구조체의 변수는 늘어나겠지만 기본적으로 위치변수(x, y)크기변수(width, height)를 구성하였습니다.
/* cub3d.h */
typedef struct s_player {
    int x;
    int y;
    int thickness;
} t_player;

(2) player_setup함수 구현하기

  • player의 변수를 초기화 시켜주는 함수입니다.
/* cub3d.h */
#define PLAYER_THICKNESS 5

/* player.c */
void player_init(t_player *player)
{
    player->x = WINDOW_WIDTH / 2;
    player->y = WINDOW_HEIGHT / 2;
    player->thickness = PLAYER_THICKNESS;
}

(3) draw*map함수 구현하기

  • 설정해준 player를 화면에 그려주는 함수입니다.
  • 색상은 0x0000FF(파란색)으로 그리도록 했습니다.
  • 위치값인 x, y의 값은 중요하지만, player의 크기는 단순히 눈에 띄기만 하면되기 때문에 세세히 값을 계산하지 않았습니다. 그래도 표시되는 Player의 중심이 (x, y)되도록 그리기 위해 새로길이가로길이for문-(player->thickness) / 2 ~ (player->thickness) / 2의 범위로 그리도록 만들었습니다.
int    draw_player(t_mlx *mlx, t_player *player, t_img *map)
{
*map->data = (int *)mlx_get_data_addr*map->img, &*map->bpp), &*map->line_size), &*map->endian));
    for (int row = -(player->thickness) / 2; row <= (player->thickness) / 2; row++)
    {
        for (int col = -(player->thickness) / 2; col <= (player->thickness) / 2; col++)
        {
        *map->data[(int)(MINI_SCALE * (WINDOW_WIDTH * (player->y + row) + (player->x + col)))] = 0x0000FF;
        }
    }
    mlx_put_image_to_window(mlx->mlx, mlx->win,*map->img, (int)(WINDOW_WIDTH * (1 - MINI_SCALE)), (int)(WINDOW_HEIGHT * (1 - MINI_SCALE)));
    return (0);
}
  • 두께(thickness)값을 5로 지정했을 때 다음과 같이 중심값(0, 0)을 기준으로 가로새로 동일한 두께로 그려졌습니다. ((col, row)가 (0, 0)일때 player의 좌표인 (x, y)와 같아진다.)
  • 하지만 홀수일때는 두께1이 작게 그려집니다. 하지만 중심이 정확히 (x, y)인가만 중요할뿐 두께의 정확한 수치는 중요하지 않기 때문에 넘어가도록 하겠습니다.
/* for문안에서의 (col, row)변수값 테스트 */
(-2, -2)(-1, -2)(0, -2)(1, -2)(2, -2)
(-2, -1)(-1, -1)(0, -1)(1, -1)(2, -1)
(-2, 0)(-1, 0)(0, 0)(1, 0)(2, 0)
(-2, 1)(-1, 1)(0, 1)(1, 1)(2, 1)
(-2, 2)(-1, 2)(0, 2)(1, 2)(2, 2)

(4) render_player함수

  • player의 위치관련해서 관리해주는 함수입니다.
void render_player(t_mlx *mlx, t_img *map)
{
	t_player    player;

    player_init(&player);
    draw_player(mlx, &player, map);
}

(5) 화면에 출력해보기

int main(void)
{
    /* 코드생략 */
    t_img player;
    player.img = mlx_new_image(mlx.mlx, (int)(MINI_SCALE * WINDOW_WIDTH), (int)(MINI_SCALE * WINDOW_HEIGHT));
    render_player(&mlx, &player);
    /* 코드생략 */
}
  • 하지만 다음과 같이 출력되었습니다. player라는 t_img변수를 새로 선언하여 사용했고 player부분을 0x0000FF(파란색)으로 그려주도록하였습니다. 그리고 색을 설정하지 않은 부분은 투명하게 적용될줄 알았습니다.
  • 즉, 그전에 랜더링된 map의 그림이 출력될 것을 예상했습니다.
  • 하지만 색을 설정하지 않은 부분은 자동으로 검은색으로 설정이 되어 출력되었습니다.

wrong_printPlayer

  • 그렇기 때문에 기존의 t_img변수를 재활용하는 방향으로 코드를 바꾸어 주었습니다. 기존의 미니맵이 그려져있는t_img map위에 덧칠해주는 개념입니다.
int main(void)
{
    /* 코드생략 */
    render_player(&mlx, &map);
    /* 코드생략 */
}

right_printPlayer

  • 정상적으로 지도player가 출력되었습니다.


2️⃣ key입력을 받기

  • 아직 minilibx사용이 서툽니다. 그렇기 때문에 함수를(막?) 사용하면서 터득해야할 것 같습니다. (정보도 많이없고 쉽게 설명해주는 곳이 적다..)
  • 일단 지도에 그린player함수를 움직이게 하기 위해서는 키보드의 키값을 입력받아야 합니다.
  • 다행히 minilibx키값을 받을 수 있는 함수가 있습니다.
  • 대표적으로 mlx_hookmlx_key_hook가 있는데 mlx_hook함수가 키버튼을 꾹눌렀을때 연속동작을 인식해주기 때문에 사용하기로 했습니다.

(1) mlx_hook함수

  • render_platyer함수에서 사용하였습니다.
void render_player(t_mlx *mlx, t_img *map)
{
	t_player    player;

    /* 코드 생략 */
	mlx_hook(mlx->win, X_EVENT_KEY_PRESS, 1L<<0, &key_press, &player);
    /* 코드 생략 */
}
  • mlx_hook함수의 첫번째 인자win포인터를 넘겨줍니다.
  • 두번째 인자로는 mlx프로그램의 고유의 이벤트 코드를 넣어주어야합니다. 대충 아래와 같이 코드가 있으며 키입력 이벤트사용을 위해 (int)2의 값을 넣어 주었습니다.
/* event list */
02: KeyPress
03: KeyRelease
04: ButtonPress
05: ButtonRelease
06: MotionNotify
07: EnterNotify
08: LeaveNotify
09: FocusIn
...
  • 세번째 인자이벤트에 맞는 Mask값을 넣어주는데 아마 비트연산을 이용하여 1L(long 1)의 값을 이동하는식으로 구분하도록 하였는데 내부적으로 어떤식으로 구현되어 있는지 알 수 없지만 Mask라고 쓰인 것으로 보아 입력받은key값과 합쳐져서 저장되는 것이 아닌가 하고 생각이 듭니다.
/* mask list */
NoEventMask 0L
KeyPressMask (1L<<0)
KeyReleaseMask (1L<<1)
ButtonPressMask (1L<<2)
ButtonReleaseMask (1L<<3)
EnterWindowMask (1L<<4)
LeaveWindowMask (1L<<5)
PointerMotionMask (1L<<6)
PointerMotionHintMask (1L<<7)
Button1MotionMask (1L<<8)
Button2MotionMask (1L<<9)
Button3MotionMask (1L<<10)
Button4MotionMask (1L<<11)
Button5MotionMask (1L<<12)
ButtonMotionMask (1L<<13)
KeymapStateMask (1L<<14)
ExposureMask (1L<<15)
VisibilityChangeMask (1L<<16)
StructureNotifyMask (1L<<17)
ResizeRedirectMask (1L<<18)
SubstructureNotifyMask (1L<<19)
SubstructureRedirectMask (1L<<20)
FocusChangeMask (1L<<21)
PropertyChangeMask (1L<<22)
ColormapChangeMask (1L<<23)
OwnerGrabButtonMask (1L<<24)
  • 이어서 네번째인자key값에 따라 동작하는 함수의 포인터를 넣어줍니다.
  • 다섯번째인자네번째인자로 넣어준 함수멤버변수(param)을 넣어줍니다. (int keycode는 기본 멤버변수로 제외한다. 그외 멤버변수가 두개 이상일시 구조체형태로 묶어줘서 전달해줘야할 듯합니다.)

(2) key_press함수 구현

# define KEY_ESC    53
# define KEY_W      13
# define KEY_A      0
# define KEY_S      1
# define KEY_D      2
  • 키보드 단축키의 고유 키값을 매크로함수로 정의해주었습니다.
int        key_press(int keycode, t_player *player)
{
    if (keycode == KEY_A)
        player->x--;
    if (keycode == KEY_D)
        player->x++;
    if (keycode == KEY_W)
        player->y--;
    if (keycode == KEY_S)
        player->y++;
    printf("x:y = %d:%d\n", player->x, player->y);
    if (keycode == KEY_ESC)
        exit(0);
    
    return (0);
}
  • 아직 어떤식으로 mlx_hook함수가 동작하는지 모릅니다. 그렇기 때문에 player구조체를 멤버변수로 받아 입력된 key값에 따라 player의 x, y 좌표가 변경되도록 구현하였습니다.

(3) 제대로 동작하는지 확인하기

  • key_press함수 내부에 printf("x:y = %d:%d\n", player->x, player->y);를 임시로 적어주어 정상적으로 입력되었는지 확인해봤습니다.
/* 실시간 출력값 */
x:y = 1:0
x:y = 1:1
x:y = 2:1
x:y = 2:2
x:y = 3:2
x:y = 3:3
x:y = 4:3
x:y = 5:3
x:y = 6:3
x:y = 7:3
x:y = 7:4
x:y = 7:5
x:y = 7:6
x:y = 7:7
x:y = 7:8
  • 분명 아래와 같이 초기화 해주었는데 key_press함수내부의 playerx, y값의 좌표가 (0, 0)부터 시작되었습니다.
player->x = WINDOW_WIDTH / 2;
player->y = WINDOW_HEIGHT / 2;
  • 그리고 화면상의 player의 위치변화도 변화지 않았습니다. 단순히 터미널상에서만 좌표가 변할 뿐이였습니다.
  • 아마도 "입력받은 키값을 화면에 출력하도록 하기위한 방법"을 좀 더 연구해봐야할 것같습니다.




© 2021.02. by kirim

Powered by kkrim