Learn C# by Building a Simple RPG – Index

Giant Spider!
Mandatory Giant Spider!

If you want to write a Role Playing Game, but don’t know how to program, or just want to learn how to program in C#, then you’re at right the place.

These lessons will take you from a complete beginner, to being an author of a Role Playing Game, for free.

Now, this isn’t the world’s greatest game. In fact, it’s very short and kind of ugly.

However, as you create it, you’ll learn the most common C# programming practices and techniques. Then, if you want, you can improve the game, adding more features and your own special touch to it.


NOTE: One of the awesome students here created a PDF document of the lessons. You can get a copy of it here. You can also download the source code files for the lessons here. Thank you, Mark!



Lesson 00.1 – What is in these lessons?

Lesson 00.2 – General information about programming in C#

Lesson 00.3 – The parts of Visual Studio

Lesson 01.1 – Defining classes and objects for the game

Lesson 02.1 – Installing Visual Studio Community 2017

Lesson 02.2 – Building the solution for the game

Lesson 03.1 – Building the first screen

Lesson 04.1 – Creating the Player class and its properties

Lesson 05.1 – Creating objects from classes

Lesson 06.1 – Creating the remaining classes

Lesson 07.1 – Inheritance and base classes

Lesson 08.1 – Setting properties with a class constructor

Lesson 08.2 – Using class constructors with derived classes

Lesson 09.1 – Using your classes as datatypes

Lesson 10.1 – Creating collections of objects

Lesson 11.1 – Using a static class

Lesson 12.1 – Add the remaining UI controls

Lesson 13.1 – Functions, procedures, and methods

Lesson 13.2 – Creating functions to handle user input

Lesson 14.1 – Variables

Lesson 14.2 – If statements

Lesson 14.3 – Foreach loops

Lesson 15.1 – Getting random numbers for the game

Lesson 16.1 – Writing the function to move the player

Lesson 16.2 – Refactoring the player movement function

Lesson 16.3 – Functions to use weapons and potions

Lesson 17.1 – Running the game on another computer

Lesson 18.1 – Future enhancements for the game

Bonus lessons (enhancements to the game)

Lesson 19.1 – Scroll to the bottom of a rich text box

Lesson 19.2 – Use a calculated value for a property

Lesson 19.3 – Clean up the source code by converting foreach to LINQ

Lesson 19.4 – Saving and loading the player information

Lesson 19.5 – Changing dropdown default values

Lesson 19.6 – Increase maximum hit points when the player gains a level

Improving SuperAdventure’s code quality by refactoring

Lesson 20.1 – Refactoring the SuperAdventure program

Lesson 20.2 – Binding a custom object’s properties to UI controls

Lesson 20.3 – Binding list properties to datagridviews

Lesson 20.4 – Binding child list properties to a combobox

Lesson 20.5 – Moving the game logic functions from the UI project to the Engine project

Adding a vendor to locations (with buying and selling items)

Lesson 21.0 – Plans for adding a vendor to locations

Lesson 21.1 – Adding a price to game items

Lesson 21.2 – Create the vendor class and add it to locations

Lesson 21.3 – Add a button and create its eventhandler in code, without the UI design screen

Lesson 21.4 – Completing the trading screen

Use SQL to save and restore player’s game data

Lesson 22.1 – Installing MS SQL Server on your computer

Lesson 22.2 – Creating database tables from classes

Lesson 22.3 – Creating the SQL to save and load the saved game data

Creating a console UI for SuperAdventure

Lesson 23.1 – Creating a console front-end for the game

Final refactoring (cleanup) of the SuperAdventure source code

Lesson 24.1 – Make the SuperAdventure source code easier to understand and modify

New game features

Lesson 25.1 – Select a random monster at a location

Lesson 26.1 Displaying a World Map

Lesson 26.2 – Hiding Unvisited Locations on the World Map


Bug Fixes

Lesson 99.1 – Preventing duplicate quests

Lesson 99.2 – Setting CurrentWeapon when the player has multiple weapons


Share your personal, expanded versions of SuperAdventure here!

338 thoughts on “Learn C# by Building a Simple RPG – Index

  1. Hello Scott,

    I could finish reading until Lesson 20.4, and saw that you explain solving current weapon issue in the further lessons.

    Maybe, I should wait and continue reading in stead of writing you immediately :).

    Now, I found out that, you should have inserted _currentMonster datavariable also in xml files (both in writing and reading), otherwise, when you start playing from a location where a monster exists, your xml file returns data values without a monster.

    I hope I do not disturb you with such messages, you can consider me as if I am doing my homework properly.


    1. No problem, Can 🙂

      There are probably a few more things in the game that need to be improved. Many people do “test-driven development”, to ensure the program always works – in every condition. That would help us find errors, and fix them immediately. I did not do that for these lessons, because test-driven development is more advanced. If you want to continue learning programming, that would be a good thing to learn, and to apply to this program.

  2. I would really appreciate if someone could provide the entire solution with all the files necessary to run the game. That way I can see it working before working on the source code.

  3. Hi Scott,

    It’s me again Can.

    On lesson 22, I have some reminders for your future edition of your publication, where I eoncountered few exceptions. In PlayerDataMapper.CreateFromDatabase() method, since SqlDataReader reader is not closed, after it is instantiated for savedgame, it should not  be created again for quest and inventory. Either we should create reader at the beginning of the method or we should close reader everytime we create  it.

    Also,  in case of any exception, we return our player null, but it should be inside the  exception {} brackets. However, you put it outside the exception (see pp:222, line 114)

    As I mentioned, these comments are only for a clearer publication for the future.

    A good news is I’ve finished Windows form application, now I’ll start WPF version.


    1. Hi Can,

      Thank you for sharing that. That is fixed on the website, but needs to be updated in the PDF version. I will try to fix the PDF this weekend.

      Good luck on the WPF version!

  4. Hi Scott, first of all huge thanks for writing this tutorial as it’s been a massive help in my learning of C# and programming applications in Windows Forms. I have a few questions to ask of you which I’m hoping you can answer… even though I know you’re probably quite busy! Firstly I keep getting an unhandled exception error to do with the weapons combobox whenever I try and run the game… I’ve come up with a temporary fix which allows me to run the program and test for other errors etc. however the combobox then just displays ‘engine.weapon’ which obviously isn’t the best when trying to select a weapon since they all show the same name! Was also wondering if it’d be at all possible to implement a sort of localised login system where a user’s username and password are stored in an Xml document and are used to access a specific player’s stats and progress in the game… I thought this would be a nice feature and although I’m currently trying to figure out how to implement this myself I am finding it quite hard with my current knowledge of C# and I’m not really sure where to start even though I kind of know what I’m meant to do if that makes sense? Anyway sorry for rambling on a bit there but I certainly look forward to your reply!


    P.S. Happy to send you any of my source code if you could provide an email address or something to send it to (although I have fiddled with it a bit to match my own style, sorry!)

    1. Hello Naoise,

      You’re welcome! If you can upload your solution (including all the directories under it, and the files in them) to GitHub or Dropbox, I can look at the it. The weapon combobox should be a quick fix (I think I know where the problem would be). It might not be able to think about the system until the weekend.

  5. Thanks for your speedy reply Scott,

    Here’s a link to the dropbox folder where I’ve uploaded all the solution files I hope you required: https://www.dropbox.com/s/rovb5c64ujd6vzw/Naoise%27s%20Adventure.rar?dl=0

    Another small issue I had which I forgot to mention at first is one I’m having with quests. Currently when a quest has been completed the user is able to receive the quest infinitely (however they’re not able to complete it again)… maybe I’ve missed something in my code here but I’ve had a look and couldn’t find anything obvious so perhaps you’d know of a fix? Once again really appreciate all the work you put into this project and its incredibly useful to have you so engaged with us and ready to lend a helping hand!


    1. For the weapon combobox problem, look at lines 255 and 256 of SuperAdventure.cs. The DisplayMember and ValueMember properties need to be set to the exact name of the properties in your Weapon/Item class. If you change them to “_name” and “_id”, the combobox should display correctly. Make sure you check the DisplayMember and ValueMember lines in the other functions that populate comboboxes.

      For the quests, the “if” on line 95 checks if the player has the quest and it is not completed. If so, it tries to complete the quest. If both of those conditions are not true, it runs the code in the “else” after line 128. So, if the player has the quest and has completed it, the “else” code will run – including line 152, which gives the player the quest again. To help you practice debugging, and fixing code, I won’t tell you exactly what to do in this reply. If you try fixing it yourself, and have trouble, let me know. I just want you to try on your own first.

      I’ll think about the multi-player login this weekend, and give you some clues on starting that.

    2. Here is the code for a new constructor for SuperAdventure.cs that will let the players select from different saved game files. https://gist.github.com/ScottLilly/6b30876092fc3442f2c76ae022104c84

      When the player starts the game, it will ask if they want to load a saved game. If they do, they can select the saved game file they want to use. If not, the game will create a new player. This uses a MessageBox, to ask if the game should load a saved game, and the OpenFileDialog class, to let the user select the saved game file to use.

      You will need to modify the naoiseRPG_FormClosing() function, to do something similar, to let the players save their games to different files. That will need to use the SaveFileDialog class.

      Let me know if you try this and have any questions.

  6. Hi Scott,
    This site was super helpful! I’m just learning C# with 0 prior programming experience under my belt and I felt these tutorials were for me a great way to learn the concepts. Small question in regards to the leveling portion. Since we are pulling the current level from the amount of experienced earned, what would be the best way to set a max level the player can reach?

    Thanks again.


    1. Thank you Dave,

      In the “getter” for Level, I would change the code to calculate the level before returning it. If the calculated level is higher than the maximum level, return the maximum level. For example, if the maximum level is 10, you could do this:

      public int Level
      int level = ((ExperiencePoints / 100) + 1);

      if (level > 10)
      return 10;
      return level;

      You could also use Math.Min() to return the lowest number of the calculated level and the maximum level, like this:

      public int Level
      return Math.Min(10, (ExperiencePoints / 100) + 1);

      That could be even shorter, by making the property an “expression-bodied property”:

      public int Level => Math.Min(10, (ExperiencePoints / 100) + 1);

  7. Every time I use a healing potion, it works fine except for when I have more than 5 health points. Instead of giving my maximum of 10 hit points it changes to just 5 hit points. As far as I can tell, the code is ok. In the SuperAdventure.cs, the player is declared
    _player = new Player(10, 10, 20, 0, 1);
    so the maximum hit points is set as 10. And in my btnUsePotion_Click method I have the following lines
    _player.CurrentHitPoints = (_player.CurrentHitPoints + potion.AmountToHeal);

    // CurrentHitPoints cannot exceed player’s MaximumHitPoints
    if (_player.CurrentHitPoints > _player.MaximumHitPoints)
    _player.CurrentHitPoints = _player.MaximumHitPoints;

    So it should all work just fine. Help?

    1. Hi Jennifer,

      I used the debugger, to watch the variables. It looks like the code is working.

      Here is what I saw:
      1. The player was fighting a rat, and had 7 hit points remaining
      2. The player drank the potion
      3. The player’s CurrentHitPoints was set to 12 (7 + 5) on line 412
      4. The player’s CurrentHitPoints was greater than their MaximumHitPoints (line 415). So, the program ran line 417
      5. On line 417, the player’s CurrentHitPoints was set to their MaximumHitPoints
      6. On line 436, the monster attacked the player, doing damage (which was subtracted from the player’s CurrenHitPoints)

      What you might have seen is the monster doing 5 points of damage – making it look like a problem with the healing potion code.

      Does that make sense?

      1. That makes sense, sorry. Thanks. Also, is the spider supposed to do so much damage it can kill you all at once?

        1. You’re welcome. The giant spider is a powerful monster. If you want to make it weaker, you could change its maximum damage in the PopulateMonsters() function, in the World class.

  8. Hey Scott, so I’m trying to add a feature to the game where there is “dialogue” when you enter a location with a quest, but I have this line of code, that when I test the game, keeps getting an index out of range exception error and I don’t know why.
    Here is my code. https://github.com/JenniferE55/SuperAdventure/tree/questDialogue . The line causing the problem is 126 in the SuperAdventure.cs class. If you could help me out, that would be great!

    1. To fix this, there are two things to do. First, change line 30 of Quest.cs to
      QuestNotFinishedDialogues = questNotFinishedDialogues.ToList();

      In the current World.cs code, the “questNotFinishedDialogues.Clear();” lines clear out the QuestNotFinishedDialogues properties in the Quest. That’s because the variable questNotFinishedDialogues is being used “by reference”, and not “by value” (more information on this). The Quest objects are all pointing to the same list, which is cleared out on line 134 of World.cs.

      Adding the “.ToList()” makes a new list in memory. Then, that new list is assigned to the QuestNotFinishedDialogues property. When you call “questNotFinishedDialogues.Clear();” in World.cs, the new list (which the property is using) is not cleared.

      It also looks like the random number generator sometimes has a problem, due to floating-point rounding. I changed line 30 of RandomNumberGenerator.cs, to [hopefully] prevent this problem. Use this new line:

      return Math.Max(Math.Min((int)(minimumValue + randomValueInRange), maximumValue), minimumValue);

      Let me know if that doesn’t work, or if you have questions.

      1. I made both changes you told me, and it’s still giving me the same error every time I come back to a location where I have an unfinished quest. Thank you!

        1. I’ve uploaded my code for the changes at: https://gist.github.com/ScottLilly/3921233171904b6e68c97ca36922231b

          Can you try that and see if it fixes the errors? If it doesn’t, can you change line 125-126 of SuperAdventure.cs to this:
          List dialogues = newLocation.QuestAvailableHere.QuestNotFinishedDialogues;
          int index = RandomNumberGenerator.NumberBetween(0, dialogues.Count()-1);
          rtbMessages.Text += dialogues[index]+Environment.NewLine;

          Then, set a breakpoint on the last of those lines (where you get the dialog text from the list) and see what values you have for “index” and for “dialogues.Count”?

  9. Hi. I stumbled upon your site by accident and am really interested in following your tutorial, but I hit a wall. I use Ubuntu and am using VS Code. I have no idea of how to start a windows form application in it, searched everywhere and nothing. Is there a way to do it or am I searching in vain…? And if not, can I still follow the lessons only typing the scripts? Thank you very much.

  10. I just can’t describe how much more logical your pdf book is than other texts I’ve looked at. This is such a great book for an admin that needs to cross over to programming. Amongst other problems, other books have different projects each chapter but there is something about having a single project all the way though – in relation to knowledge nothing can be skipped, put out of order or assumed. Very very good read. And the short chapters are ideal for not getting overwhelmed and lost in pages of what seems at the time pointless theory for when your first starting out.

  11. Hi Scott,
    its me again, so after the help with the chance of spawning monsters; I been adding in more items and monsters; I thought of changing the players starting stats and items so i tried that. It seems that i broke the player, and i reverted the changes and it seems that the player is still broken. You think you can take a look at my code again and tell me what went wrong.
    the only thing I changed was it receiving a singular string to tell me what class to pick but I taken that off for now.

    1. When I ran it, I had a problem with the “_player” variable being null in GAME.cs. But, that might be because I don’t have your database or saved game file, and was trying to create a default player object. The problem I had was on line 35, of GAME.cs. You instantiate a player object, but that is not assigned to the “_player” variable. So, I see a “null reference” error when the rest of the program tries to get values from “_player”.

      When I changed line 35 to “_player = Player.CreateDefaultPlayer();”, it looks like the game is working for me. You could also have a problem if you changed your database structure, or XML saved game file format, for the changes you made, but didn’t change them back when you reverted the changes.

      If the change I made doesn’t fix your game, try setting a debug breakpoint in PlayerDataMapper.CreateFromDatabase() and Player.createplayerforxmlstring(), and use F10 to step through it to see exactly which line has an error.

      Let me know if that does not fix the problem for you.

      1. oh, i forgot to make it _player = player.CreatedefaultPlayer(); well that was the bug and didn’t realized it. I really haven’t work with Database yet, i guess i just kept over looking it.

Leave a Reply

Your email address will not be published. Required fields are marked *