Jak stworzyć regulowaną formułę dla wymagań zwiększania poziomu RPG?

44

Próbuję utworzyć formułę, którą można zmodyfikować, zmieniając po prostu dwie wartości: liczba_poziomów i doświadczenie_poziomu. Ma to na celu umożliwienie osobom modyfikującym grę zmiany wymagań dotyczących wyrównywania.

Mam go, abym mógł określić liczbę XP potrzebną do ostatniego poziomu, ale chcę móc kontrolować XP potrzebną do pierwszego poziomu, który w tym przypadku może się bardzo różnić. Na przykład, jeśli mam 40 poziomów i 1 000 000 XP na ostatnim poziomie, wówczas wymaganie pierwszego poziomu wynosi 625. Ale jeśli zmienię poziomy na 80, pierwszy poziom wzrośnie do 156. W obu przypadkach ostatni poziom potrzebuje 1 000 000

Musi być jakiś sposób, aby komputer opracował odpowiednią krzywą, biorąc pod uwagę tylko te dwie podstawowe wartości.

#include <iostream>

int main()
{
    int levels = 40;
    if (levels < 2) levels = 2;

    int experience_for_last_level = 1e6;
    float fraction = 1.0 / levels;

    {
        int i = 0;
        float fraction_counter = fraction;
        int counter = levels;
        int total = 0;

        for (i = 1; i <= levels; ++i, fraction_counter += fraction, --counter)
        {
            int a = static_cast<int>(fraction_counter * experience_for_last_level / counter);

            std::cout <<"Level "<<i<<":  "<<a<<" ("<<counter<<")"<<"\n";

            total += a;
        }

        std::cout << "\nTotal Exp: " << total;
    }
}

Wynik:

Level 1:  625   (40)      Level 15: 14423  (26)      Level 29: 60416  (12)
Level 2:  1282  (39)      Level 16: 16000  (25)      Level 30: 68181  (11)
Level 3:  1973  (38)      Level 17: 17708  (24)      Level 31: 77499  (10)
Level 4:  2702  (37)      Level 18: 19565  (23)      Level 32: 88888  (9)
Level 5:  3472  (36)      Level 19: 21590  (22)      Level 33: 103124 (8)
Level 6:  4285  (35)      Level 20: 23809  (21)      Level 34: 121428 (7)
Level 7:  5147  (34)      Level 21: 26250  (20)      Level 35: 145833 (6)
Level 8:  6060  (33)      Level 22: 28947  (19)      Level 36: 179999 (5)
Level 9:  7031  (32)      Level 23: 31944  (18)      Level 37: 231249 (4)
Level 10: 8064  (31)      Level 24: 35294  (17)      Level 38: 316666 (3)
Level 11: 9166  (30)      Level 25: 39062  (16)      Level 39: 487499 (2)
Level 12: 10344 (29)      Level 26: 43333  (15)      Level 40: 999999 (1)
Level 13: 11607 (28)      Level 27: 48214  (14)
Level 14: 12962 (27)      Level 28: 53846  (13)
Buława
źródło
13
Podstawowym problemem jest to, że istnieje nieskończenie wiele krzywych poziomów XP, które kończyłyby się na ostatnim poziomie wymagającym takiej ilości XP. Nie ograniczyłeś wymiarów problemu, ponieważ nie określiłeś, jak chcesz zmieniać XP z poziomu na poziom. Czy chcesz wykładniczą krzywą wzrostu? Paraboliczna krzywa wzrostu? Liniowy? Twój problem jest nierozwiązywalny w obecnym stanie. Osobiście, gdybym modyfikował grę, chciałbym mieć większą kontrolę nad krzywą XP niż tylko numer ostatniego poziomu i XP ostatniego poziomu. Chciałbym kontrolować samą krzywą.
Nicol Bolas,
Mogę pozwolić modderom kontrolować poziomowanie za pomocą skryptu.
Pałka

Odpowiedzi:

71

Chociaż istnieje nieskończenie wiele sposobów ich wyboru, często krzywe niwelacyjne są zgodne z regułą mocy, taką jak następująca:

f(level) == A * exp(B * level)

Główną zaletę tej formuły można łatwo wyjaśnić: dla danej reguły istnieje stała wartość N, tak że każdy poziom kosztuje N procent więcej niż poprzedni .

Twoje początkowe zmienne dodają następujące ograniczenia:

f(1) - f(0) == experience_for_first_level
f(levels) - f(levels - 1) == experience_for_last_level

Dwa równania, dwie niewiadome. To wygląda dobrze. Proste matematyki dają Ai B:

B = log(experience_for_last_level / experience_for_first_level) / (levels - 1);
A = experience_for_first_level / (exp(B) - 1);

Wynikające z następującego kodu:

#include <cmath>
#include <iostream>

int main(void)
{
    int levels = 40;
    int xp_for_first_level = 1000;
    int xp_for_last_level = 1000000;

    double B = log((double)xp_for_last_level / xp_for_first_level) / (levels - 1);
    double A = (double)xp_for_first_level / (exp(B) - 1.0);

    for (int i = 1; i <= levels; i++)
    {
        int old_xp = round(A * exp(B * (i - 1)));
        int new_xp = round(A * exp(B * i));
        std::cout << i << " " << (new_xp - old_xp) << std::endl;
    }
}

I następujące dane wyjściowe:

1 1000          9 4125          17 17012        25 70170        33 289427
2 1193          10 4924         18 20309        26 83768        34 345511
3 1425          11 5878         19 24245        27 100000       35 412462
4 1702          12 7017         20 28943        28 119378       36 492389
5 2031          13 8377         21 34551        29 142510       37 587801
6 2424          14 10000        22 41246        30 170125       38 701704
7 2894          15 11938        23 49239        31 203092       39 837678
8 3455          16 14251        24 58780        32 242446       40 1000000
sam hocevar
źródło
12
Gdyby tylko wszystkie odpowiedzi były dobrze zaplanowane i przemyślane.
Nate
Krzywa tutaj jest znacznie smaczniejsza.
Pałka
Dobra odpowiedź. To może być głupie pytanie, ale jak obliczyć N, że opisałeś powyżej? Co jeśli chcesz zmienić Nzmienną wtykową? Daj mi znać, czy powinienem zadać w tym celu osobne pytanie.
Daniel Kaplan
1
@tieTYT związek między Ni Bjest exp(B) = 1 + N, lub B = log(1 + N). Jeśli więc chcesz, aby każdy poziom wymagał np. 15% więcej niż poprzedni, będziesz potrzebować B = log(1 + 0.15) = 0.13976.
sam hocevar,
18

Nie zapomnij zaokrąglić liczb po ustaleniu krzywej. Nie ma sensu mówić graczowi, że potrzebuje 119 378 punktów doświadczenia, aby przejść do następnego poziomu - ponieważ osoba ta zawsze rozumiałaby to jako „około 120 000”. W ten sposób lepiej będzie, jeśli wykonasz zaokrąglenie samodzielnie i zaprezentujesz swoim graczom „czyste” wyniki. Na przykład następujący kod (który rozciąga się na Sama Hocevara) będzie próbował zaokrąglić w górę do significant2.2 cyfr znaczących (oczywiście ta stała może być dowolnie dostosowywana):

from math import exp, log

levels = 40
xp_for_first_level = 1000
xp_for_last_level = 1000000

B = log(1.0 * xp_for_last_level / xp_for_first_level) / (levels - 1)
A = 1.0 * xp_for_first_level / (exp(B) - 1.0)

def xp_for_level(i):
    x = int(A * exp(B * i))
    y = 10**int(log(x) / log(10) - 2.2)
    return int(x / y) * y

for i in range(1, levels+1):
    print( "%d:  %d" % (i, xp_for_level(i) - xp_for_level(i-1)) )

Dane wyjściowe to:

1:  1000     9:  4200     17:  17100    25:  70000     33:  287000
2:  1190    10:  4900     18:  20300    26:  84000     34:  340000
3:  1420    11:  5900     19:  24200    27:  100000    35:  420000
4:  1710    12:  7000     20:  28700    28:  119000    36:  490000
5:  2030    13:  8400     21:  34000    29:  142000    37:  590000
6:  2420    14:  10000    22:  42000    30:  171000    38:  700000
7:  2870    15:  11900    23:  49000    31:  203000    39:  840000
8:  3400    16:  14200    24:  59000    32:  242000    40:  1000000
Basza
źródło