티스토리 뷰
안녕하세요, 팀 사모예드입니다! 팀파이트 매니저 2는 2~3주 간격으로 개발 일지를 통해 꾸준히 개발 과정을 공유하고자 합니다. 많은 관심 부탁드립니다.
매치 엔진
첫번째 개발 일지에서 다룰 내용은 게임에서 가장 중요한 부분인 매치 엔진의 설계와 관련한 부분입니다. 아직 개발 초기 단계여서 앞으로도 많은 변경이 있을 예정이다보니 지금은 간단하게 어떤 방향성을 가지고 매치 엔진을 설계하고 있는지에 대해서만 다뤄보려고 합니다.
게임 규칙
팀파이트 매니저의 경우 1분동안 상대를 더 많이 죽인 팀이 승리한다는 단순한 규칙을 가지고 있습니다. 이러한 단순함은 게임을 쉽게 이해할 수 있게 만들어 주는 장점을 갖고 있지만, 그만큼 다양한 게임 구도가 나오기 힘들어 오래 플레이할 수록 단조롭게 느껴진다는 단점도 가지고 있습니다.
팀파이트 매니저 2의 경우 좀 더 오랜 시간 재밌게 즐길 수 있는 깊이 있는 게임 시뮬레이션을 추구하고자 합니다. 이를 위해 데스매치 규칙에서 벗어나, 간단화된 MOBA 스타일의 게임 규칙을 도입했습니다.
- 게임은 팀파이트 매니저와 동일하게 4대4 로 진행됩니다
- 탑, 정글, 바텀, 서포터 4개의 포지션이 있습니다. 맵을 보면 알 수 있듯 게임에는 탑, 바텀 2개의 라인이 있으며 탑 라인에는 탑 포지션이, 바텀 라인에는 바텀, 서포터 포지션이 가서 플레이하며 정글 포지션은 정글 몬스터를 잡으며 성장 및 갱킹을 수행하는 구조입니다.
- 경험치 개념이 있어 각 챔피언은 미니언 /정글 몬스터 처치, 타워 파괴, 처치 관여를 통해 경험치를 획득하고 레벨업을 할 수 있습니다. 레벨에 비례해서 능력치가 상승하며 레벨이 3이 되면 두 번째 스킬을 사용할 수 있게 됩니다
- 먼저 상대 성물을 파괴하는 쪽이 승리합니다.
게임의 규칙은 실존하지 않는 가상의 게임인 만큼 학습 비용과 직관성을 고려해 최대한 단순하게 만들고 있습니다. 한 두번 정도 게임 시뮬레이션이 돌아가는걸 구경만 해봐도 게임 규칙에 대해 이해할 수 있을만한 범위에서 규칙을 구성하는 것이 목표입니다.
선수 플레이
팀파이트 매니저의 경우, 경기 내 선수의 움직임은 단순한 규칙에 따른 행동을 반복하는 형태로 되어 있습니다. 이 AI에 영향을 주는 특성들이 몇가지 있지만, 전체적으로 선수가 직접 플레이한다는 느낌은 약합니다.
팀파이트 매니저2의 매치 엔진 목표중 하나는 좀 더 선수가 직접 플레이하는 것 같은 느낌을 주는 것입니다. 예를 들어, 팀파이트 매니저에서는 특성이 없다면 항상 가장 가까이 있는 적을 우선적으로 공격하게 되어있지만 팀파이트 매니저 2의 경우 선수가 본인의 판단에 따라 더 멀리 있는 적을 타게팅할 수 있습니다.
또, 선수의 능력치 부분에서도 변화를 생각하고 있습니다. 팀파이트 매니저의 경우 선수의 능력치(공격력, 방어력, 챔피언 숙련도 등)가 게임 시뮬레이션 속 챔피언의 능력치에 직접적으로 영향을 끼칩니다. 선수의 공격력이 높을 수록 시뮬레이션 속 챔피언이 더 강하게 공격하죠. 이러한 구성은 “플레이어가 실력이 좋아서 공격을 더 잘 한다”는 정도의 설정을 간략화해서 직관적으로 표현한 것이긴 하지만, 조금 어색하게 느껴지는건 사실입니다.
팀파이트 매니저2에는 선수의 능력치 차이가 직접적인 챔피언 능력치 차이로 이어지지 않습니다. 대신 플레이어의 AI에 영향을 끼쳐 실질적인 플레이의 차이를 만듭니다. 간단하게 예를 들자면 “메카닉”이 높으면 상대의 논타겟 스킬을 더 잘 피한다거나, “판단력”이 높으면 더 좋은 판단을 통해 운영적으로 이득을 본다거나 하는 식의 영향을 끼치게 되는 것입니다. 이를 통해 더 현실적인 시뮬레이션과 함께 다양한 팀컬러, 전술, 인게임 상황을 구축하는 것을 목표로 하고 있습니다.
챔피언 설계
그리고 이러한 방향성의 변경에 맞춰 챔피언의 스킬 구성도 좀 더 선수의 AI에 따른 차이가 잘 드러날 수 있는 형태로 설계하고 있습니다.
위는 현재 개발 진행중인 매치 엔진 상에서의 듀얼 블레이더와 처형인의 라인전 이미지입니다. 이미지에서 드러나있듯, 처형인의 당겨오는 스킬과 듀얼 블레이더의 돌진해서 상대를 띄워올리는 스킬이 모두 논타겟 형태로 변경되었습니다. 이때 듀얼 블레이더의 경우 돌진 스킬을 적을 공격하는 용도로도 쓸 수 있고, 선수의 판단에 따라 상대의 공격을 회피하거나 도망가는 용도로도 사용할 수 있습니다.
이러한 논타겟 스킬 위주의 챔피언은 선수의 메카닉이 높을 수록 더 활용도가 높게 됩니다. 위의 이미지에서는 듀얼 블레이더를 플레이하는 선수의 메카닉이 높아 처형인의 스킬을 잘 피해서 전투에서 이기지만, 메카닉이 충분히 높지 않다면 아래 이미지와 같이 전투의 결과가 달라질 수 있습니다.
팀파이트 매니저2의 경우 챔피언의 스킬, 능력치 설계와 선수의 능력치를 결합하여 챔피언마다의 특성이 충분히 드러날 수 있는 방향으로 기획하고 있습니다. 어떤 챔피언은 메카닉이 높을 수록 효과가 좋고, 어떤 챔피언은 판단력이 높을 수록 효과가 좋고, 선수의 능력치에 따른 AI의 차이로 라인전의 구도가 바뀔 수 있고… 이런 요소들을 잘 활용해서 우리 팀에 맞는 밴픽과 전술을 잘 구성할 수 있는 게임이 될 수 있도록요.
전술
게임의 규칙이 바뀌면서 다양한 전술적인 요소를 넣을 수 있게 되었습니다. 전술의 경우 라인전, 갱킹, 중후반의 운영등 다양한 부분에서 플레이어가 전체적인 게임의 방향성을 입맛에 맞게 설정할 수 있도록 하는 게 목표입니다.
예를 들어, 탑, 바텀 라인을 모두 후반에는 아주 강력하지만 초반 라인전이 약한 조합으로 뽑았다고 해봅시다. 이 경우 전술을 통해 각 라이너는 라인전을 최대한 수비적으로 진행하게 해서 안정적 성장을 도모할 수 있습니다. 여기에 덧붙여 개입 능력이 아주 강한 초반 집중형 정글을 픽한 후 전술을 통해 정글이 성장보다는 라인의 개입을 위주로 플레이하게 해 초반 라인전 단계를 수월하게 지나갈 수 있게 할 수도 있겠죠.
전술을 밴픽에 맞춰 적절한 승리 플랜을 세울 수 있는 장치로 동작하게 하되, 최대한 직관적인 옵션들을 위주로 구성하여 전술을 지정하는 과정이 너무 피로하지 않게 만드는 것이 현재의 목표입니다.
기술적 이야기
지금까지는 기획적 측면에서 팀파이트 매니저2의 매치 엔진이 가지고 있는 방향성에 대해 이야기해보았습니다.
이제 기술적인 측면에서 매치엔진 구현이 가지고 있는 어려움과 이를 어떤 식으로 해결해가고 있는지에 대해 다뤄보려고 합니다. 조금 전문적인 내용을 포함하고 있으므로 기술적 내용에 관심이 없으시다면 읽지 않고 넘어가셔도 괜찮습니다!
성능 문제
팀파이트 매니저의 경우 게임의 규칙이 간단하고 항상 1분만에 경기가 끝나기 때문에 비교적 시뮬레이션이 단순했습니다. 그럼에도 불구하고 그 정도의 단순한 시뮬레이션 마저도 꽤 큰 부하가 되었습니다. 가장 큰 이유는 데이터 수집입니다.
팀파이트 매니저 시리즈의 경우 게임 내부 규칙이 고정적이지 않습니다. 게임 내에서 일어나는 게임 속 패치로 인해 챔피언의 성능이 계속해서 바뀌고, 이렇게 바뀌는 환경에 컴퓨터의 밴픽 AI도 적절히 반응해야할 필요가 있습니다.
이를 위해 게임의 백그라운드에서는 계속해서 시뮬레이션을 돌리며 데이터를 수집하고 이를 바탕으로 컴퓨터의 밴픽 AI를 현재의 패치에 맞게 조정하는 과정을 거치는데요, 이 과정에서 상당히 많은 수의 게임 시뮬레이션을 필요로 합니다. 따라서 매 게임 시뮬레이션은 최대한 빠르게 동작할 필요가 있습니다.
팀파이트 매니저 2의 경우 전작보다도 훨씬 복잡한 게임 시뮬레이션을 가지고 있습니다. MOBA 스타일의 게임 규칙으로 바뀌면서 게임 시간이 1분보다 훨씬 길어졌고, 게임 속에 등장하는 오브젝트가 많으며, 벽과 같이 이동 불가능한 위치가 있어 길찾기도 필요하며, 부쉬와 같은 시야 비대칭이 있어 시야와 관련한 처리도 필요합니다. 여기에 더해 선수의 AI 역시 전작보다 훨씬 복잡한 처리를 요구하므로 전체적인 연산 요구량은 수십배 이상에 달합니다.
이러한 시뮬레이션 성능 문제를 해결하고 게임의 최적화를 최대한 높은 수준으로 달성하기 위해 팀파이트 매니저2는 rust를 기반으로 한 자체 엔진을 기반으로 작성되고 있습니다.
결정적(deterministic) 시뮬레이션
각 경기의 진행 내용을 프레임 단위로 전부 저장하는 건 세이브 파일의 용량 문제에서 아주 큰 부담이 됩니다. 이러한 부담을 줄이기 위해서는 각 경기의 초기 상태(시드값, 각 팀의 전술, 출전 선수 목록 등)가 완전히 동일하다면 항상 동일한 결과가 나오는 것을 보장할 필요가 있습니다. 이를 결정적(deterministic)이라고 하는데요, 이 조건을 만족할 경우 각 경기의 초기 상태만 저장해두면 각 경기 시뮬레이션의 리플레이를 얼마든지 다시 재생할 수 있으므로 용량 측면에서 아주 큰 이점을 가지게 됩니다.
또, 멀티 플레이 진행시에도 경기 시뮬레이션 과정을 일일히 전송해줄 필요가 없고 경기 시작시 시작 상태만 보내주면 각자 알아서 시뮬레이션을 돌릴 수 있다는 장점이 있습니다.
이런 결정적 시뮬레이션의 구현의 경우 크게 신경써야 할 부분은 두 가지가 있습니다.
- 랜덤 : 각 경기 시뮬레이션은 초기 시드값을 기반으로 한 난수 생성기를 별도로 생성하여 사용해야합니다. 크게 어려운 부분은 아니지만, 각종 라이브러리를 사용하다보면 의도치 않게 비결정적 동작이 섞일 수 있어 주의가 필요합니다.
- 부동소수점 연산 : 이 경우, 같은 pc 내에서는 결정적이라 할 수 있지만 환경에 따라 오차가 다를 수 있다는 문제가 있습니다. 멀티플레이 등을 염두에 뒀을 때 이러한 오차가 플레이어마다 경기 결과를 다르게 보게 되는 문제를 발생시킬 수 있으므로, 이로 인한 문제도 원천적으로 차단할 필요가 있습니다. 어차피 성능적인 측면에서도 부동소수점 연산이 훨씬 느리기도 하고 경기 시뮬레이션의 특성상 연산의 오차가 아주 정밀해야할 필요도 없기 때문에, 정수를 기반으로 한 고정 소수점 연산으로 모든 로직을 구현했습니다.
전처리 최적화
길찾기와 시야의 계산은 어쩔 수 없이 느립니다. 이를 위해서 전체 맵을 32x32 단위의 칸으로 잘라 전처리한 데이터를 사용하는 방식으로 연산 비용을 최대한 줄였습니다.
예를 들어 길찾기의 경우 “(a,b) 칸에서 (c,d) 칸으로 이동하기 위해 움직여야하는 다음 칸” 정보를 모두 미리 계산해서 저장해놓고 사용하면 실제 시뮬레이션 중에는 즉각적인 길찾기가 가능해집니다. 시야의 경우에도, 마찬가지로 “(a,b) 칸에서 (c,d)칸을 볼 수 있는가?” 정보를 미리 계산해두면 시뮬레이션 중에는 별도의 추가 연산 없이 시야 정보를 얻을 수 있게 됩니다. 대신에 이 정보를 저장하는데 몇 mb 단위의 메모리가 필요해지지만.. 요즘 컴퓨터 성능에서 크게 문제가 되는 크기의 메모리는 아니죠.
물론 이 과정에서 각 연산이 32픽셀 단위로 잘린 조각을 기준으로 결과가 정해지므로 정밀도는 조금 더 떨어지게 되지만, 경기 진행을 눈으로 볼 때 전혀 신경쓰이지 않는 정도의 차이라 그다지 문제가 되지 않았습니다.
메모리 할당 최적화
이런 종류의 게임 시뮬레이션은 아주 빈번한 메모리의 할당과 해제를 요구합니다. 각종 투사체가 경기 중에 수백번씩 생성되고 사라지며, 온갖 종류의 버프, 챔피언 및 오브젝트의 사망과 재생성 과정에서 계속해서 메모리 할당과 해제가 일어납니다. 그리고 컴퓨터에서 이런 메모리의 할당, 해제는 상당히 비용이 큰 작업입니다.
rust와 같은 저수준 제어가 가능한 언어는 이런 메모리 할당 과정을 입맛에 맞게 수정하기 용이하다는 장점을 가지고 있습니다. 이를 바탕으로 게임 시뮬레이션 구조에 가장 적절한 메모리 할당자를 이용해서 최적화를 진행하고 있습니다.
렌더링 분리
게임 시뮬레이션에서 실제로 해당 게임의 진행을 관전하는 경우가 아니라면 생략할 수 있는 정보가 많이 있습니다. 이러한 렌더링과 관련한 정보는 백그라운드에서 게임 진행의 결과 및 관련 통계 수치만을 얻기 위한 경우에는 계산하지 않고 생략해도 됩니다.
따라서 이 두 상황을 구분지어서 백그라운드에서 돌아갈 때는 렌더링을 위한 정보 생성을 생략함으로써 게임 시뮬레이션을 좀 더 빠르게 구동할 수 있습니다.
병렬성
하나의 CPU 코어 상에서 돌릴 수 있는 시뮬레이션 성능에는 한계가 있으므로, 모든 코어를 잘 활용해서 병렬로 시뮬레이션을 돌릴 필요가 있습니다. 이를 위해 개발 초기 단계부터 시뮬레이션의 병렬 처리를 원활히 진행할 수 있는 내부 구조를 구현하여 사용하고 있습니다.
이러한 과정을 거쳐 현재 게임 시뮬레이션은 게임 시간 1분치를 계산하는데 0.05초에서 0.1초 정도의 시간이 걸리는 정도로 구현되어 있습니다. 시뮬레이션에 걸리는 시간은 당연히 PC 성능과 병렬 스레드 수에 따라 차이가 있겠지만, 일단 현재 개발 PC 기준 4개 스레드 병렬 처리의 경우 1000개의 경기를 시뮬레이션하는데 4분 정도의 시간이 걸립니다(경기당 0.24초 정도). 매치 엔진의 성능은 개발과 최적화를 계속해서 병행하고 있으며, 현재는 여러 기술적 난점들을 하나씩 해결하며 매치 엔진의 기획적 방향성을 충실히 살리면서도 아주 빠르게 작동할 수 있도록 기반 작업을 계속해서 진행하고 있는 상황입니다.
긴 글 읽어주셔서 감사합니다! 다음 개발 일지에서 다시 뵙겠습니다.