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!

 

SECTIONS

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 Express 2013 for Desktop

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!

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

  1. Hello again, Mr. Lilly,

    It has been a while since you last helped me in my endeavors to learn C# and programming in general. I don’t have any questions for you this time, but some news. 10 months after graduating with my BS in CS, I’ve finally managed to break into the professional field by landing a paid internship. I cannot overestimate how ecstatic I am. I’ve reason to believe that part of what put me over the edge of other potential employees (if they existed) was that I had demonstrative proof of having worked on a project of some sort. Among the coding examples I provided after the initial interview was the project I started after finding this site. Though there is no object-oriented programming going on in this position, several portions of the project either hinted at or was directly related to the skills deemed requirements, specifically knowledge of XML.

    I just wanted to let you know that you and your tutorials helped pave the way to my success. I know I don’t know you very well, but I admire your work and your willingness to teach.

    I don’t believe I can thank you enough.

    1. Congratulations! That is great news!

      You also learned two important career lessons, very early on. Invest time to improve your skills (or learn new skills), and have something you can demonstrate to employers. Many programmers wait for their employers to “give them an opportunity” to learn something new. A few programmers “make their own opportunities”. They experiment with new technologies, and post their code samples online. When a new position comes along, it’s easy to guess which type of programmer will be selected for it. Even if you only put in a few hours every week, you can see some amazing results over the years.

      Best wishes to you, in your new career.

  2. I found this tutorial incredible and worthwhile – so thank you for the lesson. Do you have any other tutorials that would show basically the same thing but with graphics?

  3. Hi Scott,

    This is really a great tutorial and easy to follow. I finished the basics (upto Lesson 16.3) and tried to run the game but I am getting this exception message:

    An unhandled exception of type ‘System.TypeInitializationException’ occurred in SuperAdventure.exe

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

    This occurs at Line 25:
    24 _player = new Player(10, 10, 20, 0, 1);
    25 MoveTo(World.LocationByID(World.LOCATION_ID_HOME));
    26 _player.Inventory.Add(new InventoryItem(World.ItemByID(World.ITEM_ID_RUSTY_SWORD), 1));

    Can you help me resolve this exception?

    Thanks,
    Vyyom

        1. The Quest class is missing the initialization of the QuestCompletionItems property. Look at line 26, in the constructor from Lesson 10.1, to find the missing line.

          I also noticed there are no eventhandlers for the movement buttons. So, the player cannot move.

          To fix this, you can open SuperAdventure in Designer (graphic) mode, click one time on each movement button (do not double-click on them). Look in the “Properties” section of Visual Studio (the lower-right corner). Click on the lightning symbol, to see the events. Look for the “Click” event, and select the right movement function for each button (for example, “btnNorth_Click”, for the “North” button). This is what the “Properties” section looks like:
          null

  4. Hey Scott,

    First of all this tutorial has been great so far I’m loving it!

    But I’ve come to a stand-still, I can run the program without errors but It doesn’t seem to be generating the quests or monsters in the game and I can’t for the life of me figure out why?

  5. Hi Scott,

    First of all thanks you for your tutorial, its has been a great help for me.

    The tutorial is easy to understand even for me who just learn C#.

     

    Well I have question is there any code that let Exp need to level up become much bigger every level or every 10 level something like that?

    for example: Level 1 need 100 exp / level 2 need 120 exp and etc or something like that

    I have try check other site but I a bit can not understand what are they talk about.

    well this is just a question and my curiosity to know is it possible and how to do it.

    1. You’re welcome!

      The simplest way to that would be using “if… else if”. You could write code like this:

      if(ExperiencePoints < 100) { return 1; ) else if(ExperiencePoints < 220) { return 2; } else if(ExperiencePoints < 360) { return 3; }

      You would need an "if else" for each level. So, you will probably have a maximum level - because you probably don't want to write 100 "if else" statements. 🙂

      Please let me know if that won't work for you, or if you have any trouble adding it to your game.

      1. Yeah this one I need “if(ExperiencePoints < 100) { return 1; ) else if(ExperiencePoints < 220) { return 2; } else if(ExperiencePoints < 360) { return 3; }”

        but then again I have trouble in adding it
        I add this in player at public int level

        get {return ((ExperiencePoint / 100)+1) }

        set
        {
        // Player at level 1
        if (ExperiencePoints < 100)
        { return ; }
        // Player at level 2
        if (ExperiencePoints < 170)
        { return; }

        and etc

        output: level still need same experiencepoint “100”.

        but it still fail and sometime when i try to adding it again it the become like this.            ex:  230 experiencepoint = 230 level or 230 Exp = 231 level

        so is it the problem in my way of statement or I put the code in wrong place              (Player , level)

        1. Because we never assign a value for the Level (it is always calculated from the ExperiencePoints), we do not need a “set” for the Level property – only a “get”. So, the property could look like this:

          public int Level
          {
          get
          {
          if(ExperiencePoints < 100) { return 1; }if (ExperiencePoints < 240) { return 2; }return 3; } }

          When the program tries to read the Level property, if the player's ExperiencePoints are below 100, the code will return "1". Because it is returning a value, the lines of code after "return 1;" will never be run. If the Player has more than 100 ExperiencePoints, the code will continue to run. It will see if ExperiencePoints is less than 240. If so, it will return 2, and stop running. If it is greater than (or equal to) 240, the code will continue running, and execute the final line "return 3;". So, for this example, the maximum level is three.

          Please let me know if that is clear, and if you are able to get this working in your version of the game.

    1. Hello, M.R Lilly thanks for your amazing tutorial but I am getting a problem at Lesson 05.1 – Creating objects from classes. It is that one red line is appearing on _player = new Player();
      CurrentHitPoints = 10;
      MaximumHitPoints = 10;
      Gold = 20;
      ExperiencePoints = 0;
      Level = 1;

      lblHitPoints.Text = _player.CurrentHitPoints.ToString();
      lblGold.Text = _player.Gold.ToString();
      lblExperience.Text = _player.ExperiencePoints.ToString();
      lblLevel.Text = _player.Level.ToString();
      And Yes I have typed (“using Engine;”) and (“public partial class SuperAdventure : Form
      {
      private Player player;
      public SuperAdventure()
      {
      InitializeComponent();
      }
      }
      }”)
      Sir, Please help me!!!

      1. There are three common source for this error:

        1. In your SuperAdventure.cs, do you have “using Engine;” on line 10 (from Step 3)?
        2. If that line is present, is the Player class public (step 3 from lesson 04.1)?
        3. Does the UI project have a reference to the Engine project (step 6 from lesson 02.2)

        If those do not solve the problem, please tell me, and we can look more.

  6. Hi Scott,

    First would I like to thank you for the fantastic C# Simple RPG lessons.

    This is what I’ve been looking for in my efforts to learn programming and game development.

    After some heavy debugging have I finally got the game to work. 🙂

     

    Now to a question;

    I like my game to be a bit more Visual and would like to load different Pictures to every location that you enter in a picturebox.

    I have searched the net for some answers but with Little luck.

    Is there a way for a total beginner to do this?

     

    Best regards

    Olle

    Here’s a screen of my build:

    https://www.dropbox.com/s/mvvbfb1mbtpgoe1/Sk%C3%A4rmklipp%202017-08-03%2016.00.40.png?dl=0

    1. Hello Olle,

      You’re welcome! Adding graphics to a Windows Form program is a little difficult (WPF programs are a little simpler). Here is a way to do it:

      Adding the images to the solution
      1. In the SuperAdventure project (the UI project), create a new folder to hold the images. I will name the folder “Images”, for this example.
      2. Right-click on the Images folder and select Add -> Existing Item…
      3. Select the image files and click the “OK” button, to add them into the project.
      4. Select all the image files, right-click on them, and select Properties.
      5. Set “Build Action” to “Embedded Resource”
      6. Set “Copy to Output Directory” to “Do not copy”
      7. Now, when you build the solution, the image files will be embedded inside the SuperAdventure DLL (assembly).

      Connecting the image file to the location
      1. Add a new string property to the Location class, to hold the name of the location’s image file. I will name this property “ImageFileName”.
      2. Modify the Location constructor to accept the ImageFileName as a parameter, and assign the parameter value to the property.
      3. Modify World.PopulateLocations() to pass in values for the imageFileName parameter. This should be the file name, with the extension.

      Displaying the image in the picturebox
      For this example, my PictureBox control is named “pboxLocation”. Make these changes to SuperAdventure.cs:
      1. Add “using System.Reflection;” with the other “using” statements at the top of the class.
      2. Add this class-level private variable (like _player): “Assembly _thisAssembly = Assembly.GetExecutingAssembly();” This variable points to the DLL (assembly) that is running. In this case, that will be the SuperAdventure DLL.
      3. Create a new DrawLocationImage function (the code is below)
      4. In the button movement functions (btnNorth_Click, etc.), after calling the movement function on the _player object, call the DrawLocationImage function.

      NEW FUNCTION
      NOTE: If you did not name the image file folder “Images”, you will need to change the value in the function from “.Images.” to yout folder name (with a “.” before and after it).
      This function gets the embedded image file as a stream (a long string of bytes), creates a new Bitmap from the stream, and sets the PictureBox to the Bitmap.

      private void DrawLocationImage()
      {
      using (Stream resourceStream = _thisAssembly.GetManifestResourceStream(_thisAssembly.GetName().Name + ".Images." + _player.CurrentLocation.ImageFileName))
      {
      if (resourceStream != null)
      {
      pboxLocation.Image = new Bitmap(resourceStream);
      }
      }
      }

      That should let you display images in the Windows Form UI. Please tell me if it does not work, or if you have any questions.

  7. Hi Scott,

    Great tutorial – really enjoyed working through these lessons and having the satisfaction of a working program throughout.

    The one aspect of the game i’m struggling to develop having completed this tutorial is a map. I’d like to ideally be able to show the user a grid of the locations they have visited and an icon to show their current location.

    I’ve created a form that can be accessed from the main screen with a map button but thats as far as i’ve gotten. What could i used to represent the tiles? Any hints would be much appreciated and thanks once again for putting the time and effort to make this tutorial available for everyone.

    Dan

    1. Thanks Dan,

      I’m going to work on something this weekend, to show how to add some images to the game. A few people have asked about adding graphics, and I can probably add a quick lesson to show the basics.

  8. Hello All
    @ Scott , thank you for providing these Tutorial , looking forward to learning more and trying the WPF version soon when sort some things out

    I have encountered a Bug and seeking Advice on fixing it please,

    Also Is the Bug in All or Just mine something I missed ?

    Bug =
    If log of On A Map that provides Quest (Alchemist Hut, Farm House)
    When Try to log back tries to Add Another Quest resulting in duplicates of that Quest in the Data Grid List.
    On Location Alchemist Hut Can still Log on Just displays the Duplicates of the same quest and can do the quest again

    However on Location Farm House when try log on Receive Exception Throne And Client Crashes Also crashes if try complete the quest again (visit location with required items)
    strange different location respond differently to the bug ?

    Exception Message is:
    System.InvalidOperationException {“Sequence contains more than one matching element”}

    Exception thrown on the line is:
    ( PlayerQuest playerQuest = Quests.SingleOrDefault(pq => pq.Details.ID == quest.ID); )

    in this part of Player class

    private void MarkPlayerQuestCompleted(Quest quest)
    {
    PlayerQuest playerQuest = Quests.SingleOrDefault(pq => pq.Details.ID == quest.ID);

    if (playerQuest != null)
    {
    playerQuest.IsCompleted = true;
    }
    }

    Following Debug Tutorial Tracked the quest being added at Line;
    // Add the PlayerQuest to the player’s property
    player.Quests.Add(playerQuest);

    in section in the PlayerDataMapper Class
    if (reader.HasRows)
    {
    while (reader.Read())
    {
    int questID = (int)reader[“QuestID”];
    bool isCompleted = (bool)reader[“IsCompleted”];

    // Build the PlayerQuest item, for this row
    PlayerQuest playerQuest = new PlayerQuest(World.QuestByID(questID));
    playerQuest.IsCompleted = isCompleted;

    // Add the PlayerQuest to the player’s property
    player.Quests.Add(playerQuest);

    }
    }

    1. Sorry Forgot To Add To the Original Post

      the bug Also Was Born In the Final section of the tutorial, New Game features with the lessons 25.1, 26.1 , 26.2

  9. LINK REMOVED FOR PRIVACY

    uploaded all the solution ,
    for the upload I changed the sql string from my actual computers name to ‘ComputerName’

    thanks much appreciated

    1. OK. This is what is happening.

      In the PlayerDataMapper (at line 55), we call Player.CreatePlayerFromDatabase(), to create the player object from the SQL saved game data. This function moves the player to the CurrentLocation in the saved game data. This fires an event, which gives the player the quest available at that location.

      Next, back in PlayerDataMapper (at lines 71 to 82), we add the player’s quests from the saved game data. This gives the player the same quest, which was in the SQL saved game data. This causes the problem. Later in the code, we cannot handle the player having the same quest twice. So, the program has an error.

      I have fixed versions of Player.cs and PlayerDataMapper.cs at: https://gist.github.com/ScottLilly/f1fd371afe5ee1b0fabf35bd36a1d923

      In Player.cs, I commented out line 158, where we move the player to the CurrentLocation from the SQL saved game data.
      In PlayerDataMapper.cs, I moved the currentLocationID variable to line 22 (so can use it outside the “using” section from lines 25 through 59). Then, on line 134 (after the saved quests have been added to the player object), I set the player’s CurrentLocation property. It does not add the quest for the CurrentLocation because the player object already has that quest.

      I’m going to test this some more, and update the lesson, after I know everything works.

      Thank you for telling me about the bug.

  10. Hello Scott , How is all
    I have been Working on My Modified Version
    I have Successfully Added Progress Bars To show player progress of each Main Level And Progress Bars For Player Health And Mana,

    But I am Having trouble With Adding The Display/progress bar , For the Current Monster

    in the SuperAdventure.cs , codes Fairly Commented
    Theres A Void for All display Bays then gets called from in the OnPropertiesChange Void, I Assume this was good spot for it so updates if anything changes

    Any Advice or Re-Arranging How And where I’m doing the bars And how to Get the Monster Bar Working,
    would be much appreciated thanks

    https://www.dropbox.com/s/pd7dulxor9kxszn/SuperAdventure001v04.zip?dl=0

    current updates; (values not finalized some are low for easy testing)
    -3 monster,
    -1 quest,
    -Level System Modded, current level Required Experience rises as level Rises
    -Magic Spell item Added, Can Attack or Heal
    -2 new Location, Atm mostly Trial Adding And learning < So need Plan them properly And make images
    -Skill System, Currently Have Skill For: Attack, Shield, Magic .. SKill effects total battle damages
    – Removed Heal Player after Battle And Roam location , still heals after death or visit Home(i think i added home heals if not its a to-do thing)
    – Mana Added For Spell Use
    – Heal Function Added To Trade Window, Atm cost 100 gold to restore Health And Mana to Players Maximam , Plan to create Health And Mana As separate buttons And Change The cost to calculate the amout restored and charge x Amount gold per 1 hit/mana point
    – Mana potion Added,

    1. Hi Mick,

      How are you doing?

      I downloaded the solution, but it looks like the original code, without your modifications. Do you have another version, or a different location I should look?

    2. Hi Mick,

      I think I have the fixes for you. The code is here: https://gist.github.com/ScottLilly/a91eeafb7ef960c640d041c26012da2d

      In the player class, I changed the CurrentMonster property from “static” to a normal property (on line 732). I also changed the CurrentMonster property to use a backing variable (line 69), and to raise a PropertyChanged notification (for when there is a new monster to fight).

      In SuperAdventure.cs, I modified the PlayerOnPropertyChanged function to also look for a change to the player’s CurrentMonster property (starting on line 353). If the CurrentMonster changed, and it is not null, we connect an eventhandler to the CurrentMonster (line 357). This will listen for changes to the monster’s properties – like CurrentHitPoints. Then, on 358, we call a new function (DisplayMonsterHealthProgressBar, on lin 368) that displays the progress bar for the CurrentMonster’s CurrentHitPoints. The eventhandler for the monster’s properties (CurrentMonster_PropertyChanged, on line 363) also call that function, to refresh the progress bar.

      On line 370, where the code calculates the percentage of the progress bar to show, I added a Math.Max with “0”. The progress bar cannot show a negative value. So, if the monster’s hit points are negative, we want to use zero. Math.Max picks the larger of the two numbers: zero or the current hit points.

      By the way, you made some cool changes to the game. I really like the progress bars for the hit points and skill progress.

      Let me know if you see any problems with the changes, or if you have any other questions.

  11. Good Thanks Scott ,
    Sorry About That it was supposed to be the correct one But after downloading a trying it was missing the projects , must of had error uploading or in the zip compression ,

    So I Have deleted and Re-Uploaded

  12. In The Main Code You Posted It Appears To Have the Player Code in The SuperAdventure file

    But I was Able To Find In The Revisions

    “” I added a Math.Max with “0”. The progress bar cannot show a negative value. “”
    have now done the same with players bars
    I had that problem of going below 0 but my solution was to add if statements which worked but took up a lot more code than Math.Max

    Currently Seems To Be Working Good, Thanks much appreciated

  13. Hi Scott,

    First of all, let me appreciate the job you did. If only other programmers presented such detailed projects. Now, I’ve just finished lesson 20.2 and feel great enthusiasm to go further.

    By the way, I’d like to inform a situation: after you do all the databinding stuff and execute  the program ( if exists, delete the PlayerData.xml file- let’s assume you play the adventure for the first time, use your weapon rusted sword but rather than select anything from cboWeapon, only click on useWeapon button), I encountered a null reference exception

    in the Player.cs file @ ToXmlString() function on the statement below:

    XmlNode currentWeapon = playerData.CreateElement(“CurrentWeapon”); currentWeapon.AppendChild( playerData.CreateTextNode(this.CurrentWeapon.ID.ToString())); stats.AppendChild(currentWeapon);

    Here, Player.CurrentWeapon returns null.

    I propose – of course you’ll find better solutions- for the 3rd edition of the booklet:

    XmlNode currentWeapon = playerData.CreateElement(“CurrentWeapon”);
    if (this.CurrentWeapon != null)
    {
    currentWeapon.AppendChild(playerData.CreateTextNode(this.CurrentWeapon.ID.ToString()));
    }
    else
    {
    currentWeapon.AppendChild(playerData.CreateTextNode(World.ITEM_ID_RUSTY_SWORD.ToString()));
    }

    Best regards,

    Can

     

    1. Thank you Can,

      There are some future lessons that fix problems with the default weapon, and the Weapons combobox. But, your technique is also a good way to prevent the error. To be very safe, you could also add code to the CreatePlayerFromXmlString() function, to set CurrentWeapon to the first Weapon in the player’s Inventory – and give the player a rusty sword, if they do not have any weapons. We should also set the CurrentWeapon property in the CreateDefaultPlayer() function.

  14. Dear Scott,

    I really enjoyed the journey of learning C# with your game!
    However, I would like to do something more with the game. Is it possible to make a monster to “disappear” after the hero kills him? Because (if I didn’t make any mistakes) after returning to the place where I killed the monster, it is still there.

    I would be very grateful, if you could help me with this.

    Best Regards,
    Krzysztof R

    1. Hello Krzysztof,

      After you defeat a monster, instead of setting CurrentMonster to a new monster, you could change the code to set CurrentLocation’s MonsterHere property to “null”. Then, if the player moves to that location again, the game will not create a new monster. If the player closes the game, and restarts the game, there will be a monster when they go to that location again, unless you also save and load the location information in the saved game data.

      Please tell me if that was not clear, and if you have any questions.

Leave a Reply

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