열렬히.뛰기

3. 회전변환 & 이동

école 42 > cub3D > 진행상황 > 3. 회전변환 & 이동

첫 번째 방식

ESC 키

c
mlx_hook(tools->win_ptr, X_EVENT_KEY_EXIT, 0, &finish_cub3d, tools);

mlx를 끝낼 수 있는 finish_cub3D라는 함수를 만들었다.

finish_cub3D애서 할당을 해제해주고, exit을 해준다.

회전변환

방향키의 왼쪽, 오른쪽을 눌렀을 때 실행되게 해야 한다.

따라서 우선적으로 회전행렬을 이용해 방향벡터와 카메라벡터를 돌려준다.

처음에 생각했던 건 다음과 같다.

(회전각이 작아질수록 움직임이 더 부드러워진다)

python
def matrix(vec) :
	newx = cos(alpha) * vec.x - sin(alpha) * vec.y
	newy = sin(alpha) * vec.x + cos(alpha) * vec.y
	vec.x = newx
	vec.y = newy
	return vec

def rotation(tools, keycode):
	if keycode == LEFT_TURN:
		tools.alpha += -(PI / 36) # += 말고 그냥 =으로 해야
	else if keycode == RIGHT_TURN:
		tools.alpha += (PI / 36) # += 말고 그냥 =으로 해야
	matrix(tools.dir, tools.alpha)
	matrix(tools.dir, tools.alpha)

이 경우 회전이 부정확해 보인다.

문제 : 회전각이 점점 더해지고 더해지는 것.

  • 내가 움직이는 것이 아닌 축을 기준으로 잡아야
  • 따라서 매번 회전각을 더해주는 것이 절대 아니다
  • 즉, 한번 회전 후 0으로 초기화된다고 생각하자.

이동

처음에 생각했던 이동은 다음과 같다.

  • 상, 하 : pos의 x를 변화시킨다.
  • 좌, 우 : pos의 y를 변화시킨다.

이러면 고개를 돌렸는데 해당 방향 기준으로 상하좌우 이동이 안된다.

따라서 방향벡터를 이용해야 한다. (각도를 이용하는 방법도 있지만…여기선 생략)

c
// 앞으로 갈 때
new_x = pos->x + dir->x * VELOCITY;
new_y = pos->y + dir->y * VELOCITY;

// 뒤로 갈 때
new_x = pos->x - dir->x * VELOCITY;
new_y = pos->y - dir->y * VELOCITY;

// 왼쪽으로 갈 때
new_x = tools->pos.y - tools->dir.x * VELOCITY;
new_y = tools->pos.x + tools->dir.y * VELOCITY;

// 오른쪽으로 갈 때
new_x = tools->pos.y + tools->dir.x * VELOCITY;
new_y = tools->pos.x - tools->dir.y * VELOCITY;

렌더링

그림을 그리는 과정에서 깜박임을 해결하기 위한 부분.

python
while(1):
	if keyhook == ARROW_KEYS
		action()	
	if keyhook == ESC:
		destroy_window()
		finish()
	drawing()

mlx에서 while의 역할을 해주는 것 : mlx_hook()

while문 안에 코드 : mlx_loop_hook()

두 번째 방식

이렇게 하니 너무 끊김이 심해서 바꾼 방식

  1. 키를 press하고, 키와 관련된 변수를 바꾼다. (ft_key_press)

  2. 키를 release하고, 키와 관련된 변수를 바꾼다. (ft_key_release)

  3. mlx_loop_hook() 안에 이동 관련 함수(ft_event)를 만든다.

    이동 관련 함수에는 키와 관련된 변수를 통해 이동을 조정한다.

c
int main()
{
		mlx_hook(tools->win_ptr, X_EVENT_KEY_PRESS, 1L << 1, &ft_key_press, tools);
		mlx_hook(tools->win_ptr, X_EVENT_KEY_RELEASE, 1L << 0, &ft_key_release, tools);
		mlx_hook(tools->win_ptr, X_EVENT_KEY_EXIT, 0, &finish_cub3d, tools);
		mlx_loop_hook(tools->mlx_ptr, &ft_event, tools);
		mlx_loop(tools->mlx_ptr);
}

키를 press, release하는 함수는 다음과 같다.

이를 mlx_hook()과 연결시켜 놓는다.

c
int	ft_key_press(int keycode, t_box *tools)
{
	if (keycode == 53)
	{
		mlx_destroy_window(tools->mlx_ptr, tools->win_ptr);
		finish_cub3d(tools);
	}
	if (keycode == LEFT_TURN)
		tools->left_turn = 1;
	else if (keycode == RIGHT_TURN)
		tools->right_turn = 1;
	if (keycode == FORWARD_MOVE)
		tools->forward_move = 1;
	else if (keycode == BACKWARD_MOVE)
		tools->backward_move = 1;
	if (keycode == LEFT_MOVE)
		tools->left_move = 1;
	else if (keycode == RIGHT_MOVE)
		tools->right_move = 1;
	return (0);
}

int	ft_key_release(int keycode, t_box *tools)
{
	if (keycode == LEFT_TURN)
		tools->left_turn = 0;
	else if (keycode == RIGHT_TURN)
		tools->right_turn = 0;
	if (keycode == FORWARD_MOVE)
		tools->forward_move = 0;
	else if (keycode == BACKWARD_MOVE)
		tools->backward_move = 0;
	if (keycode == LEFT_MOVE)
		tools->left_move = 0;
	else if (keycode == RIGHT_MOVE)
		tools->right_move = 0;
	return (0);
}

실제 이동을 담당하는 ft_event()는 다음과 같다.

c
static void	move_pos(t_box *tools, t_vec_f *pos, t_vec_f *new)
{
	if (tools->total_map[(int)pos->y][(int)new->x] == 0)
		pos->x = new->x;
	if (tools->total_map[(int)new->y][(int)pos->x] == 0)
		pos->y = new->y;
}

void	ft_move_key(t_box *tools, t_vec_f *pos, t_vec_f *dir, t_vec_f *new)
{
	if (tools->forward_move == 1)
	{
		new->x = pos->x + dir->x * VELOCITY;
		new->y = pos->y + dir->y * VELOCITY;
	}
	else if (tools->backward_move == 1)
	{
		new->x = pos->x - dir->x * VELOCITY;
		new->y = pos->y - dir->y * VELOCITY;
	}
	else if (tools->left_move == 1)
	{
		new->x = pos->x + dir->y * VELOCITY;
		new->y = pos->y - dir->x * VELOCITY;
	}
	else if (tools->right_move == 1)
	{
		new->x = pos->x - dir->y * VELOCITY;
		new->y = pos->y + dir->x * VELOCITY;
	}
	move_pos(tools, pos, new);
}

int	ft_event(t_box *tools)
{
	t_vec_f	new;

	new.x = 0;
	new.y = 0;
	if (tools->left_turn == 1 || tools->right_turn == 1)
		rotation(tools);
	if (tools->forward_move == 1 || tools->backward_move == 1 || \
			tools->left_move == 1 || tools->right_move == 1)
		ft_move_key(tools, &tools->pos, &tools->dir, &new);
	drawing(tools);
	return (0);
}

단, 이 경우 키를 오래 누르면 매끄럽게 움직이나 키를 가볍게 누르면 프레임이 변화하지 않는다.

따라서 키를 누르는 함수 안에도 ft_event()를 넣어서 루프 안에서 실행하거나 키를 누른 상황에서나 모두 그림이 출력되도록 한다.

키를 누른 상황에서 그림이 출력되면 연산이 중복되는 게 아닌가 싶지만, 오버헤드가 그렇게 크지 않을 것이다.

<수정된 ft_key_press()>

c
int	ft_key_press(int keycode, t_box *tools)
{
	if (keycode == 53)
	{
		mlx_destroy_window(tools->mlx_ptr, tools->win_ptr);
		finish_cub3d(tools);
	}
	if (keycode == LEFT_TURN)
		tools->left_turn = 1;
	else if (keycode == RIGHT_TURN)
		tools->right_turn = 1;
	if (keycode == FORWARD_MOVE)
		tools->forward_move = 1;
	else if (keycode == BACKWARD_MOVE)
		tools->backward_move = 1;
	if (keycode == LEFT_MOVE)
		tools->left_move = 1;
	else if (keycode == RIGHT_MOVE)
		tools->right_move = 1;
	ft_event(tools); // 이 부분을 추가해 가볍게 키를 쳤을때도 렌더링이 되도록 한다.
	return (0);
}