본문 바로가기

42 Seoul

so_long

so long and thanks for all the fish

안녕, 그리고 고마워요 모든 물고기들

 

.ber 형식의 맵파일을 받아서 돌고래가 물고기를 잡아먹고 맵을 탈출하는 게임이다.

1. ber파일 파싱해서 맵이 정상적인지 확인.

2. 그래픽 화면 켜지면서 게임시작

 

 

사용할 수 있는 함수

#include <unistd.h>

close, read, write

#include <fcntl.h>

open

#include <stdlib.h>

malloc, free, exit

#include <stdio.h>

perror

#include <string.h>

strerror

다운받을 수 있는 minilibx 라이브러리 함수 전부

 

 

맵의 조건

  • 지도는 단 5개의 가능한 문자열로만 구성되어야 합니다: 0은 빈 공간, 1은 벽, C는 수집품, E는 맵의 출구, P는 주인공의 시작지점입니다.
  • 지도는 벽으로 둘러쌓여 있어야 합니다. 그렇지 않으면 에러를 반환해야 합니다.
  • 지도는 최소한 하나의 출구, 하나의 물고기 (수집품), 하나의 시작 지점을 포함해야 합니다.
  • 지도에서 출구로 가는 길의 유무 판단 (검증)은 굳이 해주지 않으셔도 됩니다.
  • 지도는 반드시 직사각형 모양이어야 합니다.
  • 이하의 규칙을 준수한 모든 종류의 지도를 파싱할 수 있어야 합니다.
  • 지도 파일에서 어떠한 허점이 발견된다면, 프로그램은 "Error\n" 과 여러분이 직접 정한 에러 메시지를 출력한 후 제대로 종료되어야 합니다

 

게임의 조건

  • 플레이어의 목표는 모든 수집품을 모으고 최소한의 움직임으로 맵을 탈출하는 것입니다
  • 각 움직임마다 현재까지 움직인 횟수가 쉘에 출력되어야 합니다.
  • 플레이어는 다음 방향으로 움직여야 합니다: 상하좌우
  • 게임은 2D 시점으로 제작하여야 합니다. (탑뷰 또는 프로필)
  • 실시간으로 진행되는 게임일 필요는 없습니다.
  • 플레이어는 벽을 뚫고 지나갈 수 없습니다.
  • W, A, S, D 키를 이용하여 주인공을 조작합니다.
  • ESC는 창을 닫고 게임을 정상적으로 종료합니다.
  • 창 좌상단의 빨간 버튼 (mac) 또는 우상단의 빨간 X (windows) 를 누르면 창을 닫고 게임을 정상적으로 종료합니다.
  • minilibX의 images를 사용하는 것을 강력히 추천합니다!

 

 

 

이미지 준비

위에서부터 이미지 주워오기 -> 자르기 -> xpm변환 이다.

https://itch.io/game-assets/free/tag-sprites

https://ezgif.com/sprite-cutter

https://anyconv.com/ko/png-to-xpm-byeonhwangi/

 

파일명 자동변경 스크립트 v0.1

#!/bin/bash

if [ $# -eq 1 ]; then
	res=`ls -a *$1* | grep -v $0`
	echo "$res"
	echo -n "do you want to continue?(y/n): "
	read flag
	if [ "$flag" == "y" ]; then
		pad="000"
		i=0
		for x in $res;do
			mv $x "${pad:${#i}}$i.xpm"
			i=$(($i + 1))
		done
	fi
else
	echo "usage: $0 <searchname> <numlen>"
fi

 

png 배경지우기

https://www.remove.bg/ko

 

xpm 배경지우기

xpm파일에서 배경 부분을 none으로 설정

#!/bin/bash

if [ $# -eq 3 ]; then
	res=`ls -a *$1* | grep -v $0`
	echo "$res"
	echo -n "do you want to continue?(y/n): "
	read flag
	if [ "$flag" == "y" ]; then
		for x in $res;do
			cat $x | grep $2
			sed -i '.bak' "s/$2/$3/g" $x
			echo "=== $x ==="
			cat $x | grep $3
		done
	fi
else
	echo "usage: $0 <searchname> <bef_color> <aft_color>"
fi

맵체크 함수

맵을 개행 기준으로 2차원 배열에 저장해서 사용한다.

전체 파일내용 읽어오는 함수 + 개행 기준으로 스플릿 해서 2차원배열에 저장

기존 스플릿을 수정해서 연속적으로 구분자가 있는경우에도 한줄로 인정해주는 다운그레이드 스플릿을 만들어서 사용한다.

 

2차원 배열의 마지막 인덱스를 알아오는 함수필요

* 게임 전체적인 상황을 관리하는 구조체를 이용해서 관리해도 좋을듯?

 

1. 첫번째줄의 길이를 저장하고 모든 줄에서 길이가 같은지 확인. 또한 저장한 첫번째줄 길이가 최소 3이상은 되어야한다. 

2-1. 첫번째줄은 전부 다 벽

2-2. 맨마지막줄의 인덱스를 구한다음 두번째줄부터 맨마지막줄 전까진 맨앞 맨끝(길이 -1 인덱스)가 벽인지 확인

2-3. 맨마지막불은 전부다 벽

3. 전부 접근하면서 플레이어, 탈출구, 수집품의 포지션을 저장

4. 플레이어, 탈출구, 수집품이 모두 포함되어 있는지 확인

 

 

맵 렌더링 함수

라이브러리에서 함

0은 빈 공간, 1은 벽, C는 수집품, E는 맵의 출구, P는 주인공의 시작지점

렌더링을 함

 

 

게임

방향키 입력을 받고 플레이어의 위치를 움직인다.

하지만 벽이있다면 못지나가게 처리해야한다.

게임 만들기는 쉬울듯? 

키입력에 따라 캐릭터 위치와 게임 상태를 계속 업데이트하고 계속 렌더링하면 될것이다.

 

 

맵체크, 렌더링, 키입력 이정도가 필요한 지식인듯? 

 


mlx 라이브러리

https://harm-smits.github.io/42docs/libs/minilibx/introduction.html

 

minilibx는 Appkit, X11이 필요하기 때문에 컴파일 시에도 프레임워크를 포함시켜야한다.

 

컴파일

-L : 라이브러리 경로 지정

-l (소문자) : 라이브러리 이름 지정. libmlx.a -> mlx

-framework : 지정한 프레임워크를 /System/Library/Frameworks 경로에서 찾은 뒤 헤더를 포함시키고 공유라이브러리를 연결

gcc *.c -I. -Lmlx_opengl -lmlx -framework OpenGL -framework Appkit

 

m1에서 사용할땐 mlx폴더 안에서 mlx를 arch -x86_64 로 컴파일 후 main.c를 컴파일하면 된다.

cd mlx_2019
arch -x86_64 make  # mlx라이브러리를 x86_64로 컴파일 
cd ..
gcc -Wall -Wextra -Werror -framework OpenGL -framekwork AppKit -Imlx_2019 -Lmlx_2019 -lmlx test.c

m1 에서는 arch 명령으로 아키텍쳐를 x86-64 변경 후 컴파일하면 된다.

mlx를 빌드할 때 OpenGL deprecation 관련 warning이 생길 수 있는데, 그냥 OpenGL 업글버전인 Metal을 이용하면된다.

 

 

mms 버전

mlx의 beta버전으로, get_screen_size() 함수를 사용할 수 있다. 

beta버전은 .a(정적라이브러리)가 아닌 .dylib(동적라이브러리)로 컴파일 되기 때문에, 프로젝트 컴파일 시 미리 같은 경로에 라이브러리를 컴파일하여 libmlx.dylib파일을 위치시킨 후 컴파일 하거나, Makefile에서 install_name_tool 명령으로 동적라이브러리 경로를 지정해 줄 수 있다.

 

사용되는 함수

일단 mlx 라이브러리 인스턴스를 생성하고, 그 포인터에 원하는 작업을 미리 세팅해둔 뒤 mlx_loop 함수를 통해 세팅한대로 동작하는 그래픽 창을 띄우는 방식이다. 

 

void *mlx_init();

- mlx 라이브러리 인스턴스 초기화 함수.

- 반환되는 포인터값을 다른 함수들의 인자로 사용하기 때문에 가장 먼저 실행되어야 하고,

성공 시 라이브러리 인스턴스 포인터 반환, 실패시 NULL 반환

 

기본

void *mlx_new_window(void *mlx_ptr, int size_x, int size_y, char *title);

- x, y 크기의 창 인스턴스를 생성해주는 함수. 창의 제목도 지정할 수 있다.

- 이 함수가 실행된다고 창이 뜨는게 아니라 창을 띄울것이라고 mlx에 세팅해주는 함수이다.

 

int mlx_clear_window(void *mlx_ptr, void *win_ptr);

- 창에 그려둔 이미지를 전부 삭제하는 함수

 

int mlx_destroy_window(void *mlx_ptr, void *win_ptr);

- 창 인스턴스를 삭제하는 함수

 

 

스트링

int mlx_string_put(void *mlx_ptr, void *win_ptr, int x, int y, int color, char *string);

- x, y 좌표에 스트링을 집어넣는 함수

 

 

이미지

void *mlx_new_image(void *mlx_ptr,int width,int height);

- 이미지 인스턴스를 만드는 함수. width, height 크기만큼 만든다. 

 

char *mlx_get_data_addr(void *img_ptr, int *bits_per_pixel, int *size_line, int *endian);

- 이미지의 포인터를 가져오는 함수.

- 이미지의 size_line, bits_per_pixel, endian도 세팅해준다.

- endian은 그래픽 작업시 컬러 표시의 endian을 의미하는것 같고, size_line은 한줄의 바이트 사이즈, bits_per_pixel은 픽셀당 비트 수를 의미한다. 

 

* 이미지 중간의 한 지점에 색을 집어넣는 방법

mlx의 이미지를 생성하게되면, 표현할 이미지의 크기만큼 메모리에 할당되어 올라가고 각 바이트에는 색을 표현하는 값이 연속적으로 저장된다.

픽셀의 주소를 찾은 뒤 메모리에 직접 색상 값을 세팅하면 mlx 이미지가 렌더링될때 해당 색으로 표시해준다.

1byte당 1의 주소를 가지고있고, 픽셀당 bits_per_pixel 크기의 비트로 이뤄져있다.

만약 bits_per_pixel이 16인 경우, 65536 가지의 색을 지정할 수 있고, 픽셀당 2byte이기 때문에 인접한 픽셀의 주소도 2씩 차이난다.

typedef struct	s_data {
	void	*img;
	char	*addr;
	int		bits_per_pixel;
	int		line_length;
	int		endian;
}				t_data;

void	my_mlx_pixel_put(t_data *data, int x, int y, int color)
{
	char	*dst;

	// 3. 현재 메모리상 이미지 주소에
	dst = data->addr + (y * data->line_length + x * (data->bits_per_pixel / 8));
	*(unsigned int*)dst = color;
}

// 1. 1920, 1080 픽셀 크기의 이미지를 생성한다. 메모리에 올라간 크기는 bits_per_pixel/8 * 1920 * 1080
img.img = mlx_new_image(mlx, 1920, 1080);

// 2. 이미지 인스턴스의 메모리상 주소를 가져온다. 
img.addr = mlx_get_data_addr(img.img, &img.bits_per_pixel, &img.line_length, &img.endian);
my_mlx_pixel_put(&img, 5, 5, 0x00FF0000);

 

int mlx_put_image_to_window(void *mlx_ptr, void *win_ptr, void *img_ptr, int x, int y);

- 만들어둔 이미지 인스턴스를 윈도우에 세팅하는 함수

 

int mlx_destroy_image(void *mlx_ptr, void *img_ptr);

- 이미지 인스턴스 디스트로이 함수

 

 

이미지 변환

void *mlx_xpm_to_image(void *mlx_ptr, char **xpm_data, int *width, int *height);

- xpm 데이터를 이미지 인스턴스로 변환한다. 이미지 인스턴스 주소를 반환.

- 이미지의 가로, 세로길이도 세팅된다.

 

void *mlx_xpm_file_to_image(void *mlx_ptr, char *filename, int *width, int *height);

- xpm 이미지파일을 이미지 인스턴스로 변환한다. 이미지 경로를 두번째인자로 지정

 

void *mlx_png_file_to_image(void *mlx_ptr, char *filename, int *width, int *height);

- png 파일을 이미지 인스턴스로 변환하는데, 이 함수는 최근에 생긴듯? 

 

 

int mlx_mouse_hook(void *win_ptr, int (*f)(), void *param);

- 해당 윈도우에 마우스 이벤트가 발생하는 경우 함수를 실행한다.

- 정확히 말하면 마우스 이벤트에 인자로 전달된 함수를 후킹해두는 함수이다.

 

int mlx_key_hook(void *win_ptr, int (*f)(), void *param);

- 키보드 이벤트가 발생하는 경우

- 훅 함수 원형에는 첫번째 인자로 keycode가 전달되어야 한다. ex) int close(int keycode, t_vars *vars)

 

int mlx_loop_hook(void *mlx_ptr, int (*f)(), void *param);

- 루프에 후킹한다. 

 

int mlx_loop(void *mlx_ptr);

- mlx 포인터를 반복호출해서 동작하게 한다. 

 

 

나머지 함수는 당장 사용되지 않기떄문에 직접 확인해라

https://harm-smits.github.io/42docs/libs/minilibx/prototypes.html#mouse-functions

 


* 렌더링

그래픽 그릴때 클리어 윈도우 웬만하면 사용하지 않는것이 좋다.

클리어 윈도우 없이 배경 이미지를 덮어씌우고 캐릭터를 새로 덮어씌우면 된다.

그렇게하면 쉽게 그려질듯? 생각보다 리소스도 많이 잡아먹지않는다. 

 

* 동시키입력

key_hook에서 전달받는 키코드는 하나밖에 없기 때문에 동시키 입력을 구현하려면 

key_press 일때 입력된 키 플래그 세팅, key_release일때 플래그 제거

mlx_loop_hook함수로 키 플래그 세팅을 확인하고 그에 맞는 행동처리

플래그가 세팅되어있는 동안만 행동하고, 릴리즈되어 플래그가 제거되면 행동이 정지된다.

 

* 콜렉션먹기


구조체

1. 맵구조체 -> 맵 파싱해서 체크하고 게임이 실행되면 플레이어위치를 일반땅으로 변경, 게임진행중에 콜렉트 먹히면 일반땅으로 업데이트. 적이 죽으면 일반땅으로 업데이트.

2. 플레이어 구조체(픽셀단위 x, y위치, collect 개수)

 

 

'42 Seoul' 카테고리의 다른 글

exam rank 03  (0) 2022.02.17
born2beroot 와 가상머신과 리눅스 2 실습  (0) 2021.10.25
푸시스왑  (0) 2021.09.08
미니톡 - signal.h  (0) 2021.08.21
정리 안하려했는데 정리하는 exam02  (0) 2021.07.05