Lesson objectives
At the end of this lesson, you will know…
- How to return a value in a property, by calculating it from other properties
One thing missing from the game is updating the player’s level, based on their current experience points.
We could do this by having the game re-calculate the level, and reset the value of the Player.Level property every time the player gains experience, but there is a much simpler way. We’re going to have the property automatically calculate its value every time someone “gets” (reads) it.
How to have a property calculate its value from other properties
Step 1: Open the SuperAdventure solution in Visual Studio and open the Player.cs class.
Step 2: Change the line for the Level property from this:
public int Level { get; set; }
To this:
public int Level { get { return ((ExperiencePoints / 100) + 1); } }
Notice that we removed the “set;”. That’s because we’re never going to put a value into the Level property – which is what the “set” is for.
We also changed the “get;” Before, since this was an “auto-property”, the “get” would get the value that it was previously “set” to. Now, the program will calculate the value for Level, by dividing the ExperiencePoints by 100.
Since this property is an “int”, it will automatically round the answer down, which is why we add 1 to it – so the player will start out a level 1, and not 0.
Step 3: Now we need to clean up the places where we were setting the Level value, since we removed the “set” option.
In Player.cs:
Remove this line from the constructor:
Level = level;
Since we don’t set the Level value any more, we can also remove it as a parameter from the constructor. So, now the constructor for Player.cs shuld look like this:
public Player(int currentHitPoints, int maximumHitPoints, int gold, int experiencePoints) : base(currentHitPoints, maximumHitPoints) { Gold = gold; ExperiencePoints = experiencePoints; Inventory = new List<InventoryItem>(); Quests = new List<PlayerQuest>(); }
Step 4: Now that we removed the level parameter from the Player class, we need to clean up anything that called the constructor.
Open the code for the SuperAdventure.cs screen class.
In the constructor for this class, we populate the “_player” variable by instantiating a new Player object.
_player = new Player(10, 10, 20, 0, 1);
Remove the value passed in for the level, so it looks like this:
_player = new Player(10, 10, 20, 0);
Step 5: Now we need to make sure the Level is updated on the game screen, after every time the player gains experience (by killing a monster or completing a quest).
You could do this by just adding this line after every time the player’s experience changes:
lblExperience.Text = _player.ExperiencePoints.ToString();
However, I decided to make this new function to update all the player’s stats:
private void UpdatePlayerStats() { // Refresh player information and inventory controls lblHitPoints.Text = _player.CurrentHitPoints.ToString(); lblGold.Text = _player.Gold.ToString(); lblExperience.Text = _player.ExperiencePoints.ToString(); lblLevel.Text = _player.Level.ToString(); }
Then add a call to that function from the other functions that could update the player’s level (the constructor, btnUseWeapon_Click, and MoveTo). If you want to see exactly where I put those calls, look at the code for this lesson in the link below.
Check your work
Build the solution and make sure there are no errors in the “output” box at the bottom of Visual Studio. If you see any problems, double-check the changes you made in this lesson.
Summary
It may not be obvious at first, but this is a powerful programming practice.
Before, we needed to manually update both the player’s ExperiencePoints property and their Level property. Why do two things (and possibly forget to do one, making the game act strangely) when you can do one thing and have it automatically update everything else that depends on it?
Source code for this lesson
Next lesson: Lesson 19.3 – Clean up the source code by converting foreach to LINQ
Previous lesson: Lesson 19.1 – Scroll to the bottom of a rich text box
All lessons: Learn C# by Building a Simple RPG Index
Thank you very much for this excellent tutorial! I am having a lot of fun learning this stuff! I had a quick question as to how you would increment the player’s Hit points based on the level? I don’t require some uber algorithm using advanced math, but maybe something easier using the same idea you had in this chapter of the tutorial series. Thank you, again!
You’re welcome.
The way I’d do it has a little bit of work, but no difficult math. Instead of letting the SuperAdventure.cs class add experience points directly to the _player object, I would create a new method in the Player class that would look something like this (I didn’t test it in the compiler, so it might be a little off):
public void AddExperiencePoints(int xpGain)
{
ExperiencePoints += xpGain// add the earned XP to the player's current ExperiencePoints property
MaximumHitPoints = (Level * 5) // if the player's level increased, their MaximumHitPoints will increase by 5
}
Then, I’d change the ExperiencePoints property to this:
public int ExperiencePoints { get; private set; }
By making the set “private”, the only code that can access it is other methods in the Player class. So, you don’t have to worry about another class directly modifying the ExperiencePoints and bypassing your code to update the MaximumHitPoints value. After you made these changes, go into the SuperAdventure.cs file and change all the places where you add to the player’s XP to use the new function.
Let me know if that was what you were looking for, and if it makes sense.
Hello Scott.
Thank you so much for this tutorial! I am having some issues with increasing the players HP when they level up. I have used
public void AddExperiencePoints(int xpGain)
{
ExperiencePoints += xpGain// add the earned XP to the player’s current ExperiencePoints property
MaximumHitPoints = (Level * 5) // if the player’s level increased, their MaximumHitPoints will increase by 5
}
and changed public int ExperiencePoints { get; private set; }
I’m unsure of what to change in the SuperAdventureCS class. I have the red line under _player.ExperiencePoints += newLocation.QuestAvailableHere.RewardExperiencePoints.
If i remove the private from public int ExperiencePoints { get; private set; }, the program works fine and leveling up works, but the HP doesn’t go up.
Hi Ryan,
You’re welcome! Can you upload your solution (including the directories under it, and all the files in those directories) to GitHub or Dropbox, so I can look at it?
Hello Scott, thank you so much for the helpful tutorial! Would you please be able to go into a little bit more detail on how to increase the players HP when they level up?
Hi Ryan,
Did I ever answer this for you? I think I did in another question, but I might have missed it.
Great! Thank you for the explanation! I added an if statement to check if the player’s Level <= 2 and set MaximumHitPoints = 10 to keep 10 hit points for the player when starting a new game. Overall, it worked great!
Cool!
Just want to say thanks so much for making these tutorials. They have helped learn c# a lot better than many of the books I have looked at. I am trying to change the way it calculates the players level so the required xp to level is not just 100 more xp per level, but you level * 100, so for level 10 you would need 1000 xp on top of your current xp etc. However, whenever I change the equation to something that would work like that all I get is errors. Is there any chance you could help me? Or would it be too complex for someone who is just learning to code. So level 1 would require 100 xp, lv 2 would require 300(TOTAL), lv 3 would require 600(TOTAL)
You’re welcome. There are a few different ways you could do that. The first one involves a lot of “else if” statements. It might look like this:
if(ExperiencePoints < 100) { return 1; } else if(ExperiencePoints < 300) { return 2; } else if(ExperiencePoints < 600) { return 3; } ... etc.
The problem with this technique is that you need to have an "else if" for each level. If you plan to have a huge game, and let the player get to a high level, that will be a lot of code.
You could also do a loop, with a counter. You've save the player's current hit points in a new variable, then go through the loop, subtracting the counter value (starting at 1) times 100. If the amount of experience points remaining, after subtracting that value) is negative, then you've reached the player's level.
The cleanest way would be to figure out a single equation that could calculate the level. If you've done calculus, or advanced algebra, you could probably figure out the equation.
Let me know if you try the second or third way and have any difficulties with them - and if you have the code for something that isn't working. If so, I can get more specific with a solution.
Ok I’ll try them out and if I have any difficulties I’ll let you know, and yeah I had an equation but the problem was it was not letting me calculate it correctly as it was an infinite loop. Thanks!
To stay in line with previous posts to this lesson I’d like to post my method for implementing the leveling system. In lesson 18 we were encouraged to expand the game ourselves so I came up with this. Please let me know if you think this is OK or if you think it could (should!) have done better. Im here to learn. 🙂
VIEW CODE HERE: https://gist.github.com/ScottLilly/946defd4ddc9f4759f2fcc467d9ad38c
Cool! I moved the code to Gist, because there is a problem with posting code in the comments here. The “less than” and “greater than” signs often break the formatting.
If you wanted to do something more advanced, you could make the NeededExperiencePoints property a (get-only) calculated value. You’d need a new function to determine XP needed for the next level (the opposite of what there is now, where you determine the level, based on the XP). When you get to the final lesson (24.1), you’ll see that I like to have read-only properties that notify the UI when their value changes. But, your update is definitely a good way to add better leveling to the game.
Hi, i’ve implemented a level up function, could you check it?
It works fine, but i’d like to know if there is something “wrong” like really bad practices or something like that.
https://github.com/Kayuwaii/Tales-of-The-Warrior
The function is at the end of the player method and is called inside the UpdatePlayerStats function. There is also a change on the Player’s class properties.
I don’t see anything “wrong” with it. I do see two small changes I would probably make.
In TalesOfTheWarrior.cs, I would change line 206 to this:
_player.Level = 1;
If the Level was ever -2 (not that it ever should be negative), “Level++” would only increase it to -1.
In Player.cs, on line 165, you do not need to add zero. So, you could use this:
ExperiencePoints = overXP;
If you wanted to make the code even smaller, you could remove line 162, and change line 165 to:
ExperiencePoints -= ExperiencePointsNeeded;
The program should work correctly without those changes. But, these might make the code’s intentions a little clearer.
i’m still so lost on what you mean.. i still get this when i did that.https://pasteboard.co/GR4hv9a.png
In the Player class, ensure the ExperiencePoints property is spelled correctly (including matching capitalization), and that the property is “public”. If that does not fix the problem, can you post the Player class to http://gist.github.com, so I can look at it?
Hey Scott, thanks for a great tutorial. It was exactly what I needed to get into C# as an absolute beginner.
Don’t know if you’re still providing feedback, but I could use some help with a “Level Up!” mechanic im trying to add to the game.
I don’t know how to implement a one time event, that would happen on a level change (i.e. 1 to 2)[actual problem for me], then increased MaxHP and displayed a message [I got this ;)].
So far I managed to that with a new method(?) and added that to UpdatePlayerStats(). It works, but I have no way of knowing if it’s the optimal approach. 🙂
It looks like this:
private void PlayerNewLevelCheck()
{
if (_player.ExperiencePoints > (_player.Level * 100)) {
_player.Level++;
_player.MaximumHitPoints = (7+ (_player.Level*3));
UpdatePlayerStats();
// Display message
rtbMessages.Text += “Congratulations! You’re are now level” + _player.Level + “! Your Hit Points increased by 3.” + Environment.NewLine;
}
}
Hello Mateusz,
That looks like a good way to increase the player’s level. Do you call if from the ExperiencePoints setter? That’s what I would probably do, so this check is run every time the player’s ExperiencePoints changes.