[so_long] 5. 렌더링

aqualung·2023년 1월 27일

🖥 so_long

1. 맵 그리기

mlx_loop_hook(game.mlx_ptr, &main_loop, &game);

mlx_loop_hook를 이용해 계속해서 window에 렌더링 해줄 함수를 호출합니다.

제 경우엔 main_loop함수가 그 역할을 합니다.

main_loop안에서 , 스프라이트를 각각 렌더링해줍니다.

우선 을 먼저 그리고 그 위에 움직이는 스프라이트를 덧씌워 그려주는 방식입니다.

을 그리는 함수입니다.

void	draw_map(t_game *game)
{
	int	i;
	int	j;

	i = -1;
	while (++i < game->map.height)
	{
		j = -1;
		while (++j < game->map.width)
        {
        	if (game->map.pos[i][j] == '1')
				put_img(game, game->tile[WALL].ptr, j * 64, i * 64);
			else if (game->map.pos[i][j] == 'E')
            {
            	if (game->coin_num >= game->map.c_cnt)
            		put_img(game, game->tile[E_UNLOCK].ptr, j * 64, i * 64);
            	else
            		put_img(game, game->tile[E_LOCK].ptr, j * 64, i * 64);
            }
			else
				put_img(game, game->tile[FLOOR].ptr, j * 64, i * 64);
        }
	}
}

반복문으로 각 좌표를 돌며 그 위치에 맞는 타일이미지를 위치시킵니다.

2. 스프라이트 그리기

1) 부드러운 이동

플레이어나 적이 움직일때 애니메이션이 없다면 맵의 각 타일처럼 알맞은 위치에 그려주기만 하면 됩니다.

이렇게 하면 좌표(x0)에서 좌표(x)로 움직일때 64픽셀만큼 순간이동하는 것처럼 표현될 것입니다.

그래서 좌표를 옮길 때, 즉 스프라이트가 움직일 때 부드럽게 좌표를 옮기도록 각 픽셀단위로 스프라이트를 그려주었습니다.

Screen-Recording-2023-01-27-at-4 36 14-PM

void	draw_sprite_walk(t_game *game, t_spr *sprite, \
		t_img *spr_img, int dir)
{
	int	x;
	int	y;

	sprite->key_block = TRUE;
	x = (sprite->x0 * 64) + (game->vector[dir].x * sprite->i * sprite->move);
	y = ((sprite->y0 * 64) - sprite->offset) + (game->vector[dir].y \
			* sprite->i * sprite->move);
	put_img(game, spr_img->ptr, x, y);
	sprite->i += 4; // i는 프레임마다 증가됨.
	if (sprite->i == 44 && sprite->move == TRUE)
    // 44픽셀만큼 움직였을때 좌표를 업데이트해줍니다.
	{
		sprite->x += game->vector[dir].x;
		sprite->y += game->vector[dir].y;
	}
	if (sprite->i >= 64)
    // 다음 타일로 완전히 넘어갔을때 
	{
		sprite->i = 0;
		sprite->key_block = FALSE;
		sprite->walk = FALSE;
	}
}

이 게임의 렌더링함수들은 무한루프를 돌면 계속해서 호출되고 있습니다.

루프를 한번 돌때를 한 프레임이라고 한다면 sprite->i는 매 프레임마다 4씩 증가하게 됩니다.

1 frame(i == 4)
2 frame(i == 8)
3 frame(i == 12)
...
16 frame(i == 64)

그렇다면 프레임마다 스프라이트의 이미지를 i픽셀만큼 옮겨서 그린다면 부드러운 애니메이션으로 표현되지 않을까요?

x = (sprite->x0 * 64) + (game->vector[dir].x * sprite->i * sprite->move);

put_img(game, spr_img->ptr, x, y);

위 코드는 다음과 같은 의미를 가집니다.

x좌표 = (현재좌표 x0) + (진행방향 * i픽셀 * (move == true))

i픽셀만큼 더해준 x, y좌표에 이미지를 그려줌

i == 64일때는 64픽셀만큼 움직인 것입니다.

즉, 스프라이트가 다음 타일로 완전히 넘어간 것이기 때문에 i를 0으로 돌려주고 움직임을 멈춥니다.

i가 44일때 좌표를 업데이트해준 것은 다른 스프라이트들과 충돌할때 자연스러운 위치에서 이벤트가 발생하도록 하기 위함입니다. 그러므로 꼭 44일 필요는 없습니다. 타일과 타일 사이의 중간 어디쯤이면 상관없습니다.

2) 애니메이션

스프라이트가 타일에서 타일을 넘어갈때 부드럽게 넘어가도록 처리해주었습니다.

하지만 매번 같은 이미지라면 재미없겠죠.

루프를 한번 돌때를 프레임이라고 하기로 했습니다.

프레임마다 이미지도 바꿔주면 어떨까요?

int	main_loop(t_game *game)
{
	draw_map(game);
	draw_coin(game);
	draw_sprite(game, &(game->player));
	event_checker(game);
	game->frame++;
	if (game->frame >= 16)
		game->frame = 0;
	return (0);
}

위는 게임이 진행되는 동안 매번 호출되는 무한루프입니다.

draw_map, draw_sprite등으로 매 프레임마다 윈도우에 렌더링하는 역할을 합니다.

event_checker같은 함수로 플레이어와 적의 충돌등을 체크할 수도 있겠죠.

무한루프에서 프레임(game->frame)을 카운트해줍니다.

루프를 한번 돌때마다 1씩 증가하여 16이 되면 다시 0으로 돌아옵니다.

Screen Shot 2023-01-27 at 1 07 08 PM

저는 프레임이 4의 배수일때마다 다른 이미지를 그리도록 해주었습니다.

t_img	*img_sprite_run(t_game *game, t_spr *sprite)
{
	t_img	*spr;

	if (game->frame < 4)
		spr = &(sprite->run[0]);
        // 첫번째 이미지
	else if (game->frame < 8) 
		spr = &(sprite->run[1]);
        // 두번째 이미지
	else if (game->frame < 12)
		spr = &(sprite->run[2]);
        // 세번째 이미지
	else if (game->frame < 16)
		spr = &(sprite->run[3]);
        // 네번째 이미지
        
    return (spr); // 해당하는 이미지를 리턴
}

0개의 댓글