방치형·클리커 게임의 지수적 상승 밸런싱 공식을 파이썬으로 검증하기

비용과 보상에 지수 함수를 적용한 방치형 게임 경제를 파이썬으로 계산하고 구매 가능 구간과 성장 체감 시점을 점검하는 방법을 정리합니다.

✓ 복사 완료!
챕터수치 공식 & 밸런싱
난이도중급
상태● 검토 완료
대상 엔진
Engine Agnostic
최종 업데이트
방치형·클리커 게임의 지수적 상승 밸런싱 공식을 파이썬으로 검증하기

왜 지수적 상승을 검증해야 할까

방치형·클리커 게임은 짧은 행동을 반복하면서 수치가 빠르게 커지는 장르다. 건물 가격, 초당 생산량, 업그레이드 효과를 모두 선형으로 올리면 초반에는 밋밋하고 후반에는 설계 의도와 다른 속도로 무너질 수 있다. 그래서 많은 게임이 가격과 보상 중 하나 또는 둘 모두에 지수적 증가를 적용한다.

다만 지수 함수는 계수 차이가 작아 보여도 플레이 시간에 따라 큰 격차를 만든다. 밸런싱 단계에서는 특정 단계의 숫자만 보는 대신, 여러 구매 단계에서 다음 구매까지 걸리는 시간과 누적 비용을 함께 계산해야 한다.

기본 모델 세우기

가장 단순한 건물 가격 모델은 다음과 같다.

C(n)=C0×rnC(n)=C_0\times r^n
  • C0C_0: 첫 구매 가격
  • rr: 구매할 때마다 적용할 가격 배율
  • nn: 이미 구매한 개수

예를 들어 첫 가격이 10이고 가격 배율이 1.15라면 10번째 구매 가격은 다음처럼 계산된다.

C(10)=10×1.1510C(10)=10\times1.15^{10}

생산량도 구매 횟수에 따라 일정 비율로 증가시킬 수 있다.

P(n)=P0×gnP(n)=P_0\times g^n

여기서 중요한 값은 가격과 생산량 자체보다 다음 구매 가격을 현재 생산 속도로 나눈 시간이다.

T(n)=C(n)P(n)T(n)=\frac{C(n)}{P(n)}

이 값이 단계가 올라갈수록 지나치게 커지면 플레이어는 정체를 느낀다. 반대로 계속 작아지면 구매가 자동으로 이어져 선택의 의미가 약해질 수 있다.

구매 단계별 비용과 생산량 증가를 비교하는 방치형 게임 밸런싱 그래프

파이썬으로 단계별 값 계산하기

아래 코드는 0번째 구매부터 지정한 단계까지 가격, 초당 생산량, 다음 구매에 필요한 시간을 출력한다. 생산량은 구매 후의 총 생산량으로 가정했다.

from dataclasses import dataclass


@dataclass(frozen=True)
class BalanceConfig:
    base_cost: float = 10.0
    cost_growth: float = 1.15
    base_production: float = 1.0
    production_growth: float = 1.08


def cost_at(step: int, config: BalanceConfig) -> float:
    return config.base_cost * (config.cost_growth ** step)


def production_at(step: int, config: BalanceConfig) -> float:
    return config.base_production * (config.production_growth ** step)


def time_to_next_purchase(step: int, config: BalanceConfig) -> float:
    return cost_at(step, config) / production_at(step, config)


config = BalanceConfig()

print("단계 | 다음 가격 | 초당 생산량 | 구매 대기 시간")
for step in range(0, 21):
    cost = cost_at(step, config)
    production = production_at(step, config)
    wait_time = time_to_next_purchase(step, config)
    print(f"{step:>4} | {cost:>9.2f} | {production:>11.2f} | {wait_time:>13.2f}초")

이 구성에서는 가격 증가율이 생산량 증가율보다 높다. 따라서 구매 대기 시간은 장기적으로 증가한다. 이것이 항상 문제라는 뜻은 아니다. 프레스티지, 활성 스킬, 광고 보상처럼 정체를 해소할 다른 장치를 어느 시점에 제공할지 함께 설계해야 한다.

배율의 관계를 먼저 읽기

대기 시간 공식을 정리하면 변화 방향을 빠르게 판단할 수 있다.

T(n)=C0P0×(rg)nT(n)=\frac{C_0}{P_0}\times\left(\frac{r}{g}\right)^n

즉 핵심은 r/gr/g다.

  • r/g>1r/g > 1이면 구매 대기 시간이 지수적으로 길어진다.
  • r/g=1r/g = 1이면 구매 대기 시간이 거의 일정하게 유지된다.
  • r/g<1r/g < 1이면 구매 대기 시간이 짧아져 후반 진행이 가속된다.

예제의 경우 1.15/1.08≈1.0651.15/1.08\approx1.065이므로 단계마다 대기 시간이 약 6.5%씩 늘어난다. 초반에는 느끼기 어려워도 50단계, 100단계로 갈수록 차이가 커진다.

누적 비용과 구간별 체감 확인하기

개별 구매 가격만으로는 부족하다. 특정 단계까지 필요한 재화 총량도 계산해야 프레스티지 비용이나 콘텐츠 해금 가격을 연결하기 쉬워진다.

등비수열의 누적 비용은 다음 공식으로 구할 수 있다.

S(N)=C0×rN−1r−1S(N)=C_0\times\frac{r^N-1}{r-1}

파이썬에서는 공식값과 반복 합산값을 함께 비교해 구현 실수를 줄일 수 있다.

import math


def total_cost_by_formula(count: int, config: BalanceConfig) -> float:
    if math.isclose(config.cost_growth, 1.0):
        return config.base_cost * count

    return (
        config.base_cost
        * (config.cost_growth ** count - 1)
        / (config.cost_growth - 1)
    )


def total_cost_by_loop(count: int, config: BalanceConfig) -> float:
    return sum(cost_at(step, config) for step in range(count))


for count in (10, 25, 50, 100):
    formula_total = total_cost_by_formula(count, config)
    loop_total = total_cost_by_loop(count, config)

    assert math.isclose(formula_total, loop_total, rel_tol=1e-9)
    print(f"{count}회 구매 누적 비용: {formula_total:,.2f}")

assert가 통과하면 공식과 구현이 같은 결과를 내는지 확인할 수 있다. 실제 프로젝트에서는 이 검증을 자동 테스트에 넣어 가격 배율이나 초기값을 바꾼 뒤에도 계산식이 깨지지 않도록 관리하는 편이 좋다.

밸런싱 판단에 쓸 최소 지표

스프레드시트나 로그에 다음 지표를 단계별로 저장해 두면 조정 근거가 분명해진다.

  1. 다음 구매 가격
  2. 현재 생산량과 다음 구매 대기 시간
  3. 누적 비용
  4. 목표 플레이 시간 안에 도달해야 하는 단계
  5. 프레스티지 전후의 생산량 배수와 회복 시간

특히 대기 시간을 한 숫자로 고정해 판단하지 않는 것이 좋다. 초반 10~30초, 중반 수분, 프레스티지 직전 수십 분처럼 구간마다 의도한 리듬을 먼저 정하고 그 리듬에 맞춰 rr과 gg를 조정하는 방식이 안전하다.

마무리

지수적 상승은 방치형 게임의 성장감을 만들기 좋은 도구지만 작은 배율 차이도 긴 플레이 구간에서는 크게 증폭된다. 가격과 생산량을 따로 조정하기 전에 r/gr/g로 대기 시간의 방향을 확인하고 파이썬으로 누적 비용과 목표 단계의 도달 시간을 검증해 보자. 감각으로 시작한 수치 조정도 반복 가능한 밸런싱 작업으로 바꿀 수 있다.