I am making an experience/ level up system and am trying to replicate Dungeons and Dragons’ leveling curve.
Currently, I am leveling correctly from level 1 to 2 when I hit 1000 experience, but am then continuing to level again at every 1000 exp, instead of following the expected curve.
If I disable the line that sets exp back to 0 on levelup, then the character continues to simply level up every frame once 1000 exp is reached.
Here is the level code I am working on; any help is appreciated:
usingUnityEngine;
usingSystem.Collections;
public class LevelSystem : MonoBehaviour
{
//reference to the player script.
player hero;
public int level;
public int exp;
//reference to the level up formula that I am using currently.
int N;
void Awake()
{
hero = player.hero;
/*the formula I am using, which is working correctly for the first level
*up, but then leveling every 1000 xp.*/
N = 500*(level^2)-(500*level);
}
void Update ()
{
LevelUp ();
}
void LevelUp()
{
if (exp >= N)
{
Level Effect();
}
}
void LevelEffect()
{
/* the following two lines set the exp back to 0, and should level
*the level by 1. In theory, I should not need that first line, but
*currently without it the player levels every frame once
*1000 xp is reached*/
exp = 0;
level = level + 1;
/*ignore the following; these are just tests for the "what happens"
*when you level up.*/
hero.maxHealth = hero.maxHealth * 1.08f;
hero.health = hero.maxHealth;
hero.damage = hero.damage * 1.1f;
}
}
You have your experience formula a little wrong.
XP needed to reach level X is:
XP = 500 * (X) * (X -1)
So, to reach say level 14, the formula would look like this:
XP = 500 * (14) * (13)
If I where you, I would have a variable that stores the Hero’s total xp gained, one that stores the total xp needed to reach the next level (use the formula above to find this), one that stores the amount of xp needed to reach the level the hero is already at (use above formula), and lastly a variable to store the amount of xp needed to level (aka, the difference between current level xp, and next level xp).
The main problem is that your formula is only executed once - at initialization time. You need to recalculate the value needed for the next level each time the player levels up.
I think this is a misunderstanding about how code execution works in C#. There are languages out there that behave by accepting “facts” that are considered and reconsidered at each query, these are called logical paradigm languages.
C#, however, is an imperative paradigm language. The expression: N = 500*(level^2)-(500*level); is evaluated and executed only once - it does not create a formula that is recalculated every time it’s constituents are changed.
The behavior you just described doesn’t make much sense to me. I can tell the code in your OP was either: not compiling when you posted it, or re-written instead of pasted in from the IDE. Are you willing to post you code as it is now?
I’ve made a few changes along the way trying to sort this, but currently here is where I am.
The current code posted below causes the player to level from level one to 4 on initialization, and then the player levels in a pattern of 1,4,5,8,9,12…
I also made the Next Level (N) int public so that I can view it in the inspector. The value never updates from 1000. Due to this, if you disable the line that sets exp to 0, the player will again continue to level every frame once 1000 exp is reached.
usingUnityEngine;
usingSystem.Collections;
publicclassLevelSystem : MonoBehaviour
{
player hero;
public intl evel;
public int exp;
public int N;
void Awake()
{
hero = player.hero;
}
void Update ()
{
LevelUp ();
N = 500*(level^2)-(500*level);
}
void LevelUp()
{
if (exp >= N)
{
LevelEffect();
}
}
void LevelEffect()
{
exp = 0;
level = level + 1;
hero.maxHealth = hero.maxHealth * 1.08f;
hero.health = hero.maxHealth;
hero.damage = hero.damage * 1.1f;
}
}
I have run the numbers, and your formula and mine yield the exact same need exp to reach any given level. I tried what you suggested but in the end am running into the same issues, which are based around formulas not updating as eisenpony pointed out in his first post.
This is still returning the strange leveling pattern that I mention above, although having the formula in start does prevent the immediate leveling from 1 - 4 on initialization. The N value never updates from 1000.
Topher, you will want to create a separate method for handling leveling.
I would suggest that you check for leveling only when your hero gains xp, not every frame.
So create an AddXP method, and at the end of the method check to see if the character needs to level.
Another idea I have, is to create a small Level Script that holds the data that all levels have in common.
This is useful, because then you could create a List of Levels on startup, each containing things such as the xp needed to reach that level, and the level value of the level.
Then all you would need to do is create a for loop that adds the correct number of levels to your List of levels, and calculates to XP needed to reach the level.
…But if I use the following, I still get the weird level progression (1, 4, 5, 8, 9, 12…) and the amount needed to level remains 1000. Although what TonanBora is suggesting I am confident could work, I really think there has to be a simpler solution.
Your code should not behave as it is. I can only assume you have another script touching this script’s public fields - probably to assist in your testing. This is the logic behind what some others are suggesting - you want to segment your code a little bit to make sure there is not unexpected interaction.
Try making your “N” field private temporarily. This will allow you to ensure no other script is interfering with the calculation.
As for the weird level progression, it could be another side effect of interfering scripts, or it could be that you are simply leveling up too fast to see the numbers change.
Originally the N field was private. I only opened it to public so that I could see the value in the inspector. I did try this again however, and with the same results. I can also confirm that the LevelEffect is running only one time, as the debug.log message displays only once per kill.
The Debug.Log message I am referring to is in the enemy script, and is part of the function that is called when the enemy is killed. It would seem that if the player were receiving the 1000 xp multiple times, the message would also play multiple times.
Appreciate your help, but as the LevelUp function still relies on another variable(the experience), this doesn’t actually do anything. I can use the same concept with the LevelEffect function (and changed to GetMouseButtonUp), but this bypasses the exact mechanic that I am trying to troubleshoot.
Yes, GetMouseButtonUp is the better way to go.
As eisen said, you might be leveling to fast to actually see it, which is why you should wrap what ever is causing the leveling in the If statement , so that it only increments when you tell it too.
I’ve been banging my head on this for almost a week. I’ve watched every tutorial on the internet that I can find regarding exp/leveling systems and I have not found a single one that actually implements a leveling curve rather than a simple exponential increase or something like +level per x experience.
I can’t believe that with something as ubiquitous to video games as exp/ level I cannot find a single resource that clearly explains any working version of this mechanic.
You guys are definitely on the right track with the multiple levels.
I added a debug.log in LevelEffect() and when I kill an enemy, the “killed enemy” message from the enemy script plays once, but then the “leveled a level” message plays three times after killing an enemy. The “leveled a level” message plays once after killing another enemy, then three times again after the third enemy, and so on.
void LevelEffect()
{
exp = 0;
level = level + 1;
//the following line displays three times after first kill, then once after second kill, etc.
Debug.Log ("leveled a level");
hero.maxHealth = hero.maxHealth * 1.08f;
hero.health = hero.maxHealth;
hero.damage = hero.damage * 1.1f;
}
Here is the relevent code from the enemy script regarding death:
voidUpdate()
{
//disregard the following line; just for the gui
healthbar.fillAmount = (float)health / (float)maxHealth;
if (health > 0)
{
Attack ();
FollowPlayer ();
}
//in update, if enemy health is less than or equal to 0, call EnemyDeath()
else
{
EnemyDeath();
}
}
void EnemyDeath()
{
//the following line displays once when an enemy is killed.
Debug.Log ("killed enemy");
Destroy(gameObject, 0.25f);
playerLevel.exp = playerLevel.exp + 1000;
this.enabled = false;
}