이전 글에서 말했긴 하지만 다시 한번 더 상기하기 위해 캐릭터 유닛의 행동과 그에 따라 필요한 속성(정보)들을 우선 나열해 보고자 한다.
캐릭터가 할 수있는 행동
1. 이동할 수 있다.
2. 이동을 멈출 수 있다.
3. 공격할 수 있다. (모든 유닛을 대상으로)
4. 공격받을 수 있다.
5. 이동과 공격하기 위한 적을 찾고, 감지할 수 있다.
6. 사망 할 수 있다.
7. 스폰되어 질 수 있다.
8. 업그레이드 되어 질 수 있다.
행동을 나열해 보았을 때 크게 공격, 이동, 사망, 스폰, 업그레이드의 총 5가지 시스템(행위)로 나누어 지는것으로 생각된다.
물론 추후에 캐릭터별 스킬을 추가하는것을 고려 한다면 이보다 더 달라 질 수 있겠지만...
캐릭터가 스폰되고, 이동해서 적을 공격하는 기본적인 로직을 먼저 구현 후 생각해도 늦지 않을 것 같다.
캐릭터의 이동에 필요한 데이터
캐릭터가 이동 시 필요한 정보는 다음과 같다.
1. 이동 속도
2. 이동 대상
이동 속도
이동 대상
쉽게 말해 얼마나 빠르게, 어디로 이동해야 하는지에 대한 정보다.
이동 대상 정보가 없다면 적을 공격하고 싶어도 적을 공격하지 못하고 그냥 가던 방향으로 계속 이동할 것이기 때문에 필요하다.
이동 대상에 대한 지정은...
- 스폰 시 적 캐릭터의 수를 확인한다
- 적 캐릭터가 없다면 적 성채를 이동 대상으로 삼는다.
- 적 캐릭터가 존재한다면... 가장 가까운 적을 찾고, 해당 적을 이동대상으로 삼는다. - 이동 중 이동 대상인 캐릭터가 사망하면 1번 로직을 다시 수행한다.
- 적 캐릭터가 공격 가능 범위(거리) 안으로 들어 오면 이동을 멈추고 공격을 수행한다. 적이 사망하면 다시 1번 반복.
캐릭터의 공격에 필요한 데이터
캐릭터가 적을 공격하기 위해서는 우선 공격할 대상을 찾고, 타겟팅할 필요가 있다.
타겟팅을 하는 방법은 어떻게 구현하는데에 따라 필요한 데이터와 구성하는 방법이 천차만별이다.
또 어떻게 구현하는지에 따라 게임의 성능도 많이 달라 질 것이다.
캐릭터가 공격대상이 될 적 캐릭터를 찾는 방법은 다음과 같다.
1. 캐릭터의 시야(Trigger 타입의 Collider) 안의 모든 적 캐릭터들과 자신의 거리를 체크하고 가장 가까운 캐릭터를 타겟팅
2. 소환된 순서들 중에 가장 오래 살아 남은 캐릭터를 타겟팅
3. 모든 적 캐릭터와 자신의 거리를 비교하여 가장 가까운 적을 타겟팅
4. 적 성채를 향해 이동하다가 적 인식 거리 안에 적 유닛이 들어 오면 가장 처음 인식한 적 유닛을 타겟팅
캐릭터 공격 대상 찾는 방법들에 대한 설명
1. 캐릭터의 시야내 모든 적 캐릭터들과 자신의 거리를 체크하고 가장 가까운 캐릭터를 타겟팅하는 방식
구현 방식
유니티에서 제공하는 OnTrigger로 시작하는 유니티 메세지를 적극적으로 활용하는 방법이다.
게임 시작 후 캐릭터는 성채를 향해 이동하게 구현할 것이고, 성채는 게임이 시작 되기 전 부터 맵 위에 존재할 것이기 때문에 언젠가는 상대방 유닛(성채도 유닛이다)이 시야 범위 안에 들어오는 것이 확실함으로 생각할 수 있는 방법이다.
- 캐릭터는 스폰 후 적 성채를 향해 이동한다.
- 적 성채를 행해 이동 중 시야 범위 콜라이더에서 OnTriggerStay2D함수로 시야 내 존재하는 적의 수를 세고, 0 이상이라면 가장 가까운 적을 타겟팅한다. 타겟팅 할때 적의 상태도 체크해서 캐릭터 사망 연출이 실행중일때는 제외하도록 한다.
- 공격 인식 범위 안에 적이 들어 오면 해당 적에게 공격을 가한다.
공격 인식 범위는 Collider로 구현해도 되고, 단순하게 상수를 지정해서 해당 값 보다 적과의 거리가 가까운 적을 확인 할 경우 공격 대상으로 삼아도 된다.
장점
- 모든 적들에 대해 탐색 연산을 수행하는 것 보다는 연산랑이 감소할 것이다.
- 유니티에서 제공하는 기능을 최대한 이용할 수 있어 구현 방법이 복잡하지 않다.
단점
- 같은 팀에도 불필요하게 Collider 연산이 일어난다.
- Collider 충돌 연산은 비용이 큰 편으로, 특히 게임 속 캐릭터들이 많아지면 많아질수록 비용이 높아진다.
2. 소환된 순서 중 가장 오래 살아남은 캐릭터를 타겟팅
구현방식
1. 스폰시스템(스폰 메니저)가 캐릭터를 스폰 할 때 몇번째로 소환했는지 번호를 매기면서 큐(Queue)형식의 배열에 저장한다.
2. static 타입의 AttackTarget변수에 소환된 적들 중 살아남은 가장 마지막으로 소환된 적을 저장합니다.
이 값은 AttackTarget으로 지정된 캐릭터가 사망할 때 까지 유지됩니다.
캐릭터가 사망하면 다음 순서의 캐릭터를 지정. 다음 순서의 캐릭터가 존재하지않으면(Queue의 원자값의 수가 0) 적 성채를 AttackTarget으로 지정한다.
※ 이 부분 구현 시 성채를 타겟으로 지정하면 사망 기다림 기능은 동작하지 않게 하고, 적 캐릭터의 소환으로 인한 AttackTarget 변경이 우선시되도록 만들어야 합니다. 그렇지 않으면 성채가 최우선 타겟이 되어 적 캐릭터를 공격하지 않은 상황이 발생할 수 있다.
3. 유닛들은 AttackTarget을 대상으로 공격을 수행한다.
장점
- 물리연산을 수행하지 않으며 캐릭터마다 별도의 연산을 진행할 필요가 없음으로 가볍다.
- 로직이 간단함으로 구현하기 좋다.
단점
- 이동 속도가 느린 캐릭터의 경우(아래 이미지 참조) 다른 빠른 캐릭터에 비해 뒤쳐지게 될 수 있고, 상대방 캐릭터는 뒤쳐진 캐릭터를 타겟으로 삼기 떄문에 의도치 않게 앞의 속도가 빠른 캐릭터를 무시하는 결과를 부를 수 있다.
- 한 캐릭터만 공격 대상으로 삼게 되므로, 해당 캐릭터의 체력이 높을 시 다른 캐릭터의 공격 진행은 하지 못하게 된다.
- 공격대상을 지정하는 알고리즘이 스폰시스템에 묶이게 됨으로 확장성이 매우 부족하다.
3. 모든 적 캐릭터와 자신의 거리를 비교하여 가장 가까운 적을 타겟팅
캐릭터마다 일정 시간과 상황을 주기로 적을 확인하는 전략이다.
구현방식
1. 스폰시스템(스폰 메니저)가 캐릭터를 스폰 할 때 리스트(List)형식의 캐릭터 유닛 배열에 저장한다. (각 팀별로 따로 저장함)
2. 소환된 캐릭터는 지정된 주기(예들들어 0.1초)마다 자신과 적의 거리를 확인한다.
적의 거리는 1번에서 저장했었던 상대방 팀의 캐릭터 유닛 배열을 순회하면서 Vector2.Distance(Vector2 a, Vector2 b) 연산을 수행하며 가장 거리가 가까운 적을 타겟팅한다.
3. 타겟팅한 적 유닛의 DieEvent에 리스너를 추가하여 적 유닛 사망 시 다른 유닛을 지정하도록 만들어 준다.
(자신의 사망한 경우 타겟팅한 적에게 리스너를 지워주는 작업 또한 필요하다.(죽은 시체에 메세지를 보낼 필요는 없으니...))
4. 2번의 과정 중 거리가 공격 가능 거리 이하면 공격을 수행한다.
5. 자신이 사망한 경우 1번에서 추가했던 배열에서 빠진다.
장점
- 소환된 순서 중 가장 오래 살아남은 캐릭터를 타겟팅하는 방법 만큼은 아니지만 물리연산을 하지 않으므로 가볍다.
- 다른 시스템에 의존된 부분을 적게 구성할 수 있음으로 확장성이 좋다.
단점
- 캐릭터별로 적을 찾기 때문에 캐릭터가 많이 존재할 경우 더 많은 연산이 필요한 점은 부정할 수 없다.
- 이전에 공격하던 적을 버리고 새로운 적에게 공격을 시도할 수 있어 유저의 입장에선 버그로 보일 수 있다.
(이 점은 로직 자체를 보완해야 함)
4. 성채를 향해 이동하다가 공격 인식 범위 내에 가장 처음 들어 온 적 유닛을 공격
앞에서 언급했던 캐릭터의 시야내 모든 적 캐릭터들과 자신의 거리를 체크하고 가장 가까운 캐릭터를 타겟팅하는 방식 에서 시야 범위관련 기능이 빠진 버전이다.
구현 방법은 단순하게 공격 인식범위의 OnTriggerEnter() 유니티 메세지로 들어 온 적을 타겟팅하고 적이 죽을 때 까지 공격을 수행하는 것이다.
장점으로는 구현 방법이 간단하다는 것이지만, 캐릭터의 시야내 모든 적 캐릭터들과 자신의 거리를 체크하고 가장 가까운 캐릭터를 타겟팅하는 방식 의 장 단점이 변화 없이 담겨 있다는 점에서... 그닥 구미가 당기지 않았다.
그래서 선택한 적 탐색방식은...
모든 적 캐릭터와 자신의 거리를 비교하여 가장 가까운 적을 타겟팅하는 방식이다.
이 글을 어쩌다 읽게 된 사람들은 Collider 물리 연산이 그리 크게 무겁지 않다거나 이정도 게임에서는 성능을 신경 쓰지 않아도 막 캐릭터를 수만개씩 소환하는게 아니기 때문에 그리 심각하게 생각하지 않아도 된다고 말할 수 있겠다.
내가 아는 선에서 내놓은 답이라 어쩌면 더 좋은 방식이 있을 수도 있고, 모두 구현해서 퍼포먼스를 직접 확인하는 방법도 있을 수 있다.
하지만 모든 로직을 구현해 보는데에는 너무 많은 시간이 들어간다.
그렇기 때문에 현재 선택할 수 있는 선에서 실수를 벌일 자신이 가장 적고, 확장하기에 더 편리하며, 로직에 대한 컨셉이 간단한 것을 선택하는 것이 필요한데, 캐릭터별로 자신과 적 캐릭터들의 거리를 확인하는 방식이 여기에 가장 적합하다고 생각했다.
이 방식에 필요한 데이터로는
1. 각 팀별로 관리되는 맵에 존재하는 소환 된 유닛 List
단 하나! 만 있으면 된다.
다만 이걸 작성하려면 우선 캐릭터 스폰이 먼저 되어야 하니 캐릭터 유닛을 단순하게 스폰만 하는 스폰 시스템 (스폰 메니저 먼저 만들어야 겠다.)
다음 편에 이어서...
'개발 > 게임 개발' 카테고리의 다른 글
간단한 유니티 2D 타워 디펜스 게임 만들기 5편 - 캐릭터 이동 (0) | 2022.10.03 |
---|---|
간단한 유니티 2D 타워 디펜스 게임 만들기 4편 - 캐릭터 스폰 (0) | 2022.09.24 |
간단한 유니티 2D 타워 디펜스 게임 만들기 2편 - 기초 쌓기 (0) | 2022.09.09 |
간단한 유니티 2D 타워 디펜스 게임 만들기 1편 - 개요 (0) | 2022.08.21 |