¿Cómo crear una fórmula ajustable para los requisitos de subir de nivel RPG?

43

Estoy tratando de crear una fórmula que pueda modificarse simplemente cambiando dos valores: number_of_levels y last_level_experience. Esto es para permitir que las personas que modifican el juego cambien los requisitos de nivelación.

Lo tengo para poder especificar la cantidad de XP necesaria para el último nivel, pero quiero poder controlar la XP necesaria para el primer nivel, que en este caso puede variar enormemente. Por ejemplo, si tengo 40 niveles y 1,000,000 XP para el último nivel, el requisito de primer nivel es 625. Pero si cambio los niveles a 80, el primer nivel se convierte en 156. En ambos casos, el último nivel necesita 1,000,000.

Debe haber alguna forma de hacer que la computadora calcule una curva adecuada dados estos dos valores básicos.

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

Salida:

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)
Cachiporra
fuente
13
El problema fundamental es que hay infinitas curvas de nivel de XP que terminarían con el último nivel que requiere tanta XP. No ha restringido las dimensiones del problema, porque no ha indicado cómo desea que el XP cambie de un nivel a otro. ¿Quieres una curva de crecimiento exponencial? ¿Una curva de crecimiento parabólico? Uno lineal? Su problema no tiene solución en su estado actual. Personalmente, si estuviera modificando el juego, me gustaría tener más control sobre la curva XP que solo el número del último nivel y el XP del último nivel. Me gustaría controlar la curva real en sí.
Nicol Bolas
Puedo permitir que los modders controlen la nivelación a través de un script.
Truncheon

Respuestas:

70

Aunque hay infinitas maneras de elegirlas, es común que las curvas de nivelación sigan una regla de poder como la siguiente:

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

La principal ventaja de esta fórmula se puede explicar fácilmente: para una regla dada, hay un valor fijo N tal que cada nivel cuesta N por ciento más que el anterior .

Sus variables iniciales agregan las siguientes restricciones:

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

Dos ecuaciones, dos incógnitas. Esto luce bien. Las matemáticas simples dan Ay B:

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

Resultando en el siguiente código:

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

Y la siguiente salida:

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
fuente
12
Si tan solo todas las respuestas estuvieran bien planificadas y pensadas.
Nate
La curva aquí es mucho más sabrosa.
Truncheon
Buena respuesta. Esta puede ser una pregunta estúpida, pero ¿cómo calculas lo Nque describiste anteriormente? ¿Qué pasa si desea hacer Nla variable conectable? Avíseme si debo hacer una pregunta por separado para esto.
Daniel Kaplan
1
@tieTYT la relación entre Ny Bes exp(B) = 1 + N, o B = log(1 + N). Entonces, si desea que cada nivel requiera, por ejemplo, un 15% más que el anterior, lo necesitará B = log(1 + 0.15) = 0.13976.
sam hocevar
17

No olvide redondear los números después de calcular su curva. No tiene mucho sentido decirle al jugador que necesita 119,378 puntos de experiencia para alcanzar el siguiente nivel, porque la persona siempre lo entendería como "aproximadamente 120,000". Por lo tanto, será mejor hacer el redondeo usted mismo y presentar resultados "limpios" a sus jugadores. Por ejemplo, el siguiente código (que se extiende sobre Sam Hocevar's) intentará redondear a ≈2.2 dígitos significativos (obviamente, esa constante se puede ajustar como desee):

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

El resultado es:

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
Bajá
fuente