[cub3d](17)마우스 시점전환기능 구현하기
1️⃣ 목표
마우스
로 시점을 바꿀 수 있는 기능을 구현할 계획입니다.- 추가적으로 프로그램을 돌렸을때
leak(메모리누수)
가 발생하지 않는지 테스트도 할 것입니다.
2️⃣ 마우스관련 mlx함수 알아보기
- 마우스 관련 함수는
mlx.h
에 크게 4가지가 정의되어 있습니다.
/* mlx.h */
int mlx_mouse_hide();
int mlx_mouse_show();
int mlx_mouse_move(void *win_ptr, int x, int y);
int mlx_mouse_get_pos(void *win_ptr, int *x, int *y);
mlx_mouse_hide()
: 마우스포인터를 숨겨주는 함수mlx_mouse_show()
: 마우스포인터를 보여주는 함수mlx_mouse_move()
: 마우스위치를win_ptr
기준으로 x, y변수에 저장해주는 함수mlx_mouse_get_pos
: 마우스위치를win_ptr
기준으로 x, y변수 위치로 옮겨주는 함수
3️⃣ 마우스 시점전환기능 구현하기
(1) update_mouse함수
void update_mouse(t_god *god)
{
if (god->key.mouse_on == TRUE)
{
mlx_mouse_hide();
mlx_mouse_get_pos(god->win, &god->mouse.x, &god->mouse.y);
mlx_mouse_move(god->win, god->map.window_width / 2, god->map.window_height / 2);
}
else
mlx_mouse_show();
}
- 숫자키
3, 4
로 god->key.mouse_on변수를키고 끌(TRUE, FALSE) 수 있도록 하였습니다. 켰을 경우
마우스커서를 숨기고 마우스움직임을 얻습니다. 그리고마우스가 창밖으로 튀는 것 을 막기 위해mlx_mouse_move()
함수를 이용하여 한틱마다 윈도우 정중앙에 위치하도록 하였습니다.껏을 경우
마우스커서를 다시 보여줍니다.
(2) 마우스함수 적용하기
/* x좌표 */
if (god->key.mouse_on == TRUE)
god->player.rota_angle += ((god->mouse.x - god->map.window_width / 2) / (100 * PI));
/* y좌표 */
if (god->key.mouse_on == TRUE)
god->player.updown_sight += 2 * (god->mouse.y - god->map.window_height / 2);
- 마우스 포인터가
윈도우 중앙에 고정 시킨 것을 생각하여 윈도우 사이즈의 절반을 빼준 좌표를 더해주도록 했습니다. 나머지 연산들은 단순히 화면전환속도를 조절하고자하는 수치일뿐입니다.
(3) 시점이 튀는 현상 발생
- 기존에
3
번키를 누르면마우스시점 전환모드 를 사용할 수 있도록 구현하였습니다. - 하지만 다음그림과 같이 최초로
3
번키를 누르는 순간"시점이 튀는 현상" 이 발생하였습니다.
< 3번키를 누르기전 >
< 3번키를 누른직후 >
- 우선 원인을 찾기 위해
printf()
함수로3
번키를 누른 순간 마우스 x좌표와 rota_angle(사용자 시점각도)을 출력해 봤습니다.
1.349634 1.944747
0.000000 1.944747
0.000000 1.944747
0.000000 1.944747
0.000000 1.944747
0.000000 1.944747
0.000000 1.944747
0.000000 1.944747
- 위처럼 1, 2틱정도가 0.00이아니라 다른 값으로 튀는 것을 볼 수 있습니다. (아마 마우스포인터를 윈도우 중앙에 고정시켜주는 함수가
후순위
에 배치되서인듯하지만1.349634
로 튀는 원인을 찾을 수 없을 뿐더러앞순위
로 보내면 정상적으로 작동하지 않았습니다.) - 단순한 해결방법으로
3
번키를 누르고 최초5틱
을 지연시키는 방법을 사용했습니다.
< 지연조건을 넣어준 뒤 코드 >
/* x좌표 */
if (god->key.mouse_on == TRUE)
{
god->mouse.delay_x++;
if (god->mouse.delay_x > 5)
god->player.rota_angle += ((god->mouse.x - god->map.window_width / 2) / (100 * PI));
}
/* y좌표 */
if (god->key.mouse_on == TRUE)
{
god->mouse.delay_y++;
if (god->mouse.delay_y > 5)
god->player.updown_sight += 2 * (god->mouse.y - god->map.window_height / 2);
}
- 전역으로 사용할 수 있도록
god->mouse
구조체안에delay변수
를 넣고 틱을 조절하는 방식으로 만들어 줬습니다.
4️⃣ leak(메모리 누수) 검사
- 컴퓨터가 발달해서인지 프로그램을
exit()
함수로 종료 시켜주면 자동으로 메모리 해제를 해줍니다. (그래도 프로그램 사용중에 사용하지않는 메모리는 free해주는 습관이 좋을듯합니다.) - 이번에
leak(메모리 누수) 검사
는 게임실행 중에 생기는 누수를 없애줄 계획입니다. vargrind
라는 프로그램을 이용해도 되지만 현재 m1맥북에는 잘 작동하지(?)않는 다고 합니다. (brew
를 사용해서 다운을 받아봐쓴ㄴ데 결국 포기)
(1) leaks 커맨드 이용하여 검사하기
leaks
커맨드 명령어를 이용하여 메모리 누수를 검사할 수 있습니다.- 터미널을 2개열고
실행 중 에leaks 프로그램명
을 입력해야 합니다.
Process: a.out [xxxx]
Path: xxxx/a.out
Load Address: xxxx
Identifier: a.out
Version: ???
Code Type: X86-64 (translated)
Platform: macOS
Parent Process: zsh [xxxx]
Date/Time: 2021-07-26 01:08:34.341 +0900
Launch Time: 2021-07-26 01:08:14.082 +0900
OS Version: macOS 11.2.3 (20D91)
Report Version: 7
Analysis Tool: /usr/bin/leaks
Physical footprint: 88.4M
Physical footprint (peak): 88.6M
----
leaks Report Version: 4.0
Process 12156: 22932 nodes malloced for 26789 KB
1 (16 bytes) ROOT LEAK: 0x7fa63940dd40 [16]
leaks
커맨드를 이용하면 위와같이 출력을 해줍니다. 저같은경우 1개의 leak이 발생했습니다.- 하지만 정확히
어느곳에서 메모리누수가 발생 했는지 찾기가 쉽지 않습니다.
(2) 원하는 위치에서 누수검사하기
- 다행히도
system()
함수를 사용하면 원하는 곳에서 메모리 누수검사를 할 수 있습니다. - 다음과 같은 코드를 메모리 누수를 검사하고싶은 곳 다음에 작성해주면 됩니다. (이 코드는 42seoul slack방에서 얻은 코드입니다. 팁주신분 감사합니다 (_ _))
- 이런식으로
system()
함수를 사용하면 굳이 터미널을 2개열고 실행할 필요도 없어 집니다.
int main(int argc, char **argv)
{
t_god god;
if (argc != 2)
return (exit_error(&god, ERROR, "WRONG ARGUMENT COUNT!"));
ft_memset(&god, 0, sizeof(t_god));
special_init(&god);
printf("\n@@@@@ 1 @@@@@\n");
system("leaks a.out > result; cat result; rm result");
if (parse_file(&god, argv[1]) == ERROR)
return (exit_error(&god, ERROR, "FAIL PARSING!"));
printf("\n@@@@@ 2 @@@@@\n");
system("leaks a.out > result; cat result; rm result");
if (check_parsing_value(&god) == ERROR)
return (exit_error(&god, ERROR, "WRONG PARSING VALUE SAVED!"));
printf("\n@@@@@ 3 @@@@@\n");
system("leaks a.out > result; cat result; rm result");
ft_init(&god);
printf("\n@@@@@ 4 @@@@@\n");
system("leaks a.out > result; cat result; rm result");
load_texture(&god);
printf("\n@@@@@ 5 @@@@@\n");
system("leaks a.out > result; cat result; rm result");
render_master(&god);
printf("\n@@@@@ 6 @@@@@\n");
system("leaks a.out > result; cat result; rm result");
mlx_loop(god.mlx);
free_texture_array(&god);
return (0);
}
- 약간의 노가다(?) 작업이지만 메모리 누수위치를 찾기가 훨씬 수월해 졌습니다.
(3) 메모리누수 원인
- 저같은 경우 메모리 누수가 다음의 로직에서 발생했습니다.
while ((gnl_ret = get_next_line(fd, &line)) > 0)
{
if (line[0] == '\0')
continue;
else if ((parse_type = check_parse_type(line)) == T_ERROR)
return (exit_error(god, ERROR, "WRONG PARSE TYPE"));
else if (do_parsing(&god->parse, gnl_ret, parse_type, line) == ERROR)
return (ERROR);
}
- while문이 반복될 때마다
get_next_line()
함수는 line변수에 동적할당을한 메모리의 주소를 저장해 줍니다. - line변수는 임시적으로 데이터를 저장하는 변수로
파싱된 이후에는 사용하지 않 도록 코드를 구현했습니다. 그렇기 때문에 line변수는 실행중에 사용하지않는 메모리이기 때문에 leak검사에서 잡히게 됩니다. do_parsing()
함수에서 line변수의 메모리를 해제해줍니다. 하지만 위의 로직에서if (line[0] == '\0')
조건문에 들어갈 경우continue
로 인해do_parsing()
함수를 거치지 않게됩니다. 결국 이 조건문에서는 따로free()
함수를 이용하여 line변수의 메모리를 해제해 주어야 합니다.
< 변경후 반복문 로직 >
while ((gnl_ret = get_next_line(fd, &line)) > 0)
{
if (line[0] == '\0')
free(line);
/* 코드 생략 */
}
leaks Report Version: 4.0
Process 13623: 18369 nodes malloced for 26343 KB