모든 ncurses의 함수를 사용하려면 당연하지만
#include<ncurses.h>
를 해줘야한다.
#include"../Include/SnakeGame.h"
int main(){
SnakeGame* sG= new SnakeGame();
sG->StartGame();
}
간단하다.
SnakeGame클래스를 하나 생성 후 StartGame()함수를 호출하도록 구현하였다.
#ifndef _SnakeGame_H
#define _SnakeGame_H
#include "Snake.h"
#include "IDraw.h"
#include "Board.h"
#include "ItemManager.h"
#include "CollisionManager.h"
#include "Gate.h"
#include<vector>
using namespace std;
class SnakeGame{
public:
SnakeGame();
SnakeGame(pair<int,int> startPoint);
~SnakeGame();
void Display();
void StartGame();
private:
int gameDelay;
Snake* player;
Board* entireBoard;
Gate* gate;
ItemManager* itemManager;
CollisionManager& colMgr=CollisionManager::GetInstance();
vector<IDraw*> drawableObjects;
};
#endif
사용되는 클래스로는
Snake, 플레이어 뱀 클래스
[Snake객체 구현]
Board, 전체 보드 클래스
[Board 및 Wall객체 구현]
Gate, 포탈 클래스
[Gate 객체 구현]
ItemManager, 아이템 관리하는 매니저 클래스
[ItemManager객체 구현]
[Item객체 구현]
CollisionManager 충돌 관리하는 매니저 클래스
[CollisionManager객체 구현]
등이 있다.
화면에 그려지는 모든 오브젝트들은 IDraw 인터페이스를 상속받는다.
해당 인터페이스를 상속받는 오브젝트들의 벡터를 따로 생성했다.
#include<unistd.h>
#include<ncurses.h>
#include "../Include/SnakeGame.h"
#include "../Include/Input.h"
#include"../Include/CollisionManager.h"
using namespace std;
SnakeGame::SnakeGame(){
gameDelay=1;
player= new Snake();
entireBoard=new Board(drawableObjects,false);
itemManager=new ItemManager(drawableObjects);
//게이트는 벽에 세워지면 벽을 지워야하므로 벽보다 늦게 생성자 실행되어야함.
gate= new Gate(drawableObjects,make_pair(2,0),make_pair(3,20));
drawableObjects.push_back(static_cast<IDraw*>( player));
}
SnakeGame::SnakeGame(pair<int,int> startPoint){
gameDelay=1;
player= new Snake(startPoint);
entireBoard=new Board(drawableObjects,false);
itemManager=new ItemManager(drawableObjects);
//게이트는 벽에 세워지면 벽을 지워야하므로 벽보다 늦게 생성자 실행되어야함.
gate= new Gate(drawableObjects,make_pair(2,0),make_pair(3,20));
drawableObjects.push_back(static_cast<IDraw*>( player));
}
SnakeGame::~SnakeGame(){
delete player;
delete entireBoard;
delete itemManager;
drawableObjects.clear();
vector<IDraw*>().swap(drawableObjects);
}
void SnakeGame::Display(){
for(auto it = drawableObjects.cbegin() ; it!= drawableObjects.cend(); ++it){
(*it)->Draw();
}
refresh();
}
void SnakeGame::StartGame(){
initscr();
noecho(); //hide input on screen
curs_set(false); //hide cursor
keypad(stdscr,TRUE);
nodelay(stdscr,TRUE); //getchar non-blocking mode로 실행
while(1){
//죽었다면 끝냄
if(player->IsDead()) break;
//아이템 매니저에서 아이템 생성
itemManager->CheckIfUnusedItemExist();
player->Move(getInput());
colMgr.CheckIfCollide(player);
clear();
Display();
sleep(gameDelay);
}
endwin();
}
생성자로는 두 가지를 만들었는데, 플레이어 시작위치만 변경하기 위해 만들어줬다.
소멸자에선 할당받은 객체들을 해제시킨다.
사실 벡터의 clear 함수가 capacity는 그대로고 size만 0으로 줄인다는 걸 알고있어서 검색해봤다.
저렇게 빈 벡터와 swap함수를 통해 교체하면 사라진다는데 나중에 확인 해봐야겠다.
main함수에서는 SnakeGame의 StartGame을 호출한다.
noecho함수를 통해 입력값을 스크린에서 빼주고,
curs_set(false)를 통해 커서를 숨긴다.
또한 nodelay함수를 호출해야 input.h의 getchar()함수가
non-blocking모드로 실행된다. [input.h 구현]
그 다음 반복문의 순서는
1. 매 반복문마다 플레이어가 죽었는지 체크한다.
2. 아이템매니저에서 사용안한 아이템이 있는지 체크 후 아이템 관련 처리
3. 그 후 플레이어를 움직인다.
4. 충돌처리매니저에서 플레이어가 이동한 좌표를 넣어서 충돌체가 있는지 판단하고 처리한다.
5. 화면을 다 지우고 Display()를 호출한다.
6. Display()함수는 IDraw* 인터페이스를 상속받는 모든 객체들의
Draw()함수를 호출한다.
할당받은 객체들을 소멸자에서 다 해제시켜주는데 나중에 smart포인터를 통해 업데이트를 하고싶다.
Display에서 iterator을 사용하는게 좀 어려웠다.
drawableobject의 원소들이 포인터 형이라서
(*it)가 가리키는 값은 포인터다.
따라서 (*it)-> Draw() 이런식으로 Draw함수를 호출해야한다.
게시글의 내용이 상당히 복잡한 프로그래밍 코드에 대한 것이군요. 게임의 구현에 대한 세부사항을 잘 설명하셨습니다. 오브젝트들의 상호작용과 각 클래스의 역할에 대한 설명이 인상적이었습니다. 특히, 코드의 세부사항을 이해하는 데 도움이 되었습니다. 또한, 다음 단계로 스마트 포인터를 사용하려는 의지도 보여주셨네요. 좋은 글 감사합니다. 계속해서 좋은 내용 공유 부탁드립니다!