게임 설명
간단한 아스키코드만을 이용한 로그라이크형식의 방탈출 게임을 만들어 봤다..!
c언어만 사용하여 개발하였으며 2인이서 약 3개월정도 걸려서 만들게 되었다.
게임은 위에서 바라보는 흔한 쯔꾸르 형식의 게임의 모습과 유사하며 아스키코드만을 사용하다
보니 많은 표현부분에서 아쉬운 점이 있다.
한번에 클리어하는 것을 목표로 하지 않았고 여러번 시도해보며 직접 랜덤성에 익숙해지며 플레이하길 권장한다..!
게임의 진행 방식은 교수님의 넘쳐 나는 과제를 풀던 학생이 새벽에 학교에 갇히게 되고 막차시간 전까지 탈출하는 것이 목표인 게임이다.
개발 배경
학교 수업 조별 과제로 C언어만 이용해 게임을 만들어야 해서 만들게 되었다.
개발 도구 (라이브러리)
•
ncurses (맥 용)
•
pdcurses (윈도우 용)
조별 과제였고, 팀원 중 1명은 맥, 1명은 윈도우 환경이었기 때문에 모두 고려해 프로그램을 만들어야 했다. Makefile을 보면 맥과 윈도우용 커맨드가 다른 것을 알 수 있다.
개발에 사용한 유용한 함수들(curses.h)
1.
mvaddch(y, x, ch), mvaddstr(y, x, str)
•
콘솔창의 지정된 위치(y, x)에 문자 혹은 문자열을 출력해준다.
2.
kbhit(), getch()
while(1) //사용자로 부터 키 입력받으며 무한루프
{
if ((npc_collision(npc) || npc_collision(secondNpc)) && curLocationFlag == 1){
printMassage("Caught by npc!");
break;
}
if(kbhit()){
ch = getch();
handleInput(ch);
if(ch == 'q') // close
return 1;
if (ch == 'r' || clearFlag == 1)
break;// restart
}
drawEverything();
}
//kbhit
int kbhit(void)
{
int ch = getch();
if (ch != ERR) {
ungetch(ch);
return 1;
} else {
return 0;
}
}
C
복사
•
kbhit() - 입력이 들어오면 true를 리턴 후, 받은 문자를 다시 큐에 삽입한다. (따로 정의해 사용해야 한다)
•
getch() - 큐에 삽입된 문자를 꺼내와 handleInput을 통해 처리한다.
•
kbhit()은 입력이 들어올 때만 실행되기 때문에, 평소에는 계속해서 drawEverything을 호출하다가 사용자가 입력을 하면 입력에 맞는 이벤트가 호출된다. 객체지향적으로 보이게 하는 중요한 부분이다.
3.
init_pair(color instance, character color, background color), COLOR_PAIR(color instance)
•
문자의 색과 배경색을 바꿀 수 있다.
•
무미건조한 아스키코드 속 유일하게 디자인 가능한 부분이다..
4.
clear(), halfdelay(5)
•
clear() - 화면을 깨끗이 지워준다. 이를 이용해 딜레이시간마다 화면을 다시 그리게 했다.
•
halfdelay() - 화면을 업데이트하는 딜레이를 설정할 수 있다.
개발 과정
1.
기본적인 틀 구성
int gameLoop(void)
{
drawEverything();
while(1) //사용자로 부터 키 입력받으며 무한루프한다.
{
if(kbhit()){
ch = getch();
handleInput(ch);
}
drawEverything();
}
return 0;
}
void drawEverything(void)
{
clear();
drawMap();
drawEntity(player);
}
C
복사
•
계속해서 drawEverything을 호출하며 입력에 반응한다. 맵은 정사각형으로 되어 있으며 플레이어는 상하좌우로 이동이 가능하다.
gameloop에서 입력 검사 및 프레임에 따라 실행 주기 결정
2.
맵 구현
void resetMapTiles() // 벽 다시그리기
{
for(int y = 0; y < MAP_HEIGHT; y++)
{
for (int x = 0; x < MAP_WIDTH; x++)
{
map[y][x].ch = '#'; //해당 타일의 문자
map[y][x].walkable = FALSE; //false 즉, 벽 걷기 불가능한.
}
}
}
C
복사
•
이런 방식으로 별찍기문제처럼 단순 노가다로 구현했다. 맵은 1층, 2층 복도, 각 방(같은 구조로 10개 존재)를 만들었다. map.c에서 확인 가능하다. walkable은 사용자가 통과할 수 있는지를 표시해주는 변수이다.
맵이동은 map.c에 존재하는 맵의 형태를 draw.c을 통해 맵을 그린 뒤 이동한다.
3.
상호작용 구현(맵 이동, 아이템 획득, 아이템 사용 등)
void callInteraction(void){
Position degPos[4] = {{-1,0},{0,-1},{0,1},{1,0}}; //검사할 좌표 배열
Position pos = player->pos;
char interch;
for (int i = 0; i < 4; i++){
interch = map[pos.y+degPos[i].y][pos.x+degPos[i].x].ch; //검사할 좌표
callMassageBox(interch);
callMoveMap(interch);
callItem(&map[pos.y+degPos[i].y][pos.x+degPos[i].x]); //검사하고, 해당하는 이벤트 호출
}
}
C
복사
•
p키를 통한 핸들러 입력을 받으면 (캐릭터에 대한 4방향 검사 동서남북)callInteraction이 호출되고 호출된 인터렉션 검사에서 걸러져 각각 메세지 박스, 맵이동 부분, 아이템 추가 및 삭제부분으로 갈라져 call하게 된다.
•
구현된 함수중에 가장 일반화 부분이 잘 구현되어 있고 가독성 측면이 뛰어나다고 생각한다.
상호 작용을 통해 아이템을 획득하여 카드키를 가지고 정문을 탈출하는게 목표이다.
4.
타이머, NPC 이동, 배터리 줄어듦 구현
void printTime(void){
cur_time = time(NULL); //현재 시간을 구해준다.
int ori_time = end_time; //타이머에 표시되는 시간 저장
char timer[10] = "";
end_time = level_time - (long long int)(cur_time - start_time); //타이머 시간 업데이트
if (end_time != ori_time) //초가 넘어갈 시 ex)10초 -> 9초
{
if (batteryFlag == 10){ //10초마다 배터리가 줄어듦
updateBattery();
batteryFlag = 0;
}
npc_move(npc);
npc_move(secondNpc); //1초마다 npc가 움직임
batteryFlag++;
}
//타이머 계산 후 출력
int min = end_time / 60;
int sec = end_time % 60;
strcat(timer,ft_itoa(min));
strcat(timer," : ");
strcat(timer,ft_itoa(sec));
mvaddstr(timerFrameY+2,timerFrameX+3,timer);
if(end_time == 0){
// clear부분
}
}
C
복사
•
1초마다 타이머가 감소하며, 동시에 npc도 움직인다. 일정시간마다 배터리가 감소하게 했다.
타이머의 감소가 0이 되면 게임오버이며 npc의 시아각에 잡혀도 게임 오버이다. 이는 전부 시간에 묶어서 작성하였다.
5.
시야각
•
캐릭터가 일정 범위만 볼 수 있게 시야각을 설정했는데, 위 호스팅을 참고했다.
•
코드 중 필요없는 부분은 제거하고, 수정해야 할 부분은 수정해 사용했다.
플레이어 시야각은 시간에 따라 감소하며(배터리) 상호작용을 통해 배터리를 주워 시야각을 회복해야 한다
함수 구조도
스크린샷
Github
플레이 해보시고 싶으신 분이나 소스코드를 보고싶은 분들은 아래 github를 이용해주시면 됩니다.
클론받은 후, 윈도우는 make, 맥은 make mac을 치면 실행 가능하다. 게임 방법은 알려주지 않는다. 죽으면서 배우시길,,