보상 시스템 디자인 (2023년 겨울 버전)

이전에 설명한 적 있는 보상 시스템을 2023년 겨울 기준으로 다시 작성해 보았습니다.

보상 시스템 디자인 (2023년 겨울 버전)

오래 전 보상 시스템 설계 (1), 보상 시스템 설계 (2)를 통해 게임에서 보상 시스템이 대략 어떤 식으로 만들어지는지 설명한 적이 있습니다. 그로부터 시간이 많이 흘렀지만 종종 검색을 통해 저 포스트들이 종종 읽힌다는 점을 알고는 있었습니다. 하지만 1년도 넘게 흐른 지금에 와서 다시 읽어보면 솔직히 도무지 뭔 소린지 잘 모르겠습니다. 대략 블릿포인트 들여쓰기를 통해 데이터구조를 설명하고 그 사용 사례를 보여주려고 한 것 같기는 하고 또 보상 체계에 어떤 기능이 필요한지 대략 파악할 수는 있을 것 같습니다. 하지만 검색을 통해 저 페이지를 발견한 누군가가 고작 저런 설명 만으로 보상 시스템의 구성요소와 대략적인 데이터구조를 파악할 수 있을 것 같지 않아 보입니다. 그래서 결론은 저 페이지에 설명한 것과 별로 다르지 않지만 2023년 겨울 현재를 기준으로 조금은 더 알아 먹을 수 있도록 설명해 보려고 합니다.

게임에서 보상은 어떤 컨텐츠를 클리어 하거나 몬스터를 죽일 때 얻는 거라고 생각하기 쉽습니다. 실은 게임에서 보상을 받는 가장 흔한 상황이 이들이기는 합니다. 가령 챕터 1-1을 클리어 하면 보상을 받고 필드에 있는 몬스터 한 마리를 처치하면 바로 보상을 받습니다. 그런데 보상은 이보다 좀 더 넓은 범위에 걸쳐 사용됩니다. 보상은 근본적으로 게임이 플레이어에게 이전에 없던 아이템을 만들어 건네 주는 행동입니다. 그래서 보상이라고 생각하지 않고 게임이 아이템을 만들어 플레이어에게 건네 주는 행동을 기준으로 게임을 살펴보면 대장간에서 강화에 의해 옵션이 변경된 새 무기를 받을 때, 업적을 달성해 업적 이름 옆에 표시된 ‘받기’ 버튼을 클릭해 아이템을 받을 때, 재료를 모아다 마을에 있는 제작 NPC에게 건네면 제작 NPC가 아이템을 만들어 플레이어에게 돌려줄 때, 또 상점에서 아이템을 구입할 때 같은 여러 동작이 실은 보상과 거의 같은 메커닉이라는 사실을 알 수 있습니다.

한편 현대 게임의 보상이 이전 시대와 가장 크게 달라진 점은 확률과 선택을 포함하며 플레이어의 상태에 따라 서로 다른 보상을 지급할 수도 있다는 점입니다. 가령 클래스 구분과 클래스 전용 장비 개념이 있는 게임에서는 초반에 플레이어의 클래스에 맞는 장비를 드랍하는 것이 좋습니다. 가령 플레이어가 원거리 클래스라면 똑같은 보스를 클리어 해도 원거리 장비를 드랍해야 플레이어가 계속해서 플레이 할 수 있습니다. 뜬금없이 보스로부터 근거리 무기가 나오면 이를 장착할 수 없을 뿐 아니라 초반 장비여서 이를 판매한다 하더라도 별 이익을 얻지 못할 가능성이 높습니다. 하지만 선택보상상자의 문제에서 보듯 게임에 따라서는 클래스와 클래스 전용 장비 개념이 있지만 클래스에 따른 보상 시스템이 없어 선택 보상 상자를 사용해 고객이 직접 장비를 선택하도록 만들기도 하는데 만약 여기서 실수하면 고객을 크게 좌절 시키게 됩니다.

또 확률과 선택은 퀘스트 종류에 따라 퀘스트를 시작하기 전에 보상으로 받을 수 있는 아이템의 종류를 보여주고 퀘스트를 클리어 하면 이들 중 하나를 확률에 따른 무작위로 받도록 하기도 하고 또 확률에 따라 기본 아이템은 항상 드랍되지만 보너스 아이템은 낮은 확률로 드랍 되도록 만들어 기대감을 만들기도 합니다. 퀘스트를 시작하기 전에 이 퀘스트를 수행함으로써 받을 가능성이 있는 여러 가지 아이템을 보여준 다음 퀘스트를 완료할 때 랜덤으로 이들 중 하나를 주거나 뒤집힌 아이템 중 하나를 선택하게 하는 메커닉은 개발자가 의도한 시행횟수를 달성하면 아이템을 얻을 수 있겠지만 이 시행횟수를 도달하는 과정을 덜 지루하게 만들면서도 시행횟수를 일정 수준 이상으로 유지하는 역할을 합니다. 또 낮은 확률로 보너스 아이템을 받을 수 있는 메커닉은 투입한 시간에 따라 항상 정해진 보상을 지급해 보상이 선형으로 증가하지만 가끔 낮은 확률로 보너스를 지급해 기대감을 만들고 또 지루함을 덜 수 있기도 합니다. 이 보너스에 집중해 낮은 확률로 많은 골드가 한번에 드랍되는 메카닉으로 여러 사람을 웃기고 울리던 사례는 오래된 MMO 게임에 종종 나타났습니다.

flowchart TB subgraph 보상 subgraph 그룹1[그룹 1] subgraph 실행조건1[실행조건] 항상처리1[항상처리] end subgraph 결과선택방법1[결과 선택 방법] 독립확률1[독립확률] end subgraph 아이템목록1[아이템 목록] subgraph 철광석[철광석] Percent100[100%, 3-10개] end subgraph 돌덩어리[돌덩어리] Percent50[50%, 5개] end end end subgraph 그룹2[그룹 2] subgraph 실행조건2[실행조건] 원거리클래스[원거리 클래스일 때만 처리] end subgraph 결과선택방법2[결과 선택 방법] 독립확률2[독립확률] end subgraph 아이템목록2[아이템 목록] subgraph 금덩어리[금덩어리] Percent5[5%, 1개] end end end end 보상요청[몬스터 사냥 후 보상 요청] --> 보상 보상 -- 그룹1: 누구에게나 높은 확률로 \n철광석, 돌덩어리 드랍--> 인벤토리 보상 -- 그룹2: 원거리클래스에 한해 \n낮은 확률로 금덩어리 드랍 --> 인벤토리

이런 보상을 처리하기 위한 기초적인 구조를 살펴봅시다. 먼저 보상을 지급할 상황에 맞는 한 가지 보상을 연결할 수 있게 하되 한 가지 보상은 그 안에 여러 개의 보상 그룹을 포함할 수 있게 하고 각각의 그룹은 서로 다른 실행조건, 결과 선택 방법에 의해 아무것도 안 나오거나 같은 아이템이 한 개 이상 나오거나 여러 아이템이 나오도록 설정할 수 있어야 합니다. 가령 위 시나리오에서 몬스터를 사냥한 다음 이 몬스터로부터 주어지는 보상은 두 그룹으로 구분되는데 그룹 1은 표준 보상을 지급하되 그때그때 수량을 조금식 조절해 서로 다른 수량이 나오도록 설정할 수 있습니다. 철광석은 항상 그랍되지만 그때그때 수량이 달라지며 돌덩어리는 나올 수도 있고 안 나올 수도 있지만 일단 나오면 항상 3개가 나오도록 설정했습니다. 그룹 2는 플레이어의 상태에 따라 아이템을 드랍할지 말 지를 먼저 결정하는데 플레이어가 원거리 클래스일 경우에 한해 그룹 2를 처리하며 다른 클래스일 때 그룹 2는 아예 실행되지 않습니다. 플레이어가 원거리 클래스라면 낮은 확률로 금덩어리가 떨어지는데 항상 1개만 떨어지도록 설정할 수 있습니다. 그래서 그룹 1과 그룹 2가 서로 다른 조건에 따라 각각 실행되어 서로 다른 아이템을 드랍 할 수 있습니다.

이런 보상 구조는 게임 내 상당히 다양한 영역에 활용할 수 있지만 이 구조를 곧이곧대로 사용할 수 있지는 않습니다. 어떤 보상은 몬스터를 사냥하고 나면 알아서 드랍되고 이를 별도의 인터페이스 없이 바로 먹을 수 있는 반면 스테이지를 클리어 할 때 얻는 보상은 보상을 표시하는 팝업이 화면을 가리기도 하고 또 어떤 보상은 우편함을 통해 먹으며 어떤 보상은 위에서 설명한 대로 여러 보상을 보여준 다음 이들 중 하나를 선택하게 만들어야 할 수도 있습니다. 그래서 이 보상 체계는 아이템을 먹을 수 있다는 측면에서는 게임 내 요구사항 대부분을 커버할 수 있지만 서로 표현 방법이 다른 보상에 적용하기 위해서는 좀 더 생각해볼 여지가 남아 있습니다. 하지만 거기까지 생각하면 너무 길고 복잡해질 수도 있으니 이번에는 서로 다른 보상 전달 방법을 고려하지는 않겠습니다.

erDiagram Reward { RewardId RewardId PK "몬스터에 연결" string Name "개발용 코멘트" RewardGroupId[] RewardGroupId FK "한 보상은 여러 그룹을 포함" } RewardGroup { RewardGroupId RewardGroupId PK string Name "개발용 코멘트" Conditions Condition "{Always | RangeClass }" ProbabilityMethod ProbabilityMethod "{Independent | Combination}" RewardItemId[] RewardItemId FK "한 그룹은 여러 아이템을 포함" } RewardItem { RewardItemId RewardItemId FK "아이템 데이터를 연결" Amount AmountMin "최소 수량" Amount AmountMax "최대 수량" Probability Probability "확률 (십만분율)" } Reward only one to zero or more RewardGroup : includes RewardGroup only one to zero or more RewardItem : includes

위에 설명한 보상 시나리오는 이런 모양으로 나타낼 수 있습니다. 이 그림이 다이어그램의 규칙을 정확히 따라 작성되지는 않았다는 점에 유의해야 합니다. 하지만 의도를 전달하기에는 적당한 수준일 것 같습니다. 먼저 Reward는 어떤 한 가지 보상 상황에 연결될 한 가지 보상 데이터를 구분하는데 사용합니다. 엑셀 테이블에 있는 몬스터 데이터에 이 리워드 구분자를 입력하거나 필드에 몬스터를 배치한 다음 몬스터 인스턴스에 리워드 구분자를 입력하는 방식으로 몬스터에 보상 데이터를 연결할 수 있습니다. 리워드 데이터는 사실 그 스스로는 아무 일도 하지 않는데 그저 자신의 하위에 여러 그룹을 포함할 수 있고 또 개발자가 이 데이터가 어떤 용도로 사용되는 보상인지 파악하도록 하기 위한 설명을 기입하는 공간이 있습니다.

Reward 데이터에는 여러 그룹이 포함될 수 있는데 그냥 그룹이라고 하면 게임에 등장할 여러 다른 그룹과 헛갈릴 수 있으니 접두사를 붙여 RewardGroup이라고 하겠습니다. 이런 모든 데이터가 엑셀 파일 모양으로 입력되던 시대에는 이런 구분이 중요했는데 여러 엑셀 파일이 모여 있는 디렉토리에 그냥 Group.xlsx가 있으면 아무도 이 그룹이 무슨 그룹인지 파악할 수 없을 겁니다. 그래서 데이터 이름에 접두사를 붙여 파일 단위로 볼 때 구분하기 쉽게 만들곤 하는데 만약 이런 데이터구조를 한 번에 표현할 수 있는 XML, JSON, 언리얼 데이터에셋 같은 다른 방식을 사용한다면 굳이 이런 접두사 규칙을 사용하지 않아도 됩니다. 리워드 그룹은 이제 본격적으로 보상 집행에 관련된 옵션을 포함하는데 먼저 이 그룹을 실행할 조건을 입력합니다. 여기서는 앞에서 플레이어가 원거리 클래스일 때 보상을 지급하는 경우가 있었으니 조건을 ‘항상’과 ‘원거리 클래스일 경우’의 두 가지로 구분했습니다. 이 조건 역시 복잡해질 수 있는데 여기서는 한번에 한 가지 조건만을 사용한다고 하겠습니다. 다음으로는 확률을 사용할 경우 확률 연산 방법을 설정합니다. 여기서는 그룹에 포함된 아이템 하나하나의 확률을 각각 계산해 아이템 하나하나를 드랍할지 말지 결정하는 방법과 그룹에 포함된 아이템에 설정된 확률을 모두 합한 다음 이들 중 어느 하나만 드랍하도록 설정할 수 있습니다.

RewardItem은 앞에서 설명한 접두사 규칙에 따라 앞에 Reward를 붙였습니다. 이제 한 그룹에 포함될 여러 아이템을 입력하는데 이 데이터는 아이템 하나가 가지고 있을 데이터 목록입니다. 먼저 실제로 어떤 아이템을 드랍할지 입력해야 하는데 여기서는 아이템 데이터를 직접 가리키고 있습니다. 즉 어떤 연산 과정에 따라 아이템을 드랍하기로 결정하면 여기서 가리키고 있는 실제 아이템 데이터를 읽어 온 다음 아이템을 생성해 플레이어에게 드랍하게 됩니다. 최소, 최대 수량은 이 둘을 같게 설정하면 항상 같은 수량만 드랍되고 이들을 달리 설정하면 이 두 숫자 사이의 수량이 드랍 됩니다. 한번은 두 숫자가 서로 다를 때 어떤 분포에 따라 드랍 될 수량이 달라지도록 만들 필요가 있을지 고민해 봤는데 정확한 의도가 있지 않은 이상 그럴 필요는 없을 것 같습니다.

마지막으로 실제 확률을 입력하는데 이 확률은 RewardGroup.ProbabilityMethod 설정에 따라 다른 방식으로 연산 됩니다. 만약 독립 확률이라면 아이템마다 십만분율로 설정된 확률을 연산해 아이템 각각을 드랍할지 말지를 결정합니다. 그래서 목록에 포함된 여러 아이템 중 일부만 드랍 되거나 전부 다 드랍될 수 있습니다. 만약 모든 확률을 합쳐서 연산할 경우 한 그룹에 포함된 아이템 각각에 설정된 확률의 합계가 십만분율 기준으로 십만에 딱 맞아야 합니다. 종종 이 제한이 불편해서 그냥 아무 숫자나 입력하고 이들 전체의 합에서 아이템 하나의 확률을 계산하는 식으로 제한을 완화해 봤는데 확률 설정에 실수가 쉽게 일어나 이전과 같이 합계가 항상 십만이 되도록 되돌린 적이 있습니다. 이 경우 십만분율을 계산한 다음 이에 해당하는 아이템 한 종류만 드랍 하게 됩니다.

확률에 왜 하필 십만분율을 사용하는지는 잘 모르겠습니다. 어느 순간 확률을 적용하기 시작할 때 십만분율을 사용하던 것을 보고 따라 하기 시작했는데 뚜렷한 이유는 없지만 이보다 작은 숫자는 낮은 확률을 충분히 표현할 수 없을 것 같고 이보다 더 큰 숫자는 그렇게까지 낮은 확률을 높은 정확도로 표현해도 한정된 시행 횟수 안에서는 별 의미가 없어 보였습니다. 그래서 정확한 이유는 모르겠지만 지금도 경험적으로 십만분율을 사용하고 있습니다.

지금까지 이전에 설명했던 보상 시스템을 그림으로 그려 다시 설명해 보았습니다. 또 한 1년 넘게 지나면 현재 버전도 마음에 안 들어 다시 설명해 보려고 할 수도 있겠지만 일단 그 때가 오기 전 까지는 이 정도로 괜찮지 않을까 싶습니다. 아니면 다음 번에는 기획서를 작성할 때와 비슷하게 데이터구조 뿐 아니라 UI를 포함해서 설명해도 재미있을 것 같은데 그건 시간이 지난 다음 시도해 보겠습니다.