들어가며
최근의 나는 <Lib's Rarry>라는 프로젝트에서 활동하고 있다.
<Lib's Rarry>는 2024 넥슨 대학생 게임잼에서 시작된 게임인데, 당시 주제 적합성, 발표 등 개인적인 부족함에 팀원들에게 미안한 마음을 갖게 됐고, 이에 속죄 아닌 속죄를 하고자 시작한 프로젝트라고 할 수 있다. 물론 그 이전에 가능성을 느꼈기에 시작한 프로젝트이기도 하고 말이다.
아무튼 게임잼 이후 8월부터 작업을 시작하여 현재는 아래와 같이 나름대로 잘 진행 중이다.


이 이야기를 갑작스레 왜 하느냐? 바로 오늘, <Lib's Rarry>의 효과 밸런싱 기준을 찾는 과정에 대하여 정리할 것이기 때문이다.
현재 <Lib's Rarry>에는 아래와 같이 칼날, 흡혈, 축적, 화상, 가시라는 5가지 효과가 있다.

나의 목표는 이 효과들이 수치 1당 어느 정도의 가치를 갖는지 환산하는 것으로, 이를 위해 자료 조사, 범위와 기준 설정, python을 통한 기댓값 계산과 시뮬레이션 등 여러 가지를 진행해 봤다. 본 글에서 이들을 하나하나 같이 확인해 보도록 하자.
목차는 아래와 같다.
목차
- 무엇을 하려고 했는가?
- 어떻게 조사했는가?
- 어떻게 가치와 기준을 설정했는가?
- 어떻게 기준을 찾고 검증할 것인가?
- Python 검증 과정
- 결론
- 포스트모템
게임 효과의 밸런싱 기준 찾기
무엇을 하려고 했는가?
이전에 <뚜두 농장> 프로젝트를 진행하면서 데이터 테이블과 함께 밸런싱 시트를 만든 적이 있다. 당시 만들었던 것들은 아래와 같은데, 열심히 만들어보기는 했으나 '이것들이 실제로 효과적이었는가?'라고 묻는다면 섣불리 답할 수 없을 것 같다.
그 이유는 단순하게도 직접 플레이하고 테스트하기 전에 프로젝트가 중단됐기 때문이다. 다만, 한 가지 기억에 남는 것은 당시에 부분적으로 구현된 몇 가지 시스템을 대상으로 테스트를 했을 때 오차율이 최대 20%까지 발생하는 것을 확인했던 기억이 있다.



당시에는 능력 부족을 비롯해 이런 저런 핑계를 대며 이 정도면 충분하다고 위안 삼고 넘어갔다. 그런데, 이를 지금 생각해 보면 능력이 부족했던 게 아니라 고민과 아이디어가 부족했던 것이라는 생각이 든다.
그때의 나는 단순하게 재화별 환산 가치를 정하고, 그걸 기반으로 유저의 행동을 '불완전하게' 예측하여 가치를 추산했는데 잘 생각해 보면 20%의 오류는 이 '불완전한 예측' 때문에 생긴 오류였다.
여기서 불완전하다는 것은 유저의 플레이 방식, 즉 가능성을 무시하고 오로지 최고 효율로만 계산을 했기에 불완전하다는 것이다.
물론 최고 효율이 하나의 기준으로써 기능할 수 있기는 하지만, 최저 효율과 함께 다룰 것이 아니라면 대다수 유저에게 온전한 경험을 전달하지 못하겠다는 생각을 했다. 더군다나 <뚜두 농장>은 힐링 모바일 게임인만큼 효율이 아닌 중위 유저에게 맞출 필요가 있기도 했고 말이다.
그래서 이번에는 다음과 같은 것을 해보고 싶었다.
내가 설정하는 기준을 통해 게임 시스템과 각 메커닉에 대한 유저의 행동 바운더리를 정의하고, 이 바운더리의 평균을 가치의 기준으로 삼아, 보다 일반적이고 정확한 밸런싱을 해보자.
이렇게 적고 보니 거창한데, 요약하면 유저가 할 수 있는 행동의 평균을 기준으로 밸런싱을 하자는 내용이다.
혹시나 싶어 말하지만 무엇이 기준이 되는지는 기획 의도와 목적에 따라 달라질 수 있기에 유저 행동의 평균이 절대적인 기준이라고 오해하지는 않았으면 좋겠다.
내가 이렇게 하는 이유는 출시 이후, 내가 예상한 유저 행동 바운더리와 실제 행동 바운더리 사이에서의 괴리를 확인하고, 바운더리 내에서 어느 수준을 기준으로 밸런싱 했을 때 반응이 가장 좋았는지 체감하기 위해서 유저 행동의 평균 예상 값을 기준으로 밸런싱을 진행하는 것이다.
이렇게 설명한 내용을 정리하면 아래와 같다.
정리
- 과거 <뚜두 농장> 밸런싱에서는 최고 효율만을 기준으로 삼았기에 오차가 컸다.
- 이번 <Lib's Rarry> 밸런싱에서는 유저 행동 바운더리를 정의하여 그 평균을 기준으로 삼으려고 한다.
- 이렇게 설정한 밸런싱에 대한 실제 유저들의 행동과 반응으로 밸런싱 감각을 키우고자 한다.
어떻게 조사하고 시도했는가?
빠르게 변화하는 게임 업계에 어울리는 것 같지는 않지만, 나는 책을 통해 자료를 조사하는 걸 선호한다. 그래서, 집에 게임 디자인에 관한 책이 꽤 많은데 신기하게도 밸런싱만을 다룬 책은 없다. 사실 국내에서 찾기도 힘들고 말이다.
그런 와중 운좋게 최근에 게임 밸런싱만을 다룬 책이 출간됐다는 걸 알 수 있었다. 바로 '게임 밸런스 수치 기획 바이블'이라는 책이다.
게임 밸런스 수치 기획 바이블 | 위안자오양 - 교보문고
게임 밸런스 수치 기획 바이블 | 위대한 게임을 만드는 기획의 핵심은 감이 아니라 데이터 게임 기획에서 비중이 커지고 있는 수치 기획을 전문적으로 다루는 최초의 책이다. 준비 작업, 전투 수
product.kyobobook.co.kr
바로 구매한 뒤 읽어봤는데 인상 깊었던 부분부터 말해보겠다.
게임 역시 복잡한 시스템의 집합으로, 여러 기능, 플레이, 성장 조합으로 구성돼 있다. 기능, 플레이, 성장은 여러 수치 구조로 연결돼 하나의 게임을 형성한다. 이 복잡한 시스템 조합은 모듈화하는 방식으로 분류 설계하고 집중 관리할 수 있다.
- 위안자오양. 게임 밸런스 수치 기획 바이블. 제이펍, 2024. p.239. -
모듈을 통해 체계적으로 게임의 수치를 구축할 수 있기 때문에 모듈화 방식으로 수치를 구축한다. 수치 설계에서 모든 수치 모델을 차례대로 연결해 수치 모델 사이클을 구성하고 게임에 좋은 수치 경험을 제공하는 작업이 가장 복잡한 부분이다.
- 위안자오양. 게임 밸런스 수치 기획 바이블. 제이펍, 2024. p.251. -
처음 읽었을 때는 굉장히 신기했다. 과거 'Reddit'에 게임 디자인 과정에서 각 시스템을 모듈화해서 관리할 수 있겠냐는 글을 올린 적이 있는데, 게임 디자인을 모듈화라는 관점에서 접근한 사람이 더 있다는 게 신기했다.
From the gamedesign community on Reddit
Explore this post and more from the gamedesign community
www.reddit.com
더군다나 이렇게 모듈화한 시스템을 정리하여 '아카이브'라는 프로젝트로 관리하고 있었는데, 저자는 이게 작업 커리어를 쌓는데 중요한 과정이라고 적은 것을 보면서 인정을 받은 느낌이라 기분이 좋았다. (아니면 내가 우물 안 개구리였던 걸 수도.. 😅😅)


아무튼 글을 쓰면서 기분이 좋아져서 주제를 약간 벗어나게 됐는데, 각설하고 정리하면 이 책을 통해서 각 효과 콘텐츠를 모듈화하여 검증해 보자는 생각을 할 수 있었다.
그리고, 이 과정에서 전체적인 유저 행동의 바운더리를 설정하기보다는 각 효과 사용에 대한 유저 행동 바운더리를 설정하여 효과마다 개별적인 가치 판단이 가능하도록 구성하자는 생각을 떠올릴 수 있었다.
전체적인 유저 행동을 예측하려니 머리가 복잡했는데 부분으로 나눠 생각하니 확실히 간단했다. 개별적인 가치 판단 이후 공통 기준으로 묶어주기만 하면 됐다.
이 외에도 '게임 시스템 디자인 입문'의 밸런싱 파트를 읽었는데 범위 밸런싱과 데이터 지렛목이라는 개념을 통해 유저 행동의 바운더리(범위)를 설정하고 그 범위의 평균을 기준으로 삼아 밸런싱한다는 개념을 구체화할 수 있었다.
게임 시스템 디자인 입문 | 댁스 개저웨이 - 교보문고
게임 시스템 디자인 입문 | 초보 게임 디자이너와 개발자가 꼭 알아야 하는 게임 기획 실무의 모든 것! 〈스타워즈〉 게임 시리즈와 〈마블 얼티밋 얼라이언스 2〉 등 유명 게임 타이틀을 수십
product.kyobobook.co.kr
이 외에도 심심할 때 유튜브에서 아래와 같은 영상들을 보면서 게임 디자이너들이 밸런싱에 대해 어떻게 이야기하는지 들어봤다.
- Game Maker's Toolkit - 게임의 밸런스를 맞추는 법 | Game Maker's Toolkit
- GDC - Slay the Spire: Metrics Driven Design and Balance
- GDC - Board Game Design Day: Balancing Mechanics for Your Card Game's Unique Power Curve
참고로, 위의 영상들은 밸런싱의 기준을 찾는 법에 대해 알려주기보다는 밸런싱에 대한 관점을 이야기하는 것이 주가 되는 영상이기에 이 점 인지하고 시청하면 좋을 것 같다.
이쯤하고 내용을 정리하면 다음과 같다.
정리
- '게임 밸런스 수치 기획 바이블'과 '게임 시스템 디자인 입문' 책을 중심으로 자료 조사를 진행했다.
- 각 효과 수치 디자인을 모듈화하여 공통 기준은 하이퍼 파라미터로 두고, 각 효과 내부에서 효과 가치를 평가할 기준 파라미터를 만들어 개별적으로 검증해 보자 생각을 했다.
- 범위 밸런싱과 지렛목 개념을 통해 각 효과 사용에 대한 유저 행동의 바운더리(범위)를 설정하고, 그 평균 값을 기준(지렛목)으로 두어 밸런싱하자는 아이디어를 구체화할 수 있었다.
어떻게 가치와 기준을 설정했는가?
자료 조사 이후에는 가장 먼저 가치 환산표를 만들었다. <Lib's Rarry>에는 특별한 재화가 없기에 전투에 한정하여 체력, 방어도, 행동력을 1차 재화로 두고 가치 환산 비율을 정의했다.

1차 재화 외에도 플레이어가 4가지 기술을 가질 수 있고, 또 그 기술을 강화할 수도 있기에 이런 기술 항목을 2차 재화로써 밸런싱 해야 겠지만, 이 글의 목적은 효과의 밸런싱 기준을 찾는 것이기에 이번 글에서는 넘어가도록 하겠다.
가치 환산표를 만든 이후에는 무엇을 공통 기준(게임 전체의 하이퍼 파라미터)으로 설정할 것인지 정리했다. 여기서 효과 가치에 영향을 미치는 것은 승리 기준 턴(일반적으로 몇 턴에 게임이 끝난다고 가정할 것인지)과 턴당 공격 횟수(일반적으로 턴당 몇 번 공격한다고 가정할 것인지)인데 이 둘을 기억하고 다음으로 넘어가자.
그럼 어떻게 효과 가치 기준을 찾고 환산했는가? 이는 아래와 같다.
즉각적으로 가치를 환산할 수 있는 경우, 그대로 처리했다.
- 흡혈 : 체력 피해를 받을 시 공격자의 체력이 해당 수치만큼 회복되고 효과는 사라진다.
흡혈 수치 1당 1의 체력이 회복되므로 체력 가치와 동일하게 설정했다. - 가시 : 매 피격 시 공격자에게 해당 수치만큼 피해를 준다. 턴 시작 시 효과가 사라진다.
가시 수치 1당 다음 턴까지 매 피격마다 1의 피해를 주기에 턴당 공격 횟수 x 체력 가치로 설정했다.
유저 패턴에 따라 가치가 달라지는 경우, 기준을 세워 처리했다.
- 칼날 : 피격 시 해당 수치만큼 추가 피해를 입는다.
한 번 쌓인 수치는 사라지지 않으므로 초반에 부여하는 것이 가장 효율이 좋고(가치 있고), 점점 가치가 떨어지게 된다. 따라서, 특정 시전 턴 비율을 기준으로 승리 기준 턴 x 시전 턴 비율 (기준) x 체력 가치로 설정했다.
예 ) 승리 기준 턴이 10이고 시전 턴 비율이 0.5(평균)인 경우, 칼날 수치는 1당 5의 체력 가치를 갖는다. - 축적 : 공격 시 해당 수치의 3배만큼 추가 피해를 준다. 턴 시작 시 해당 수치만큼 피해를 받는다. 공격 시 효과가 사라진다.
효과 부여 시점으로부터 2 턴 내에 공격 시 이득이고, 3 턴 이후부터는 손해다. 따라서, 특정 발동 턴 오프셋을 기준으로 (3 - 발동 턴 오프셋 (기준)) x 체력 가치로 설정했다.
예 ) 발동 턴 오프셋이 1인 경우(효과 부여 이후 1턴 뒤에 발동한다고 가정하는 경우), 축적 수치는 1당 2의 체력 가치를 갖는다.

나는 이런 식으로 가치를 추산했다. 여기서 눈썰미가 좋은 독자들은 한 가지 특이한 점을 발견했을 것이다. 바로, 화상 효과에 대한 가치가 설정되지 않았다는 점이다.
화상 효과를 따로 뺀 이유는 화상 효과의 효율이 독립적이지 않다고 생각했기 때문이다. 위에 표기한 네 가지 효과를 살펴보면 모두 효과 수치 1에 대한 가치 계산이 가능하다는 것을 확인할 수 있다.
흡혈의 경우는 수치 그 자체가, 가시의 경우는 턴당 공격 횟수가, 칼날의 경우는 남은 턴 수가, 그리고 축적의 경우는 발동까지의 턴 수가 그 가치와 직결된다. 수치 1을 부여하든 10을 부여하든 이는 달라지지 않는다.
그러나, 화상 효과는 다르다고 생각했다. 화상 효과는 턴마다 화상 수치만큼 데미지를 입히고 그 수치가 절반으로 감소하기에, 첫 턴에 24만큼 부여하는 경우와 네 턴에 걸쳐 6씩 부여하는 경우의 데미지가 다르다고 생각했다.

..뭐라고? 같지 않냐고? 맞다. 한 번에 24를 부여하든 네 턴에 걸쳐 6씩 부여하든 최종 데미지는 같다. 그러나, 그때의 나는 왜인지 모르게 그냥 다르다고 착각했다. 이 때문에 화상 효과의 가치를 환산하기 어렵다고 생각했고, 파이썬을 통해 기준을 찾아 가치를 환산할 수식을 찾아보자는 생각을 했다.
그렇다. 이게 내가 삽질을 하게 된 원인이다. 사실 무언가 안 된다고 생각되면 기획을 살짝만 바꾸면 되긴 한데, 앞으로 게임 디자인을 하면서 이런 문제를 다시 맞닥뜨리지 않을 거라는 보장을 할 수 없었기에, 이번에 한 번 해결해 보자는 생각을 했다.
의도는 좋았지만 ..부주의함과 근성이 낳은 삽질이라고 할 수 있겠다.. 이런 연유로 불필요하게 파이썬을 통해 화상 효과의 기준을 찾고 검증하는 과정을 거치게 됐다.
정리
- 각 효과를 모듈화한 뒤, 공통 기준과 판단 기준을 엮어 가치를 추산했다.
- 화상 효과에 대한 데미지가 효과를 어떻게 부여하느냐(분산)에 따라 달라진다고 착각하여 독립적이지 않다고 생각했다.
- 파이썬을 통해 화상 효과의 기준을 찾고 검증하고자 했다.
어떻게 기준을 찾고 검증할 것인가?
효과 수치의 기준을 검증하는 방법은 단순했다. 그냥 아이디어가 떠오를 때마다 해당 아이디어에 대한 기댓값을 구했고, 따로 시뮬레이션을 진행하여 기댓값과 비교해 보며 아이디어를 검증했다.
이 과정에서 몇 가지 함수를 정의한 뒤 반복해서 사용했는데 이는 아래와 같다.
- u(t) : 유저가 t턴에 부여하는 효과 수치
- e(t) : t턴에서의 효과 수치 (초반에는, b(t)로 표기함)
- D(t) : t턴까지의 효과에 의한 데미지 (burn_expected, total_damage 등으로 혼용해 표기함)
이들 사이의 관계는 $D'(t) = e(t) \times \text{효과 1당 피해량} = \{e(t-1) + u(t)\} \times \text{효과 1당 피해량}$
로 정리할 수 있다.
파이썬으로 기준을 찾고 검증하는 모든 과정은 계획적으로 한 게 아닌 생각이 가는대로 했기에, 이름이 조금 다르거나 두서없는 부분이 있을 수 있다. 이 점 이해 바란다.

개인적으로 수식이나 검증하는 과정이 복잡하면 작업자에게도, 실용성 측면에서도 좋지 않다고 생각한다.
이러한 점에서 혹자는 '밸런싱하는데 뭔 미적분이냐?'라는 이야기를 할 수 있을 것 같은데, 앞서 말한대로 지망생 입장에서의 '삽질' 기록이며, 필자가 밸런싱을 하는 방법을 몰라서 그런 거니 더 좋은 방법이나 의견이 있다면 댓글로 조언 부탁한다.
마지막으로, 이 모든 과정에서 ChatGPT의 도움을 많이 받았음을 알린다. 전자공학부를 나왔다고는 하지만 수학적인 개념이 많이 부족하기에, 어떤 아이디어를 검증하기 위한 개념들을 찾고 특성을 이해하는데 주로 사용했다.








+ 2025.01.17 추가)
지금 보니까 어지럽네.. 분산 값이 작은 경우는 한 턴에 30 부여처럼 데이터가 평균 값에 몰린 경우고, 분산 값이 큰 경우는 데이터들이 오히려 평균에서 멀리 떨어져 U자형 그래프가 되는 경우지.. 상원아.. 이런 거 하기 전에 개념부터 제대로 챙기자..
정리
- 순간적인 아이디어를 따라 수식을 세우고, 기댓값과 시뮬레이션 값을 비교하며 검증을 진행했다.
- 수식을 세우고 검증하는데 ChatGPT를 활용했다.
- 처음이라 많이 미숙하니 이해바란다. (이 사람은 얼마나 멍청하게 삽질하는지 같이 즐겨보자.)
Python 검증 과정
가장 먼저 공통 기준으로 전투 종료 턴을 정한 뒤, 유저 입력 2가지를 만들어 테스트해 보며, 분산에 따라 최종 데미지가 달라지는지 여부를 확인했다.
그 뒤에는 화상 효과 수식, $e(t) = \frac{e(t-1)}{2}+u(t)$를 일반항으로 풀어 $e(t) = \sum_{i=0}^{t} 2^{t-i} u[i]$를 구했고, 이 수식으로 기댓값을 계산하여 시뮬레이션 결과와 비교했다.



한 번에 24 효과를 부여했을 때와 네 턴에 걸쳐 6 효과를 부여했을 때의 데미지를 구해 비교해 봤는데 당연히 같았다. 이를 통해 우리는 분산 값이 총 데미지에 영향을 미치지 않으며, 화상 효과는 독립적으로 가치를 추산할 수 있다는 걸 알게 됐다.
약간의 오차가 발생하는 건 시뮬레이션 과정에서 계산 결과가 일부 버려져 발생하는 것 같다.
그런데, 처음 테스트할 때는 내 실수로 값을 잘못 넣어 확인했다. 그냥 테스트 순간에 머리가 이상해져서 그런 건지는 모르겠는데 한 번에 72를 넣은 것과 6턴에 걸쳐 6의 값을 넣은 것(이래놓고 72라고 착각함🤔)을 비교하면서 값이 다르다고 분산이 예상대로 큰 영향을 미친다고 생각했다.
이게 내 삽질의 시작이었다. 정확한 값을 입력했는지 확인해야 됐는데 그냥 좋다며 그대로 진행해버렸다. 비록 이미 결론이 나왔지만.. 멍청한 나를 여러분의 반면교사로 삼을 수 있도록 틀린 가치를 찾고 검증하는 과정을 같이 한번 살펴보도록 하자.. 😂😂
분산이 총 데미지에 영향을 준다는 것을 확인(착각)한 이후에는 내가 $u(t)$, 즉 유저 입력을 만들어서 $u(t)$에 따라 분산과 최종 데미지가 어떻게 되는지 확인하는 환경을 만들어봤다.


이렇게 $u(t)$에 따른 결과를 확인한 이후에는 numpy 라이브러리의 base_repr 메서드로 공통 기준 내에서 가능한 모든 $u(t)$를 만들어 분산과 최종 데미지 사이의 연관성을 확인했다.


턴마다 부여할 수 있는 효과 수치를 정해두고 모든 t에 대한 u(t) 값을 0에서 해당 수치까지 중 하나로 설정하게 구현했다.
구현 방식은 base_repr 메서드로 첫 번째 인자 i를 base로 들어온 두 번째 인자에 해당하는 진법의 문자열로 변환한 뒤, 남은 자릿수를 zfill 메서드를 통해 0으로 채워준다. 그 뒤 int 형으로 변환하고 np.array 메서드를 통해 numpy 배열로 변환하도록 구현했다.
예를 들어, step 19에서는 turn_grantable_value가 2이니 3진법으로 $2\times3^2 + 1\times3^0 = 201$이 되고, zfill 메서드에 의해 turn_criteria 크기만큼 0으로 채워져 $0000000201$이 된다.
여기서 이 문자열이 하나씩 int로 변환되어 numpy 배열이 되니 최종적으로 $[0, 0, 0, 0, 0, 0, 0, 2, 0, 1]$이라는 $u(t)$를 얻게 되는 것이다.
이런 식으로 하나씩 순회해서 $[2, 2, 2, 2, 2, 2, 2, 2, 2, 2]$까지 확인하게 된다. 위의 우측 이미지가 그 결과인데.. 혹시 분산과 데미지의 연관성이 보이는가?
물론 보일리가 없다. 하지만 내가 누구인가.

나는 고집의 AeonFlor. 결과를 믿지 않고, 내 고집에만 집중하느라 테스트가 잘못됐다고 생각했다 🤗🤗🤗 (답이 없네..)
위의 순회 테스트는 공통 기준 값이 커지면 바로 메모리 부족으로 터져서 제대로 확인이 불가했는데, 그럴거면 데이터 샘플링을 통해 랜덤으로 생성한 $n$개의 $u(t)$를 대상으로 결과를 확인해 보자는 생각을 했다.
10000개의 u(t)를 생성해서 테스트한 결과는 아래와 같다.


결과는 잘 나왔다. 어? 그런데 분석하려고 보니까 이래서는 제대로 된 분석이 안 된다. 왜냐하면 turn_grantable_value 때문에 턴마다 사용 가능한 수치가 제한되기 때문이다.
이렇게 되면 최대로 사용했을 때 $[2, 2, 2, 2, 2, 2, 2, 2, 2, 2]$과 같이 효과 사용에 상한이 걸리게 된다. 나는 한 턴에 폭발적으로 효과를 적용시키도록 유도하고 싶기에 위와 같이 턴당 부여 가능한 효과 수치(turn_grantable_value)라는 기준은 부적합하다.
따라서, 턴당 부여 가능한 효과 수치를 삭제하고, 전투 중 부여할 효과 수치 총합(total_grant_effect)을 추가해 다시 생성해봤다.


여기에서는 random disribution을 통해 효과 수치 총합을 랜덤하게 $u(t)$로 분배해 $[0, 20, 0, 0, 0, 0, 0, 0, 0, 0]$과 같이 극단적인 꼴도 확인할 수 있게 구성해 봤다.
이쯤 되면 이런 생각이 든다.
근데 분산이랑 데미지랑 연관 없다면서 이건 언제 확인함?
나도 모르겠다. 과거의 이 사람은 구현하는데 정신이 팔렸다. 이를 돌아보건데 이때의 나는 이미 분석 따위는 눈에 들어오지 않는 상태였던 것 같다.
대충 돌아가는 것 같으니 일단 구현하고 봤던 것 같은데.. 그래도 일말의 양심은 있었는지 random distribution이 아닌 random.normal 메서드를 통해 분산 값으로 총합이 total_grant_effect인 $u(t)$를 만들어 확인해 봤다.
그리고, 이렇게 분산을 통해 $u(t)$를 만들 수 있는 걸 확인한 이후에는 분산이 0일 때부터 4일 때까지 $u(t)$를 생성 후 총 데미지를 계산하여 아래와 같이 확인해 봤다.


이렇게 확인해 봐도 분산은 총 데미지와 연관이 없다. 하지만 나는 여전히 잘못 확인한 전제 조건만 믿고 정신 차리지 못했다...
..라는 게 원래 쓰려던 글의 내용이었는데, 지금 다시 확인해 보니까 분산에 따라 $u(t)$가 제대로 생성되지도 않네..

처음이니까.. 나는 처음이니까.. 이해 바란다😪. 이제는 마음 놓고 이 인간이 어디까지 삽질을 하나 계속 따라가 보자. (원래 처음에 이미 결론이 나왔다. 모든 과정이 삽질인데 더 멍청한 삽질이 됐을 뿐이다. 온 세상이 트루먼 쇼였던 거임..!)
아무튼 그 뒤에는 목표 분산에 대한 $u(t)$가 제대로 생성되는지 확인하다가, 분산이 커지면 $u(t)$가 음수가 나오는 경우가 생겨서 truncated normal distribution으로 바운더리를 지정해서 0 이상의 값을 갖도록 수정했다.


여기에 더해 분산이 극단적으로 작은 경우, 내 예상으로는 한 턴에 값이 몰려 있어야 정상인데, truncated normal distribution의 경우는 오히려 더 골고루 분포됐다.
당시에는 그냥 다른 방법을 찾자고 넘어갔는데 지금 와서 이유를 고민해 보면 분산이 t에 대한 분산이 아니라 최종 데미지에 대한 분산이어서 mean 값으로 들어간 3으로 밀집됐다는 생각이 든다. (이런 바보)
이는 ChatGPT의 도움을 받아 dirichlet distribution이라는 것을 사용하여 조정자를 통해 분산을 제어해 해결할 수 있었다. (사실 분산 축만 바꿔서 개선해도 됐을 텐데..)


이후에는 시뮬레이션 과정에서의 내림 처리로 인해 dirichlet distribution의 총합이 보장된다는 특성이 무용해져서, 이것저것 시도해 보다가 결국 반올림을 통해 총합에 근사하도록 수정했다.

위는 0에서 4까지의 variance control 값으로 dirichlet distribution을 통해 $u(t)$를 생성해서 그 결과를 확인한 그래프이다. 여기에 와서야 내 머릿속에는 다음과 같은 의문이 떠올랐다.
어? 이거 왜 자꾸 분산이랑 데미지랑 상관이 없다고 나오지..?
왜긴 왜야. 상관이 없으니까 상관이 없다고 나오지.
와! 드디어 내 고집이 꺾일 때가 왔다. 마지막으로 하나의 variance control 값에 대해 샘플링해서 결과를 확인해 봤다.


결과는?

이때 정신이 아찔해졌다. 빠르게 돌아가서 처음부터 다시 확인을 하는데 첫 전제부터 잘못 됐더라. 72와 36을 비교해 놓고 결과가 다르다고 분포에 따라 총 데미지가 영향을 받는다고 했던 부분. 바로 거기가 내 패인이다.

그래.. 그렇다. 내 삽질은 이렇게 끝이 났다.
나는 무엇을 위해...
아무튼 틀린 건 틀린 거고 확인할 건 확인해야지. 나는 파이썬 가지고 노는.. 걸 멈추고, 차분히 생각해 봤다.
엥.. 근데 분포에 상관없이 총 데미지가 효과 입력 수치의 2배로 수렴하네..? 왜 이러지?
얘야.. $\lim_{n \to \infty} \left(1 + \sum_{t=0}^{n} \left(\frac{1}{2}\right)^t\right) \approx 2$란다.

그래서, 마지막으로 공통 기준으로 턴 종료 기준(turn_criteria), 효과 가치 환산 기준으로 화염 효과 기본 데미지 (effect_base_damage) , 턴당 감쇠율(decay_rate)로 $d(t) = \text{effect_base_damage} \times \left(\frac{1 - (\text{decay_rate})^{\text{decay_duration}}}{1 - \text{decay_rate}}\right)$와 같은 수식을 만들어 아래와 같이 기댓값을 계산하고 시뮬레이션 값과 비교해 봤다.

+ 2025.01.17 추가)
상원아.. 너가 한 건 기댓값 계산과 시뮬레이션이 아니란다.. 그냥 같은 식을 다르게 계산한 거잖아. 확률이 없는데 기댓값 계산과 시뮬레이션이 무슨 의미니.. 부끄러워부끄러워부끄러워
이게 끝이다.
정리
- 삽질함.
결론
화상 효과는 턴 종료 기준(turn_criteria), 효과 가치 환산 기준으로 화염 효과 기본 데미지 (effect_base_damage) , 턴당 감쇠율(decay_rate)로 $\text{effect_base_damage} \times \left(\frac{1 - (\text{decay_rate})^{\text{decay_duration}}}{1 - \text{decay_rate}}\right) \times $체력 가치로 가치를 환산할 수 있다.
- 유저 바운더리를 정의하고 테스트할 수 있는 환경을 구축했다.
- 수식을 통한 기댓값 계산과 시뮬레이션을 진행하며 테스트 경험치를 쌓았다.
- 이전과 같이 엑셀에 테스트 및 밸런싱 환경을 일일이 구축하지 않고, 파이썬으로 1차 검증 후 엑셀로 옮기는 작업 방식을 도입했다.
포스트모템
- 전제 조건이 정확한지 확실히 확인하자.
- 테스트 결과가 잘못됐다면 조건을 확인하자. 이상이 없다면 가설이 잘못된 거다.
- 테스트 환경 구축 시 구현과 분석 절차를 분리하자. 구현하면서 분석하려니 한쪽에 소홀해진다.
- 결과를 확정 지어 놓고 테스트를 진행하지 말자. 테스트는 결과를 검증하기 위한 도구이지 멍청한 자기 확신을 위한 수단이 아니다.
- 테스트 과정에서 잘 모르는 개념을 넣어 시도해 보는 건 좋지만 제대로 사용하려면 개념과 원리를 꼭 제대로 이해하자.
마치며
최근 성장기가 다시 찾아왔다. 이에 다소 바보 같더라도 이것저것 시도해 보며 경험치를 쌓는 중이다. 내 현실적인 기획과 나아가야 할 방향도 어느 정도 감이 잡히고 말이다.
오늘의 기록도 그 일환 중 하나였는데 글을 쓰는데 생각보다 오래 걸려서 중간부터, 정확히는 Python 검증 과정부터 내 삽질에 내 스스로 부끄러워져서 반쯤 정신 놓고 기록한 것 같다. 🙄😅
일단 올려두고 늘 그렇듯 조금씩 퇴고하며 개선해 보면 되지 않을까?
늘 게임 디자인 관련 글을 작성해야 한다는 부담감이 있었는데, 순수한 게임 디자인이라기에는 조금 이상하지만 그래도 밸런스 디자인 관련 글이라는 점에서 이 정도면 시작은 했다고 봐도 좋을 것 같다.. 아마두..? 😊😊
혹시나 순수 기획적인 내용을 기대했던 분이 있다면 죄송하다는 말을 전한다. 그래도, 요즘 기획 요청 방식에 대해 이전 기획들을 돌아보며 정리하고 있어서 다음에 이걸 주제로 글을 쓴다면 그건 확실히 기획 관련된 내용일 것 같다.
아마 그전에 <Breath in Winter> 관련된 글로 찾아올 것 같긴 하지만 말이다. 😏
아무튼.. 오늘도 관심을 갖고 내게 다가와, 이 글을 읽어줘서 정말 고맙다. 다시 한번 좋은 글, 좋은 컨텐츠로 보답하겠다.
그럼 안뇽!
'게임 디자인 > 게임 디자인 연구' 카테고리의 다른 글
어드밴티지와 피드백 루프 (3) | 2024.03.16 |
---|---|
전투 디자인 해석 (0) | 2024.02.18 |
들어가며
최근의 나는 <Lib's Rarry>라는 프로젝트에서 활동하고 있다.
<Lib's Rarry>는 2024 넥슨 대학생 게임잼에서 시작된 게임인데, 당시 주제 적합성, 발표 등 개인적인 부족함에 팀원들에게 미안한 마음을 갖게 됐고, 이에 속죄 아닌 속죄를 하고자 시작한 프로젝트라고 할 수 있다. 물론 그 이전에 가능성을 느꼈기에 시작한 프로젝트이기도 하고 말이다.
아무튼 게임잼 이후 8월부터 작업을 시작하여 현재는 아래와 같이 나름대로 잘 진행 중이다.


이 이야기를 갑작스레 왜 하느냐? 바로 오늘, <Lib's Rarry>의 효과 밸런싱 기준을 찾는 과정에 대하여 정리할 것이기 때문이다.
현재 <Lib's Rarry>에는 아래와 같이 칼날, 흡혈, 축적, 화상, 가시라는 5가지 효과가 있다.

나의 목표는 이 효과들이 수치 1당 어느 정도의 가치를 갖는지 환산하는 것으로, 이를 위해 자료 조사, 범위와 기준 설정, python을 통한 기댓값 계산과 시뮬레이션 등 여러 가지를 진행해 봤다. 본 글에서 이들을 하나하나 같이 확인해 보도록 하자.
목차는 아래와 같다.
목차
- 무엇을 하려고 했는가?
- 어떻게 조사했는가?
- 어떻게 가치와 기준을 설정했는가?
- 어떻게 기준을 찾고 검증할 것인가?
- Python 검증 과정
- 결론
- 포스트모템
게임 효과의 밸런싱 기준 찾기
무엇을 하려고 했는가?
이전에 <뚜두 농장> 프로젝트를 진행하면서 데이터 테이블과 함께 밸런싱 시트를 만든 적이 있다. 당시 만들었던 것들은 아래와 같은데, 열심히 만들어보기는 했으나 '이것들이 실제로 효과적이었는가?'라고 묻는다면 섣불리 답할 수 없을 것 같다.
그 이유는 단순하게도 직접 플레이하고 테스트하기 전에 프로젝트가 중단됐기 때문이다. 다만, 한 가지 기억에 남는 것은 당시에 부분적으로 구현된 몇 가지 시스템을 대상으로 테스트를 했을 때 오차율이 최대 20%까지 발생하는 것을 확인했던 기억이 있다.



당시에는 능력 부족을 비롯해 이런 저런 핑계를 대며 이 정도면 충분하다고 위안 삼고 넘어갔다. 그런데, 이를 지금 생각해 보면 능력이 부족했던 게 아니라 고민과 아이디어가 부족했던 것이라는 생각이 든다.
그때의 나는 단순하게 재화별 환산 가치를 정하고, 그걸 기반으로 유저의 행동을 '불완전하게' 예측하여 가치를 추산했는데 잘 생각해 보면 20%의 오류는 이 '불완전한 예측' 때문에 생긴 오류였다.
여기서 불완전하다는 것은 유저의 플레이 방식, 즉 가능성을 무시하고 오로지 최고 효율로만 계산을 했기에 불완전하다는 것이다.
물론 최고 효율이 하나의 기준으로써 기능할 수 있기는 하지만, 최저 효율과 함께 다룰 것이 아니라면 대다수 유저에게 온전한 경험을 전달하지 못하겠다는 생각을 했다. 더군다나 <뚜두 농장>은 힐링 모바일 게임인만큼 효율이 아닌 중위 유저에게 맞출 필요가 있기도 했고 말이다.
그래서 이번에는 다음과 같은 것을 해보고 싶었다.
내가 설정하는 기준을 통해 게임 시스템과 각 메커닉에 대한 유저의 행동 바운더리를 정의하고, 이 바운더리의 평균을 가치의 기준으로 삼아, 보다 일반적이고 정확한 밸런싱을 해보자.
이렇게 적고 보니 거창한데, 요약하면 유저가 할 수 있는 행동의 평균을 기준으로 밸런싱을 하자는 내용이다.
혹시나 싶어 말하지만 무엇이 기준이 되는지는 기획 의도와 목적에 따라 달라질 수 있기에 유저 행동의 평균이 절대적인 기준이라고 오해하지는 않았으면 좋겠다.
내가 이렇게 하는 이유는 출시 이후, 내가 예상한 유저 행동 바운더리와 실제 행동 바운더리 사이에서의 괴리를 확인하고, 바운더리 내에서 어느 수준을 기준으로 밸런싱 했을 때 반응이 가장 좋았는지 체감하기 위해서 유저 행동의 평균 예상 값을 기준으로 밸런싱을 진행하는 것이다.
이렇게 설명한 내용을 정리하면 아래와 같다.
정리
- 과거 <뚜두 농장> 밸런싱에서는 최고 효율만을 기준으로 삼았기에 오차가 컸다.
- 이번 <Lib's Rarry> 밸런싱에서는 유저 행동 바운더리를 정의하여 그 평균을 기준으로 삼으려고 한다.
- 이렇게 설정한 밸런싱에 대한 실제 유저들의 행동과 반응으로 밸런싱 감각을 키우고자 한다.
어떻게 조사하고 시도했는가?
빠르게 변화하는 게임 업계에 어울리는 것 같지는 않지만, 나는 책을 통해 자료를 조사하는 걸 선호한다. 그래서, 집에 게임 디자인에 관한 책이 꽤 많은데 신기하게도 밸런싱만을 다룬 책은 없다. 사실 국내에서 찾기도 힘들고 말이다.
그런 와중 운좋게 최근에 게임 밸런싱만을 다룬 책이 출간됐다는 걸 알 수 있었다. 바로 '게임 밸런스 수치 기획 바이블'이라는 책이다.
게임 밸런스 수치 기획 바이블 | 위안자오양 - 교보문고
게임 밸런스 수치 기획 바이블 | 위대한 게임을 만드는 기획의 핵심은 감이 아니라 데이터 게임 기획에서 비중이 커지고 있는 수치 기획을 전문적으로 다루는 최초의 책이다. 준비 작업, 전투 수
product.kyobobook.co.kr
바로 구매한 뒤 읽어봤는데 인상 깊었던 부분부터 말해보겠다.
게임 역시 복잡한 시스템의 집합으로, 여러 기능, 플레이, 성장 조합으로 구성돼 있다. 기능, 플레이, 성장은 여러 수치 구조로 연결돼 하나의 게임을 형성한다. 이 복잡한 시스템 조합은 모듈화하는 방식으로 분류 설계하고 집중 관리할 수 있다.
- 위안자오양. 게임 밸런스 수치 기획 바이블. 제이펍, 2024. p.239. -
모듈을 통해 체계적으로 게임의 수치를 구축할 수 있기 때문에 모듈화 방식으로 수치를 구축한다. 수치 설계에서 모든 수치 모델을 차례대로 연결해 수치 모델 사이클을 구성하고 게임에 좋은 수치 경험을 제공하는 작업이 가장 복잡한 부분이다.
- 위안자오양. 게임 밸런스 수치 기획 바이블. 제이펍, 2024. p.251. -
처음 읽었을 때는 굉장히 신기했다. 과거 'Reddit'에 게임 디자인 과정에서 각 시스템을 모듈화해서 관리할 수 있겠냐는 글을 올린 적이 있는데, 게임 디자인을 모듈화라는 관점에서 접근한 사람이 더 있다는 게 신기했다.
From the gamedesign community on Reddit
Explore this post and more from the gamedesign community
www.reddit.com
더군다나 이렇게 모듈화한 시스템을 정리하여 '아카이브'라는 프로젝트로 관리하고 있었는데, 저자는 이게 작업 커리어를 쌓는데 중요한 과정이라고 적은 것을 보면서 인정을 받은 느낌이라 기분이 좋았다. (아니면 내가 우물 안 개구리였던 걸 수도.. 😅😅)


아무튼 글을 쓰면서 기분이 좋아져서 주제를 약간 벗어나게 됐는데, 각설하고 정리하면 이 책을 통해서 각 효과 콘텐츠를 모듈화하여 검증해 보자는 생각을 할 수 있었다.
그리고, 이 과정에서 전체적인 유저 행동의 바운더리를 설정하기보다는 각 효과 사용에 대한 유저 행동 바운더리를 설정하여 효과마다 개별적인 가치 판단이 가능하도록 구성하자는 생각을 떠올릴 수 있었다.
전체적인 유저 행동을 예측하려니 머리가 복잡했는데 부분으로 나눠 생각하니 확실히 간단했다. 개별적인 가치 판단 이후 공통 기준으로 묶어주기만 하면 됐다.
이 외에도 '게임 시스템 디자인 입문'의 밸런싱 파트를 읽었는데 범위 밸런싱과 데이터 지렛목이라는 개념을 통해 유저 행동의 바운더리(범위)를 설정하고 그 범위의 평균을 기준으로 삼아 밸런싱한다는 개념을 구체화할 수 있었다.
게임 시스템 디자인 입문 | 댁스 개저웨이 - 교보문고
게임 시스템 디자인 입문 | 초보 게임 디자이너와 개발자가 꼭 알아야 하는 게임 기획 실무의 모든 것! 〈스타워즈〉 게임 시리즈와 〈마블 얼티밋 얼라이언스 2〉 등 유명 게임 타이틀을 수십
product.kyobobook.co.kr
이 외에도 심심할 때 유튜브에서 아래와 같은 영상들을 보면서 게임 디자이너들이 밸런싱에 대해 어떻게 이야기하는지 들어봤다.
- Game Maker's Toolkit - 게임의 밸런스를 맞추는 법 | Game Maker's Toolkit
- GDC - Slay the Spire: Metrics Driven Design and Balance
- GDC - Board Game Design Day: Balancing Mechanics for Your Card Game's Unique Power Curve
참고로, 위의 영상들은 밸런싱의 기준을 찾는 법에 대해 알려주기보다는 밸런싱에 대한 관점을 이야기하는 것이 주가 되는 영상이기에 이 점 인지하고 시청하면 좋을 것 같다.
이쯤하고 내용을 정리하면 다음과 같다.
정리
- '게임 밸런스 수치 기획 바이블'과 '게임 시스템 디자인 입문' 책을 중심으로 자료 조사를 진행했다.
- 각 효과 수치 디자인을 모듈화하여 공통 기준은 하이퍼 파라미터로 두고, 각 효과 내부에서 효과 가치를 평가할 기준 파라미터를 만들어 개별적으로 검증해 보자 생각을 했다.
- 범위 밸런싱과 지렛목 개념을 통해 각 효과 사용에 대한 유저 행동의 바운더리(범위)를 설정하고, 그 평균 값을 기준(지렛목)으로 두어 밸런싱하자는 아이디어를 구체화할 수 있었다.
어떻게 가치와 기준을 설정했는가?
자료 조사 이후에는 가장 먼저 가치 환산표를 만들었다. <Lib's Rarry>에는 특별한 재화가 없기에 전투에 한정하여 체력, 방어도, 행동력을 1차 재화로 두고 가치 환산 비율을 정의했다.

1차 재화 외에도 플레이어가 4가지 기술을 가질 수 있고, 또 그 기술을 강화할 수도 있기에 이런 기술 항목을 2차 재화로써 밸런싱 해야 겠지만, 이 글의 목적은 효과의 밸런싱 기준을 찾는 것이기에 이번 글에서는 넘어가도록 하겠다.
가치 환산표를 만든 이후에는 무엇을 공통 기준(게임 전체의 하이퍼 파라미터)으로 설정할 것인지 정리했다. 여기서 효과 가치에 영향을 미치는 것은 승리 기준 턴(일반적으로 몇 턴에 게임이 끝난다고 가정할 것인지)과 턴당 공격 횟수(일반적으로 턴당 몇 번 공격한다고 가정할 것인지)인데 이 둘을 기억하고 다음으로 넘어가자.
그럼 어떻게 효과 가치 기준을 찾고 환산했는가? 이는 아래와 같다.
즉각적으로 가치를 환산할 수 있는 경우, 그대로 처리했다.
- 흡혈 : 체력 피해를 받을 시 공격자의 체력이 해당 수치만큼 회복되고 효과는 사라진다.
흡혈 수치 1당 1의 체력이 회복되므로 체력 가치와 동일하게 설정했다. - 가시 : 매 피격 시 공격자에게 해당 수치만큼 피해를 준다. 턴 시작 시 효과가 사라진다.
가시 수치 1당 다음 턴까지 매 피격마다 1의 피해를 주기에 턴당 공격 횟수 x 체력 가치로 설정했다.
유저 패턴에 따라 가치가 달라지는 경우, 기준을 세워 처리했다.
- 칼날 : 피격 시 해당 수치만큼 추가 피해를 입는다.
한 번 쌓인 수치는 사라지지 않으므로 초반에 부여하는 것이 가장 효율이 좋고(가치 있고), 점점 가치가 떨어지게 된다. 따라서, 특정 시전 턴 비율을 기준으로 승리 기준 턴 x 시전 턴 비율 (기준) x 체력 가치로 설정했다.
예 ) 승리 기준 턴이 10이고 시전 턴 비율이 0.5(평균)인 경우, 칼날 수치는 1당 5의 체력 가치를 갖는다. - 축적 : 공격 시 해당 수치의 3배만큼 추가 피해를 준다. 턴 시작 시 해당 수치만큼 피해를 받는다. 공격 시 효과가 사라진다.
효과 부여 시점으로부터 2 턴 내에 공격 시 이득이고, 3 턴 이후부터는 손해다. 따라서, 특정 발동 턴 오프셋을 기준으로 (3 - 발동 턴 오프셋 (기준)) x 체력 가치로 설정했다.
예 ) 발동 턴 오프셋이 1인 경우(효과 부여 이후 1턴 뒤에 발동한다고 가정하는 경우), 축적 수치는 1당 2의 체력 가치를 갖는다.

나는 이런 식으로 가치를 추산했다. 여기서 눈썰미가 좋은 독자들은 한 가지 특이한 점을 발견했을 것이다. 바로, 화상 효과에 대한 가치가 설정되지 않았다는 점이다.
화상 효과를 따로 뺀 이유는 화상 효과의 효율이 독립적이지 않다고 생각했기 때문이다. 위에 표기한 네 가지 효과를 살펴보면 모두 효과 수치 1에 대한 가치 계산이 가능하다는 것을 확인할 수 있다.
흡혈의 경우는 수치 그 자체가, 가시의 경우는 턴당 공격 횟수가, 칼날의 경우는 남은 턴 수가, 그리고 축적의 경우는 발동까지의 턴 수가 그 가치와 직결된다. 수치 1을 부여하든 10을 부여하든 이는 달라지지 않는다.
그러나, 화상 효과는 다르다고 생각했다. 화상 효과는 턴마다 화상 수치만큼 데미지를 입히고 그 수치가 절반으로 감소하기에, 첫 턴에 24만큼 부여하는 경우와 네 턴에 걸쳐 6씩 부여하는 경우의 데미지가 다르다고 생각했다.

..뭐라고? 같지 않냐고? 맞다. 한 번에 24를 부여하든 네 턴에 걸쳐 6씩 부여하든 최종 데미지는 같다. 그러나, 그때의 나는 왜인지 모르게 그냥 다르다고 착각했다. 이 때문에 화상 효과의 가치를 환산하기 어렵다고 생각했고, 파이썬을 통해 기준을 찾아 가치를 환산할 수식을 찾아보자는 생각을 했다.
그렇다. 이게 내가 삽질을 하게 된 원인이다. 사실 무언가 안 된다고 생각되면 기획을 살짝만 바꾸면 되긴 한데, 앞으로 게임 디자인을 하면서 이런 문제를 다시 맞닥뜨리지 않을 거라는 보장을 할 수 없었기에, 이번에 한 번 해결해 보자는 생각을 했다.
의도는 좋았지만 ..부주의함과 근성이 낳은 삽질이라고 할 수 있겠다.. 이런 연유로 불필요하게 파이썬을 통해 화상 효과의 기준을 찾고 검증하는 과정을 거치게 됐다.
정리
- 각 효과를 모듈화한 뒤, 공통 기준과 판단 기준을 엮어 가치를 추산했다.
- 화상 효과에 대한 데미지가 효과를 어떻게 부여하느냐(분산)에 따라 달라진다고 착각하여 독립적이지 않다고 생각했다.
- 파이썬을 통해 화상 효과의 기준을 찾고 검증하고자 했다.
어떻게 기준을 찾고 검증할 것인가?
효과 수치의 기준을 검증하는 방법은 단순했다. 그냥 아이디어가 떠오를 때마다 해당 아이디어에 대한 기댓값을 구했고, 따로 시뮬레이션을 진행하여 기댓값과 비교해 보며 아이디어를 검증했다.
이 과정에서 몇 가지 함수를 정의한 뒤 반복해서 사용했는데 이는 아래와 같다.
- u(t) : 유저가 t턴에 부여하는 효과 수치
- e(t) : t턴에서의 효과 수치 (초반에는, b(t)로 표기함)
- D(t) : t턴까지의 효과에 의한 데미지 (burn_expected, total_damage 등으로 혼용해 표기함)
이들 사이의 관계는
로 정리할 수 있다.
파이썬으로 기준을 찾고 검증하는 모든 과정은 계획적으로 한 게 아닌 생각이 가는대로 했기에, 이름이 조금 다르거나 두서없는 부분이 있을 수 있다. 이 점 이해 바란다.

개인적으로 수식이나 검증하는 과정이 복잡하면 작업자에게도, 실용성 측면에서도 좋지 않다고 생각한다.
이러한 점에서 혹자는 '밸런싱하는데 뭔 미적분이냐?'라는 이야기를 할 수 있을 것 같은데, 앞서 말한대로 지망생 입장에서의 '삽질' 기록이며, 필자가 밸런싱을 하는 방법을 몰라서 그런 거니 더 좋은 방법이나 의견이 있다면 댓글로 조언 부탁한다.
마지막으로, 이 모든 과정에서 ChatGPT의 도움을 많이 받았음을 알린다. 전자공학부를 나왔다고는 하지만 수학적인 개념이 많이 부족하기에, 어떤 아이디어를 검증하기 위한 개념들을 찾고 특성을 이해하는데 주로 사용했다.








+ 2025.01.17 추가)
지금 보니까 어지럽네.. 분산 값이 작은 경우는 한 턴에 30 부여처럼 데이터가 평균 값에 몰린 경우고, 분산 값이 큰 경우는 데이터들이 오히려 평균에서 멀리 떨어져 U자형 그래프가 되는 경우지.. 상원아.. 이런 거 하기 전에 개념부터 제대로 챙기자..
정리
- 순간적인 아이디어를 따라 수식을 세우고, 기댓값과 시뮬레이션 값을 비교하며 검증을 진행했다.
- 수식을 세우고 검증하는데 ChatGPT를 활용했다.
- 처음이라 많이 미숙하니 이해바란다. (이 사람은 얼마나 멍청하게 삽질하는지 같이 즐겨보자.)
Python 검증 과정
가장 먼저 공통 기준으로 전투 종료 턴을 정한 뒤, 유저 입력 2가지를 만들어 테스트해 보며, 분산에 따라 최종 데미지가 달라지는지 여부를 확인했다.
그 뒤에는 화상 효과 수식,



한 번에 24 효과를 부여했을 때와 네 턴에 걸쳐 6 효과를 부여했을 때의 데미지를 구해 비교해 봤는데 당연히 같았다. 이를 통해 우리는 분산 값이 총 데미지에 영향을 미치지 않으며, 화상 효과는 독립적으로 가치를 추산할 수 있다는 걸 알게 됐다.
약간의 오차가 발생하는 건 시뮬레이션 과정에서 계산 결과가 일부 버려져 발생하는 것 같다.
그런데, 처음 테스트할 때는 내 실수로 값을 잘못 넣어 확인했다. 그냥 테스트 순간에 머리가 이상해져서 그런 건지는 모르겠는데 한 번에 72를 넣은 것과 6턴에 걸쳐 6의 값을 넣은 것(이래놓고 72라고 착각함🤔)을 비교하면서 값이 다르다고 분산이 예상대로 큰 영향을 미친다고 생각했다.
이게 내 삽질의 시작이었다. 정확한 값을 입력했는지 확인해야 됐는데 그냥 좋다며 그대로 진행해버렸다. 비록 이미 결론이 나왔지만.. 멍청한 나를 여러분의 반면교사로 삼을 수 있도록 틀린 가치를 찾고 검증하는 과정을 같이 한번 살펴보도록 하자.. 😂😂
분산이 총 데미지에 영향을 준다는 것을 확인(착각)한 이후에는 내가


이렇게


턴마다 부여할 수 있는 효과 수치를 정해두고 모든 t에 대한 u(t) 값을 0에서 해당 수치까지 중 하나로 설정하게 구현했다.
구현 방식은 base_repr 메서드로 첫 번째 인자 i를 base로 들어온 두 번째 인자에 해당하는 진법의 문자열로 변환한 뒤, 남은 자릿수를 zfill 메서드를 통해 0으로 채워준다. 그 뒤 int 형으로 변환하고 np.array 메서드를 통해 numpy 배열로 변환하도록 구현했다.
예를 들어, step 19에서는 turn_grantable_value가 2이니 3진법으로
여기서 이 문자열이 하나씩 int로 변환되어 numpy 배열이 되니 최종적으로
이런 식으로 하나씩 순회해서
물론 보일리가 없다. 하지만 내가 누구인가.

나는 고집의 AeonFlor. 결과를 믿지 않고, 내 고집에만 집중하느라 테스트가 잘못됐다고 생각했다 🤗🤗🤗 (답이 없네..)
위의 순회 테스트는 공통 기준 값이 커지면 바로 메모리 부족으로 터져서 제대로 확인이 불가했는데, 그럴거면 데이터 샘플링을 통해 랜덤으로 생성한
10000개의 u(t)를 생성해서 테스트한 결과는 아래와 같다.


결과는 잘 나왔다. 어? 그런데 분석하려고 보니까 이래서는 제대로 된 분석이 안 된다. 왜냐하면 turn_grantable_value 때문에 턴마다 사용 가능한 수치가 제한되기 때문이다.
이렇게 되면 최대로 사용했을 때
따라서, 턴당 부여 가능한 효과 수치를 삭제하고, 전투 중 부여할 효과 수치 총합(total_grant_effect)을 추가해 다시 생성해봤다.


여기에서는 random disribution을 통해 효과 수치 총합을 랜덤하게
이쯤 되면 이런 생각이 든다.
근데 분산이랑 데미지랑 연관 없다면서 이건 언제 확인함?
나도 모르겠다. 과거의 이 사람은 구현하는데 정신이 팔렸다. 이를 돌아보건데 이때의 나는 이미 분석 따위는 눈에 들어오지 않는 상태였던 것 같다.
대충 돌아가는 것 같으니 일단 구현하고 봤던 것 같은데.. 그래도 일말의 양심은 있었는지 random distribution이 아닌 random.normal 메서드를 통해 분산 값으로 총합이 total_grant_effect인
그리고, 이렇게 분산을 통해


이렇게 확인해 봐도 분산은 총 데미지와 연관이 없다. 하지만 나는 여전히 잘못 확인한 전제 조건만 믿고 정신 차리지 못했다...
..라는 게 원래 쓰려던 글의 내용이었는데, 지금 다시 확인해 보니까 분산에 따라

처음이니까.. 나는 처음이니까.. 이해 바란다😪. 이제는 마음 놓고 이 인간이 어디까지 삽질을 하나 계속 따라가 보자. (원래 처음에 이미 결론이 나왔다. 모든 과정이 삽질인데 더 멍청한 삽질이 됐을 뿐이다. 온 세상이 트루먼 쇼였던 거임..!)
아무튼 그 뒤에는 목표 분산에 대한


여기에 더해 분산이 극단적으로 작은 경우, 내 예상으로는 한 턴에 값이 몰려 있어야 정상인데, truncated normal distribution의 경우는 오히려 더 골고루 분포됐다.
당시에는 그냥 다른 방법을 찾자고 넘어갔는데 지금 와서 이유를 고민해 보면 분산이 t에 대한 분산이 아니라 최종 데미지에 대한 분산이어서 mean 값으로 들어간 3으로 밀집됐다는 생각이 든다. (이런 바보)
이는 ChatGPT의 도움을 받아 dirichlet distribution이라는 것을 사용하여 조정자를 통해 분산을 제어해 해결할 수 있었다. (사실 분산 축만 바꿔서 개선해도 됐을 텐데..)


이후에는 시뮬레이션 과정에서의 내림 처리로 인해 dirichlet distribution의 총합이 보장된다는 특성이 무용해져서, 이것저것 시도해 보다가 결국 반올림을 통해 총합에 근사하도록 수정했다.

위는 0에서 4까지의 variance control 값으로 dirichlet distribution을 통해
어? 이거 왜 자꾸 분산이랑 데미지랑 상관이 없다고 나오지..?
왜긴 왜야. 상관이 없으니까 상관이 없다고 나오지.
와! 드디어 내 고집이 꺾일 때가 왔다. 마지막으로 하나의 variance control 값에 대해 샘플링해서 결과를 확인해 봤다.


결과는?

이때 정신이 아찔해졌다. 빠르게 돌아가서 처음부터 다시 확인을 하는데 첫 전제부터 잘못 됐더라. 72와 36을 비교해 놓고 결과가 다르다고 분포에 따라 총 데미지가 영향을 받는다고 했던 부분. 바로 거기가 내 패인이다.

그래.. 그렇다. 내 삽질은 이렇게 끝이 났다.
나는 무엇을 위해...
아무튼 틀린 건 틀린 거고 확인할 건 확인해야지. 나는 파이썬 가지고 노는.. 걸 멈추고, 차분히 생각해 봤다.
엥.. 근데 분포에 상관없이 총 데미지가 효과 입력 수치의 2배로 수렴하네..? 왜 이러지?
얘야..

그래서, 마지막으로 공통 기준으로 턴 종료 기준(turn_criteria), 효과 가치 환산 기준으로 화염 효과 기본 데미지 (effect_base_damage) , 턴당 감쇠율(decay_rate)로

+ 2025.01.17 추가)
상원아.. 너가 한 건 기댓값 계산과 시뮬레이션이 아니란다.. 그냥 같은 식을 다르게 계산한 거잖아. 확률이 없는데 기댓값 계산과 시뮬레이션이 무슨 의미니.. 부끄러워부끄러워부끄러워
이게 끝이다.
정리
- 삽질함.
결론
화상 효과는 턴 종료 기준(turn_criteria), 효과 가치 환산 기준으로 화염 효과 기본 데미지 (effect_base_damage) , 턴당 감쇠율(decay_rate)로
- 유저 바운더리를 정의하고 테스트할 수 있는 환경을 구축했다.
- 수식을 통한 기댓값 계산과 시뮬레이션을 진행하며 테스트 경험치를 쌓았다.
- 이전과 같이 엑셀에 테스트 및 밸런싱 환경을 일일이 구축하지 않고, 파이썬으로 1차 검증 후 엑셀로 옮기는 작업 방식을 도입했다.
포스트모템
- 전제 조건이 정확한지 확실히 확인하자.
- 테스트 결과가 잘못됐다면 조건을 확인하자. 이상이 없다면 가설이 잘못된 거다.
- 테스트 환경 구축 시 구현과 분석 절차를 분리하자. 구현하면서 분석하려니 한쪽에 소홀해진다.
- 결과를 확정 지어 놓고 테스트를 진행하지 말자. 테스트는 결과를 검증하기 위한 도구이지 멍청한 자기 확신을 위한 수단이 아니다.
- 테스트 과정에서 잘 모르는 개념을 넣어 시도해 보는 건 좋지만 제대로 사용하려면 개념과 원리를 꼭 제대로 이해하자.
마치며
최근 성장기가 다시 찾아왔다. 이에 다소 바보 같더라도 이것저것 시도해 보며 경험치를 쌓는 중이다. 내 현실적인 기획과 나아가야 할 방향도 어느 정도 감이 잡히고 말이다.
오늘의 기록도 그 일환 중 하나였는데 글을 쓰는데 생각보다 오래 걸려서 중간부터, 정확히는 Python 검증 과정부터 내 삽질에 내 스스로 부끄러워져서 반쯤 정신 놓고 기록한 것 같다. 🙄😅
일단 올려두고 늘 그렇듯 조금씩 퇴고하며 개선해 보면 되지 않을까?
늘 게임 디자인 관련 글을 작성해야 한다는 부담감이 있었는데, 순수한 게임 디자인이라기에는 조금 이상하지만 그래도 밸런스 디자인 관련 글이라는 점에서 이 정도면 시작은 했다고 봐도 좋을 것 같다.. 아마두..? 😊😊
혹시나 순수 기획적인 내용을 기대했던 분이 있다면 죄송하다는 말을 전한다. 그래도, 요즘 기획 요청 방식에 대해 이전 기획들을 돌아보며 정리하고 있어서 다음에 이걸 주제로 글을 쓴다면 그건 확실히 기획 관련된 내용일 것 같다.
아마 그전에 <Breath in Winter> 관련된 글로 찾아올 것 같긴 하지만 말이다. 😏
아무튼.. 오늘도 관심을 갖고 내게 다가와, 이 글을 읽어줘서 정말 고맙다. 다시 한번 좋은 글, 좋은 컨텐츠로 보답하겠다.
그럼 안뇽!
'게임 디자인 > 게임 디자인 연구' 카테고리의 다른 글
어드밴티지와 피드백 루프 (3) | 2024.03.16 |
---|---|
전투 디자인 해석 (0) | 2024.02.18 |