2서클 나의 마지막 과제 so_long
mlx 라이브러리를 사용해서 플레이어가 Collect를 다 모으고 출구로 나가는 2D 게임을 구현하는 과제이다.
개인적으로 되게 재밌게 구현했다.
mlx란 42 자체에서 openGL을 사용해서 교육용으로 만든 그래픽 라이브러리이다.
서브젝트 내에 첨부되어 있는 mlx 라이브러리를 다운로드 받아서 컴파일 단계에서 -L./mlx -lmlx -framework OpenGL -framework AppKit
와 같이 불러와 사용할 수 있다.
먼저 so_long은 textures와 maps를 허용하기 때문에 디렉토리를 먼저 생성하고, textures에는 png 파일을 본인이 사용할 픽셀 단위로 조절해서 xpm 파일로 만들어 저장해두고, maps에는 다양한 테스트를 해볼 .ber
확장자의 맵 파일을 저장해두면 된다.
42Docs mlx를 보면 mlx 내에 선언된 기능을 파악할 수 있다.
먼저 mlx_init()
함수를 통해 mlx 객체를 void *
형태로 선언해준다.
새로운 윈도우를 사용하기 위해 win 객체를 void *
로 받는 mlx_new_window()
함수를 통해 설정해준다.
자세한 프로토타입은 위 문서에 잘 나와있다.
게임을 하기 위해선 key event 발생 시 호출할 함수를 mlx_hook
함수를 통해 걸어준다.
이 때, 각 key event에 대한 상수가 따로 설정이 되어 있기 때문에 예를 들어 KEY_RELEASE=3
과 같이 사용할 이벤트에 대해 훅을 걸어주면 된다.
textures 디렉토리에 넣어둔 사용할 이미지 파일 (.xpm)을 mlx image로 사용하기 위해 mlx_xpm_file_to_image
로 변수에 할당한다.
이미지 변수를 mlx_put_image_to_window
함수를 통해 윈도우에 이미지를 렌더링한다.
so_long을 구현하며 가장 까다로운 부분이 map parsing 부분이다.
여러가지 유효성 검사를 해야하는데 기억나는 순서대로 작성해본다.
.ber
확장자를 사용하는 파일만 가능하다.10EPC
만을 포함할 수 있으며 그 외의 글자는 허용되지 않는다. 맵은 1(벽)으로 감싸져있어야 하며 직사각형이어야 한다. 또, C(수집품)를 다 먹을 수 있으며, E(출구)에 도달할 수 있어야 한다. 여기가 참 힘든데 맵이 비었을 때, 맵이 개행으로 끝났을 때, E(출구)가 2개 이상, P(플레이어 시작 위치)가 2개 이상일 때 모두 invalid로 처리해야한다.
우선, GNL을 통해 한 줄씩 map을 읽어온다.
나는 1차원 배열을 통해 문제를 해결했기 때문에 첫 줄을 width
의 기준으로 잡고 개행을 제거하며 width
를 비교하며 heigth
를 증가시키며 ft_strjoin을 통해 문자열을 붙인다.
이후 ft_strlen
을 통해 마지막 글자가 \n
인지 확인하고 있다면 invalid 처리한다.
맵이 문자열로 완성되었다면 모든 글자에 10EPC
를 제외한 글자가 있는지 확인한 후 첫 줄과 마지막 줄, 제외한 각 라인의 처음과 마지막 인덱스가 벽인지 확인한다.
E와 P와 C의 갯수를 각각 체크해서 C가 1이상이고 E, P가 유일하게 존재하는지 확인한다.
마지막으로 DFS 혹은 BFS를 통해 C를 다 먹을 수 있는 map인지, E에 도달할 수 있는 map인지 확인해야한다.
나는 DFS를 통한 재귀함수로 맵의 유효성을 확인했지만, 원형 큐를 이용한 BFS가 구현면에서 (특히 42에서) 어렵지만 성능면에서 맵의 유효성을 확인하기 적합한 알고리즘이기 때문에 선택에 따라 갈릴 것 같다.
맵이 유효하다면 이제 mlx_put_image_to_window
함수를 통해 초기 맵을 렌더링하고, mlx_loop
함수를 통해 이벤트를 대기한다.
이제 mlx_hook
을 통해 걸어놨던 KEY_RELEASE
이벤트가 발생했다면 key_event를 통해 WASD
키인지 체크해서 각각의 키가 눌렸을 때 갈 방향에 P
를 옮기고 다시 그림을 렌더링한다.