Как создать настраиваемую формулу для повышения уровня RPG?

43

Я пытаюсь создать формулу, которую можно изменить, просто изменив два значения: number_of_levels и last_level_experience. Это сделано для того, чтобы люди, изменяющие игру, могли изменить требования к уровню.

Я понял, что могу указать количество XP, необходимое для повышения до последнего уровня, но я хочу иметь возможность контролировать XP, необходимое для повышения до первого уровня, который в этом случае может сильно отличаться. Например, если у меня есть 40 уровней и 1 000 000 XP для последнего уровня, то для первого уровня требуется повышение до 625. Но если я изменю уровни на 80, то для первого уровня поднимается 156. В обоих случаях последний уровень требует 1000000.

Должен быть какой-то способ заставить компьютер вычислить подходящую кривую, учитывая только эти два основных значения.

#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;
    }
}

Выход:

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)
дубинка
источник
13
Основная проблема заключается в том, что существует бесконечно много кривых уровня XP, которые заканчиваются последним уровнем, требующим столько XP. Вы не ограничивали масштабы проблемы, потому что вы не указали, как вы хотите, чтобы XP менялся от уровня к уровню. Вы хотите экспоненциальную кривую роста? Параболическая кривая роста? Линейный? Ваша проблема неразрешима в ее текущем состоянии. Лично, если бы я занимался моддингом, я бы хотел больше контролировать кривую ХР, чем просто номер последнего уровня и ХР последнего уровня. Я хотел бы контролировать саму кривую.
Николь Болас
Я могу позволить моддерам контролировать уровень с помощью скрипта.
дубинка

Ответы:

70

Хотя существует бесконечно много способов их выбора, для выравнивающих кривых характерно соблюдение степенного правила, такого как следующее:

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

Основное преимущество этой формулы может быть легко объяснено: для данного правила существует фиксированное значение N, такое, что каждый уровень стоит на N процентов больше, чем предыдущий .

Ваши исходные переменные добавляют следующие ограничения:

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

Два уравнения, два неизвестных. Это выглядит хорошо. Простая математика дает Aи B:

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

В результате получается следующий код:

#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;
    }
}

И следующий вывод:

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
Сэм Хоцевар
источник
12
Если бы все ответы были хорошо спланированы и продуманы.
Нейт
Кривая здесь гораздо приятнее.
дубинка
Хороший ответ. Это может быть глупый вопрос, но как вы рассчитываете, Nчто вы описали выше? Что делать, если вы хотите сделать Nподключаемую переменную? Дайте мне знать, если я задам отдельный вопрос для этого.
Даниэль Каплан
1
@tieTYT отношения между Nи Bесть exp(B) = 1 + N, или B = log(1 + N). Так что если вы хотите, чтобы каждый уровень требовал, например, на 15% больше, чем предыдущий, вам понадобится B = log(1 + 0.15) = 0.13976.
Сэм Хоцевар
17

Не забудьте округлить числа после того, как вы выяснили свою кривую. Не имеет смысла говорить игроку, что ему нужно 119 378 очков опыта, чтобы достичь следующего уровня - потому что человек всегда будет понимать его как «примерно 120 000». Таким образом, вам будет лучше сделать округление самостоятельно и представить «чистые» результаты своим игрокам. Например, следующий код (который распространяется на код Сэма Хочевара) попытается округлить до ≈2,2 значащих цифр (очевидно, что константа может быть настроена так, как вы хотите):

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)) )

Выход:

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
паша
источник