Press "Enter" to skip to content

Learn C# by Building a Simple RPG

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.

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: If you already know the basics of C# programming (classes, properties, functions, “if” statements, etc.), you might want to look at the newer “Build a C#/WPF RPG” lessons. The code in those lessons is more like how I would write a “real” professional program – using better design and architecture.



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 Edition

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!


  1. Dennis
    Dennis November 4, 2016

    Here you go:


    Didn’t work on it for a while and was frustrated after several trys 😀

    Hope you can help me with it 😉

    • Scott Lilly
      Scott Lilly November 6, 2016

      I had some code to allow spawning different monsters at a location. However, I could not find it.

      I’ll write something new, post it in a few days, and let you know when it is ready.

      • Dennis
        Dennis November 7, 2016

        Thanks a lot. That would help me very much

  2. Ali
    Ali November 13, 2016

    hi Scott ,i have visualy looked at few of your lessons and i think there good.i need some advice on whether i should go through them all,i am currently struggling to learn c# i want to learn it so i could make games on unity  ,do you think its worth my time? like its alot of lesson and then its like what if i dont need alot of what ive learnt from the lessons-for unity scripting .

    i would be grateful if you would reply.


    • Scott Lilly
      Scott Lilly November 13, 2016

      If you only want to create games in Unity, you might want to follow a different guide – one specifically for Unity.

      This is more of a guide for learning C#, while using Visual Studio. The “game” is not the main focus. Unity has a different editor, and (I believe) built-in classes and libraries, that are designed to build a game faster. The Quill18Creates channel on YouTube looks like a good one for Unity.

  3. Joshua
    Joshua November 25, 2016

    I’ve been having issues with Lesson 16. I’m attempting to run the program to see if the MoveTo function works, so I may not be supposed to do that, but I would assume that the thing could run before I add the battle logic, right? Well, I keep getting an error on this line while building and running it in debug.

    rtbMessages.Text += “You receive the ” + newLocation.QuestAvailableHere.Name + ” quest.” + Environment.NewLine;


    Is this going to be fixed in the next lesson or did I do something wrong? It’s more likely the latter since I messed up the Quest part and I had to go back to PlayerQuest and fix the constructor.

    • Scott Lilly
      Scott Lilly November 25, 2016

      That error should not be happening.

      There are four classes where I think the error might be happening, but I would need to see the source code, to find the exact location. Can you upload your solution (and all the files, in all the solution’s sub-directories) to GitHub, or Dropbox, so I can look for the error?

      • Joshua
        Joshua November 25, 2016

        Alright, I made an account on GitHub and uploaded it here.

        • Scott Lilly
          Scott Lilly November 26, 2016

          Double-check the SuperAdventure.cs class with the code here: Lesson 16.1 source code. In the MoveTo function, it looks like there is a line missing – line 100. This is the “if” statement that checks if there is a quest at the location. If you correct the MoveTo function to have that condition, the error should go away.

          Please let me know if that does not solve the problem.

  4. Joshua
    Joshua November 26, 2016

    Oh! Okay, I looked at it some more since I was a bit bored waiting, and saw that it was forcing it to look for a quest and give the player a quest in a no quest area. That was why it’s null. I added a simple if statement just checking if there was a quest, and if there wasn’t, it would skip that whole block of code. I showed my changes in a new branch on GitHub. I think it would have been fixed once I got to the refactoring though.

    • Scott Lilly
      Scott Lilly November 26, 2016

      That’s the basic solution. However, you would probably want to add that “if” at line 100, instead of line 234. That will correctly match it with the “else” statement on line 232.

      Because that code has several levels of curly braces, it might be better to copy the code for that function from the Gist page, and paste it over the current MoveTo function. It’s easy to get something out of position, when the “if: statements go that many levels deep (something we would want to clean, in a more “professional” version of this program).

      • Joshua
        Joshua November 26, 2016

        Just saw your above comment to my original post. I basically just did what you did in a different position. Thanks for the help and definitely for the entire tutorial.

  5. Raven6990
    Raven6990 November 30, 2016

    SoI’ve been working on this for about two weeks now and I’m at the point where I am adding my own stuff into it. However I have an issue when I go to debug the program. I get a NullReferenceException that seems to be linked to the player.XML file. When ever I delete the file, it loads up normally, obviously without any of the saved stuff from the previous run.


    I am getting the error in player.cs line 151. I am going to try to do a release to see if the issue persists there as well.


    Link to player.cs on pastebin:


    Appreciate your help, without this tutorial I would never of gotten as far as I have.

      • Scott Lilly
        Scott Lilly November 30, 2016

        I didn’t see any obvious errors.

        Can you run the game, exit from it (to save the XML file), and see if the XML file has a value in the “CurrentLocation” node? If there is a value there, make sure you have a location with that ID in your World class. That’s the only other thing I can think of right now.

        If you want to see what is happening when the game reads and writes the XML file, you could use the debugger (tips for how to use it, in this article). I’d set breakpoints in the Player class, on lines 100 (to see what is happening when the game reads the XML file), and line 571 (to watch what is being written in the XML file).

        If that doesn’t help you find the source of the problem, I would need to have the complete solution, so I can run the game, and watch what is happening. You could upload it to GitHub, or Dropbox, so I could look at it.

  6. Raven6990
    Raven6990 December 1, 2016

    So I figured out the problem, it was in my world.cs and right after the connecting of the Cells to each other there is a place to add the locations to the static list, Noticed that the ones I could save in where listed there and the ones I could not save in where not, adding the ones not listed than going into the XML file and changing the location ID to some place that I could not previously load into, debug the game and it worked!


    Thanks for your help!

    • Scott Lilly
      Scott Lilly December 1, 2016

      Great! I’m glad you were able to track it down.

  7. Lost
    Lost December 5, 2016

    Hello Scott, great tutorial!

    Are you able to create a tutorial for “How to exclude hard-coded stuff”?
    My World.cs is growing so fast and the performence ist dying..

    Just for the “great parts” like the mapping-code, or the item-parts, that would be extremly amazing.

    Thanks for this lessons so far, great Job.


    • Scott Lilly
      Scott Lilly December 5, 2016

      Thank you! I’ve thought about creating a lesson (or an app) to help create the game data in an XML file, and edit it through a simple user interface. I’ll think about what it would require, and see if I can do that. I might need to do it in the new C#/WPF version of the tutorial – although it should be easy to adapt for people using the Windows Forms version of the tutorial.

      I’ll let you know when (or if) that is ready. It will probably take a while to do.

  8. Barbaros Bostan
    Barbaros Bostan December 10, 2016

    Hi Scott, thanks for the amazing tutorial. We just opened our “Game Design” undergraduate program this year and I am the instructor of the “Introduction to Programming” course given in C#. I covered the major topics using “The C# Player’s Guide” book and I will be grateful if I can use your tutorial and PDF document of the lessons for the remaining weeks. Can I integrate your lessons into my course?

    • Scott Lilly
      Scott Lilly December 10, 2016

      You’re welcome. Yes, you definitely may integrate these lessons into your course.

  9. Robert
    Robert December 10, 2016

    Hello Scott. There is something I have wanted to try, but I feel as though I am running in circles a bit. I was wondering if you might create a lesson that could help, either here or in your new tutorial. Or, should it be the case, if you might tell me which of the existing lessons could be adapted for what I am trying to do. To help explain, I added something for “fire damage,” and that can exist by itself as a “spell” or as part of another class, such as an “enchantment” on a weapon. Right now, though, I have each effect individually coded.

    What I hope to accomplish is twofold: first, to code the fire effect in one place that can be called by the spell or the enchantment, and second, to make it flexible enough that I could add, for example, a cold effect that could be run through the same method. Broadly speaking, I am curious if there is some efficient, reasonable way to code a modular system for applying varied effects with different classes. Would that be something you might add as a lesson, or would that be going beyond the “simple” scope?

    • Scott Lilly
      Scott Lilly December 11, 2016

      Hi Robert,

      I’m going to think about this for a bit. To do this well, it would be good to build some general-purpose “effect” classes – and that might get a little complex.

  10. Brock
    Brock December 20, 2016

    Um, So I’m using this tutorial as a sort of way to learn C# (Mainly because I really only need to learn the basics to start off and this is the only thing I’ve found that actually teaches me what I want and need to know without it just entirely flying by my head) Anyways I’m at the point of implementing health and damage and all that and I want to figure out how to do stats.

    My only problem with stats is that I have no clue what I’m doing. I’m sort of straying from the tutorial as I already know a bit of C# (Admittedly I learned what I do know from doing this tutorial at an earlier point). Really the only thing I’m stumped on is doing the stats. After that I’m basically home free and I know most of the rest.

    So I was wondering if you could help me figure out how to do the player stats? I mean I know how to set them but what I haven’t been able to figure out is how to access them in a battle.

    • Scott Lilly
      Scott Lilly December 20, 2016

      For the player stats, are you talking about things like strength, dexterity, constitution, etc.?

      If so, I would make them new public properties in the Player class. Then, I would modify the functions for attacking, and using potions. I would probably modify the lines that use random numbers – to determine if the player hits the monster, if the player is hit by the monster, and how much damage is done. If the player has a low dexterity, I might subtract five points from the random number that determines if the hit the monster (to make the player less likely to hit the monster). If they have a high dexterity, I would add five points. If they have a low strength, maybe subtract one point when we calculate how much damage they do to the monster. For a high strength, add a point of damage.

      Then, be sure to modify the player save and load functions to include the new properties.

      Does that make sense?

  11. Brock
    Brock December 24, 2016

    Yes, Thank you!

    And I guess since it’s almost christmas, Merry Christmas.

    • Scott Lilly
      Scott Lilly December 24, 2016

      You’re welcome, and Merry Christmas to you!

  12. Ash
    Ash December 26, 2016

    I finished the lesson 16.3 but when I start debugging nothing happened pls help

    • Scott Lilly
      Scott Lilly December 27, 2016

      What are you trying to debug? Do you see any errors, when you try to run the game?

  13. Reg Smith
    Reg Smith January 12, 2017

    I went through the tutorial and my program worked but there were a few logic errors on my end connecting things, I wanted to just use the source code so I could alter it to add things to the game at this point.

    I tried copying all of the source files into Visual Studio from the Lesson Folders but they are giving me errors. Would we be able to get a final source folder specifying which files go into Engine and which files go into SuperAdventure? Or even just a Visual Studio project folder?

  14. Alex Coronas
    Alex Coronas January 18, 2017

    Hi! First of all, i’d like to thank you for this lessons, they helped me with OOP ( I’m kinda bad at working with objects/classes ).

    I’ve a question that’s not about the code itself. As much as I know, having too many classes is a bad practice, so i was wondering why did you use derived classes.

    Is it bad practice? Or there’s a reason to do it in this situation?

    • Scott Lilly
      Scott Lilly January 18, 2017

      You’re welcome.

      A big reason why I used derived classes was to show how class inheritance works. If I were creating this game as a “real” program, I would design many things differently. However, for a tutorial, I wanted to show different things you may encounter when creating your own programs.

  15. Ross Curry
    Ross Curry January 31, 2017

    Greetings Scott Lilly! I am having some problems with my program specifically the SuperAdventure.cs I get so many errors and lots of ambuguity. I am new to programming and I would imagine there may be others with the same problems. I followed each step but I think when you were cleaning code I could have missed something. Thanks so much for the free lessons I’ve actually gone through it all twice. First I coded myself and couldnt run but second time I went through copy and pasting and I still coulding run program/

    Thanks so much for you time! It is greatly appreciated

    • Scott Lilly
      Scott Lilly February 1, 2017

      Hello Ross. Can you upload your solution to GitHub or Dropbox? Then I can look at it and try to find the source of the errors.

      • Tobias Ripper
        Tobias Ripper February 2, 2017

        Guide works as intended, thanks Scott.

          • Tobias Ripper
            Tobias Ripper February 13, 2017

            Would be awesome to have sort of guide/tutorial for working with TCP/IP, WinSockets, COMUART and other stuff related to IoT and development. I’m working as hardware developer and doing simple devices with MCU and number of perepherials. Had to dig into C# since I need to accept and visualise received data.
            Any plans?
            Best Regards,

          • Scott Lilly
            Scott Lilly February 13, 2017

            I haven’t done anything with hardware yet – although, there are a lot of tempting devices available nowadays. After I finish my current projects, I’ll see if I can come up with an interesting project.

  16. Tobias Ripper
    Tobias Ripper February 14, 2017

    You can get starter kit for 10$-30$ and this will give you all necessar stuff to get into hardware development. Believe me, its exciting! If you have basic electronics knowledge – this wont be a problem to master fast, since MCU requiers C / C++ code.
    You can upgrade Super Adventure guide adding hardware features. If you’ll need help choosing hardware and parts – I can help you with it

    • Scott Lilly
      Scott Lilly February 14, 2017

      Thanks! I’ll get in touch with you when I’m ready to start looking at the hardware.

  17. Konstantin
    Konstantin February 24, 2017

    Hello, I have a bit of a problem. I followed the guide and I think I did everything properly. The problem is in Program.cs, at the line:
    Applocation.Run(new SuperAdventure());
    I found a comment about this issue, wrote it as advised, and now it underlines SuperAdventure() and when debbuged, nothing happens. The error says “This name space is aviliable but is not a valid type”, or something like this. I read the code, tryed to imply logic to find the problem, but I can’t detect nothing. My form name is SuperAdventure, my Class is called SuperAdventure, and I have:
    public SuperAdventure()
    in the SuperAdventure.cs.
    Where am I wrong, or what have I missed?

    • Scott Lilly
      Scott Lilly February 24, 2017

      If the project had a different name, and you renamed it, that could be a source of the problem. If so, was it Lesson 02.2B that you looked at?

      If that doesn’t fix the problem, can you upload your solution to GitHub or Dropbox, so I can look at it?

      • Konstantin
        Konstantin February 26, 2017

        Yup, all set and done. It appears, that I somehow missed this lesson. Thank you a lot and sorry for wasting your time. I should have looked better :/

        • Scott Lilly
          Scott Lilly February 26, 2017

          Cool! When programming, it’s easy to miss one small thing – which can have a big impact on the program. Please let me know if you encounter any other problems.

  18. J
    J March 10, 2017

    Hey Scott. Last year I followed your tutorial. Before that I studied two full microsoft books on C#. But your tutorial helped me get some minor practical experience and after that I took a pragmatic course on mvc5 and EF 6. All this helped me to start my career as a Junior developer in .net, but I feel your simple tutorial helped me a lot in getting my feet wet from just theory to practical stuff 🙂 So I especially came back here to thank you man. You’re a boss! 🙂 thanks!

    Kind regards,

    J from the Netherlands.

    • Scott Lilly
      Scott Lilly March 10, 2017

      That’s great! Programming can be an amazing career – especially if you like learning new things. Best wishes in your job! 🙂

  19. Jackman10k
    Jackman10k March 13, 2017

    Hello, Mr. Lilly.

    As my quest to expand on the game continues, I’ve finally gotten around to trying to add a very crucial feature: NPCs that the player can just talk to. I’ve made an NPC class, added a List of NPC to the Location class, added some NPCs to a location in World.CS, and run into trouble. When the game first starts and tries to add an NPC to a location, it fails and the game can’t start. It tells me is this:

    “An unhandled exception of type ‘System.TypeInitializationException’ occurred in ClassLibrary1.dll

    Additional information: The type initializer for ‘Engine.World’ threw an exception.”

    Naturally, I’ve looked up the error, but none of the common fixes seem to work for me. I’ve stepped through the debugger numerous times, and I’ve noticed that the exception is thrown when World.cs tries to assign the NPCs Name variable with the information that was sent to the constructor. On the line ‘Name = name’ in NPC.cs, it immediately goes to the CreateDefaultPlayer function in Player.CS and gives me the above error message. Are there some advanced debugging techniques I can use to try to resolve the issue? This seems like a very unusual problem.

    • Scott Lilly
      Scott Lilly March 14, 2017

      That is the error you get when a static “constructor” methods fails. Unfortunately, it isn’t a useful error message. 🙁

      Are you building the NPCs in the World class, like the items, monsters, etc.? Is there a private List variable? Do you have a PopulateNPCs function that creates new NPC objects, and adds them to the list variable? Do you call the PopulateNPC function before you call the PopulateLocation function – so the list will have the NPC objects created, before you try to add them to the list of NPCs available at a Location? Do you have an NPCByID function?

      If you’re having trouble finding the problem with the debugger, you might try commenting out (or removing) the new NPC code. Then, add one step at a time (create the private List variable, create an empty PopulateNPC function, add it to the “constructor”, have PopulateNPC create a new NPC object, have it set property values on the NPC object, have it add the object to the private List variable, etc.) After each step, run the program and see if it works. If you get an error after adding one of the steps, then you know exactly which line has the problem.

      • Jackman10k
        Jackman10k March 14, 2017

        I’d been building the NPCs in World.cs, but now I’ve made it more nuanced with PopulateNPCs (which is called before PopulateLocations) and NPCByID. I imagined implementing them more like vendors, which were built during PopulateLocations, which gave me an idea after reading your response.

        If I have an area contain just a single NPC rather than a List, the game runs just fine, but I have my doubts that it creates everything properly. As soon as I make it back into a List, the exact same problem persists where trying to add to the List causes the program to end up in CreateDefaultPlayer in Player.cs and World.cs throws an exception. The new additions helped me see the problem more clearly, however.

        What actually happens is that, for some strange, strange reason, somewhere between trying to add the NPC to the Location and throwing an exception, ItemByID is called. This can’t be seen when NPCsLivingHere in Location.cs is a List, but can be viewed through the debugger just fine when there can be just one. Because of this sudden turn to ItemByID, I’m fairly certain that, even though the game runs, the test-NPC I’m using isn’t created correctly. I haven’t the slightest idea why this happens, as I’m sure that “voodoo magic” is an unacceptable answer.

        In following your advice, I’ve learned more about the issue I’m having, but am somehow also more confused. ItemByID being called during a line that doesn’t call it and then subsequently moving to another class that has no relation to the function from before goes against every convention of logic I’ve been taught in programming. You’ve already given me such great advice, but I have to ask since you have so much more experience than me: Can you make sense of this? Do issues like this happen often?

        Anything you can tell me would be greatly appreciated.

        • Scott Lilly
          Scott Lilly March 15, 2017

          Are you familiar with using the callstack, for debugging? I haven’t created anything on how to use it, but this article might help you. That should let you see how the code is getting to the ItemByID function: which function called it, and which function called the previous function, etc. I’m certain it’s not “voodoo”.

          If you try that, and can’t find the source of the problem, please upload the current version of your program to GitHub.

          • Jackman10k
            Jackman10k March 17, 2017

            I found the callstack very useful, though it technically brought me no closer to finding the source of the trouble.

            What actually happens is that, after going through the Designer file and there is no save file found, the game starts to create the default player and goes to World.cs when it starts to add the rusty sword to the player’s inventory. The program seems to go through the entire World.cs file at this time, including creating and trying to add NPCs. Whatever causes the static constructor to fail and cause the exception to be thrown is still a mystery to me, but at least it now makes sense why the program ends up in CreateDefaultPlayer.

            Here is the link to the project on GitHub: LINK REMOVED FOR PRIVACY

            I’m very much looking forward to learning what the heck is going on. As many times as I’ve looked, I still don’t see anything outside the ordinary with the code I’ve written.

            Here are some code lines that will be helpful to know, I think:

            -A List of NPC NPCsLivingHere is created on line 29 of Location.cs
            -NPCs are populated starting on line 155 in World.cs.
            -An NPC is added to a location on line 194 in World.cs.
            -NPCByID starts on line 414 in World.cs.

            I thank you in advance for your time.

          • Scott Lilly
            Scott Lilly March 17, 2017

            I made a video of how to track down this type of error – along with a solution for the error in your version of SuperAdventure:

            Please let me know if you have any questions.

  20. Jackman10k
    Jackman10k March 18, 2017

    YouTube is telling me that the video is unavailable. I’m not seeing anything very-recently uploaded on your channel, so I assume something went wrong quickly after the video was put up.

    • Scott Lilly
      Scott Lilly March 18, 2017

      Can you try it again? I thought I set it to “Unlisted” (so it could be linked to, but wouldn’t show up in the list of all other videos). However, it was still “Private”.

      • Jackman10k
        Jackman10k March 19, 2017

        Excellent! But I can’t help but feel a little foolish for not realizing I had to initialize the List first. I highly doubt I’ll ever forget again though, and your approach to debugging it has been noted and kept in mind. I’m glad it was just a misstep on my part, as some people, according to what I’ve read, had this error for reasons beyond their control.

        As always, you have been a great help, Mr. Lilly. I feel like I’ve learned in about a month what I’d gotten in a whole semester.

        • Scott Lilly
          Scott Lilly March 19, 2017

          There is a saying, “Good judgement is the result of experience. Experience is the result of bad judgement.” In programming, the lessons we remember best, are often the ones we learned from our bugs. 🙂

Leave a Reply

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