Day 11 A* 알고리즘을 이용한 길찾기가 적용된 타워 디펜스 게임 구현
타워 디펜스 게임에서는 적이 왼쪽에 있는 시작 타일에서 오른쪽에 있는 마지막 타일로 이동하려 한다. 초기에 적들은 왼쪽에서 오른쪽으로 똑바로 이동한다. 하지만 플레이어는 격자상의 사각형에 탑을 세울 수 있으므로 경로는 상황에 따라 탑 주변으로 이동하게 변경된다.
타일을 선택하고자 마우스를 클릭한다. 타일을 선택한 후에는 타워를 세우기 위해 B키를 사용한다. 적 비행기는 A 알고리즘을 통해 길을 찾아 타워 주변을 지나간다. 세워진 각각의 타워 때문에 Ai는 상황에 따라경로를 변경한다. 플레이어가 적을 완전히 차단할 수 없도록 하기 위해 플레이어가 타워를 세우는 것을 요청하면 코드는 먼저 적이 이동할 수 있는 경로가 여전히 존재하는지를 확인한다. 타워가 적을 완전히 차단하면 게임은 플레이어가 타워를 세우는 걸 허락하지 않는다. 코드의 간결함을 위해 게임 프로젝트의 Tile 클래스는 A 탐색에 사용된 추가 데이터뿐만 아니라 모든 그래프 관련 정보를 포함한다. 모든 타일을 생성하고 그래프를 초기화하는 코드는 Grid 클래스의 생성자에 있다. Grid 클래스느 또한 실제 A* 검색을 수행하는 FindPath 함수를 포함한다.
소스 코드
game.h
먼저 game.h 헤더 파일에서 Grid 액터에 대해 저장할 수 있는 멤버 변수를 선언하고 Enemy 액터들이 생성될 때 저장하는 std::vector<class Enemy*> mEnemies 멤버 변수를 선언한다. 그리고 Enemy가 생성되는 타임을 설정하는 mNextEnemy를 선언한다. 그리고 타워가 가까운 Enemy를 공격할 수 있게 타워와 가장 가까운 Enemy를 찾아주는 GetNearestEnemy 함수를 선언한다.
game.cpp
Game::ProcessInput 함수에서 마우스 클릭에 대한 입력을 받는다. 마우스의 입력을 통해 타워를 세울 Tile을 결정해야하기 때문이다. 마우스 입력은 키보드 입력과 같이 SDL 함수를 이용해받는다. 그리고 Game::GetNearestEnemy 함수에서는 mEnemies에 저장된 적들의 현재 위치와 타워의 위치를 계산해서 해당 타워와 가장 가까운 적을 계산한다.
Tile.h
Tile.cpp
Tile 클래스는 디펜스 맵의 각 타일의 state와 알고리즘을 계산하기 위힌를 저장한다. 그리고 각 Tile마다 인접 노드와 부모 노드를 저장하는 멤버 변수를 선언한다. Tile 클래스에서 저장하지 않고 Gird 클래스에서 저장한다. ToggleSelect 함수는 마우스로 클릭한 Tile을 select된 상태(mSelected = true)로 유지하는 함수이다. Tile::SetTileState는 타일의 state를 설정하는 함수다. ToggleSelect 함수나 SetTileState 함수나 UpdateTexture 함수는 호출해서 계속 타일의 텍스쳐를 갱신한다.
NavComponent.h
NavComponent.cpp
NavComponent는 Enemy 액터가 길을 따라가기 위한 클래스이다. MoveComponent로 Enemy가 전진하고 있을때 NavComponent를 통해 이동할 방향을 결정한다. Update를 통해 계속 다음 노드를 갱신하고 TurnTo 함수로 방향을 바꿔준다.
Tower.h
Tower.cpp
Tower 클래스는 Tile을 선택하고 B키를 누르면 건설되는 Tower 액터에 대한 클래스이다. Grid 클래스의 BuildTower를 통해서 타워가 지어진다. 타워의 발사체 발사 시간, 타워의 사정 거리를 정해준다. 그리고 UpdateActor 에서는 deltaTime마다 계속 갱신하면서 Enemy의 위치를 찾고 Enemy 방향으로 Bullet을 생성해서 발사한다. 여기서 Game::GetNearestEnemy 함수를 이용해서 가까운 적을 찾는다.
Enemy.h
Enemy.cpp
Enemy는 소멸자로 해당 액터를 없앤다. 그리고 BaseTile에 도착하면 상태를 EDead로 바꾸고 사라진다. 그리고 피격 범위를 CircleComponent를 통해 정해준다. 그리고 NavComponent를 통해 길을 따라 방향을 찾아 움직인다.
Grid.h
Grid.cpp
Grid 클래스에서는 알고리즘을 통해 start tile부터 end tile까지의 길을 찾고, 마우스 클릭을 다루고, 타워를 짓는다. 먼저 Grid 클래스의 생성자에서는 7x16의 Tile을 초기화한다. 그리고 시작과 끝 Tile을 정해주고 타일마다 인접 타일을 설정해준다. 그리고 FindPath함수에서 알고리즘을 통한 길찾기를 진행한다. 그리고 UpdatePathTiles를 통해 Tile의 상태를 EDefault로 초기화하고 path tile의 상태를 EPath로 초기화한다. 그리고 BuildTower 함수를 통해 타워를 건설하고 만약에 원래 길 위에 타워가 건설되면 길을 다시 설정하고 FindPath를 통해 다시 길을 찾는다. FindPath 함수에서는 알고리즘을 통해 길을 찾는데 먼저 모든 타일의 경로 비용을 초기화한다. 그리고 인접 노드에 대해서 검사를 한다. open set에는 검사를 한 타일이 들어가고 close set에는 경로가 될(부모 노드가 되는)타일이 담긴다. 알고리즘에서 노드의 경로 비용은 부모의 g값에 따라 달라지므로 부모가 바뀌면 f(g가 바뀜)가 바뀐다. mBlocked 는 타워가 있는지 없는지를 저장한다. 그리고 mBlocked가 경로를 바꾸는데도 영향을 미친다. 그리고 UpdateActor에서는 적을 생성한다.
Bullet.h
Bullet.cpp
Bullet 클래스는 발사체 액터에 대한 정의를 한다. 발사체 또한 CircleComponent를 선언해서 Enemy와 Bullet이 부딪혔을때 두 액터의 상태를 EDead로 바꾼다. 그리고 MoveComponent로 이동을 구현한다.