[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)인가만 중요할뿐 두께의 정확한 수치는 중요하지 않기 때문에 넘어가도록 하겠습니다.
(-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
의 그림이 출력될 것을 예상했습니다. - 하지만 색을 설정하지 않은 부분은 자동으로
검은색으로 설정 이 되어 출력되었습니다.
- 그렇기 때문에 기존의
t_img
변수를 재활용하는 방향으로 코드를 바꾸어 주었습니다. 기존의 미니맵이 그려져있는t_img map
위에 덧칠해주는 개념입니다.
int main(void)
{
/* 코드생략 */
render_player(&mlx, &map);
/* 코드생략 */
}
- 정상적으로 지도와 player가 출력되었습니다.
2️⃣ key입력을 받기
- 아직
minilibx 사용이 서툽니다. 그렇기 때문에 함수를(막?) 사용하면서 터득해야할 것 같습니다. (정보도 많이없고 쉽게 설명해주는 곳이 적다..) - 일단 지도에 그린
player 함수를 움직이게 하기 위해서는키보드의 키값 을 입력받아야 합니다. - 다행히
minilibx 에 키값을 받을 수 있는 함수가 있습니다. - 대표적으로
mlx_hook
와mlx_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
의 값을 넣어 주었습니다.
02: KeyPress
03: KeyRelease
04: ButtonPress
05: ButtonRelease
06: MotionNotify
07: EnterNotify
08: LeaveNotify
09: FocusIn
...
- 세번째 인자로 이벤트에 맞는 Mask값을 넣어주는데 아마 비트연산을 이용하여
1L
(long 1)의 값을 이동하는식으로 구분하도록 하였는데 내부적으로 어떤식으로 구현되어 있는지 알 수 없지만 Mask라고 쓰인 것으로 보아 입력받은key값과 합쳐져서 저장되는 것이 아닌가 하고 생각이 듭니다.
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함수
내부의player
의x, y
값의 좌표가(0, 0)
부터 시작되었습니다.
player->x = WINDOW_WIDTH / 2;
player->y = WINDOW_HEIGHT / 2;
- 그리고 화면상의
player
의 위치변화도 변화지 않았습니다. 단순히 터미널상에서만 좌표가 변할 뿐이였습니다. - 아마도
"입력받은 키값을 화면에 출력하도록 하기위한 방법" 을 좀 더 연구해봐야할 것같습니다.