Lesson 10.1 – Creating collections of objects

Lesson Objectives

At the end of this lesson, you will know…

  • How to store groups of objects in one property or variable, and read them later.

 

How to handle groups of objects

You may have noticed that there are properties missing from some of our classes. When outlining the program, we mentioned that the player will be able to have items in their inventory. The player will have quests. Quests have items that need to be turned in, to complete them. Monsters have items that can be looted from them.

We have a Quest class, and an Item class, but we don’t have any place to store them in the Player or Monster classes. We also need a way to store more than one item, since a Player will probably have more than one item in their inventory and could have more than one quest.

We do this with lists, or collections.

But before we create these list properties, we’re going to need a place to store some additional information for the items and quests.

For example, with the items in the Player’s inventory, we need to store the quantity of each items they have. It’s similar to what we’ll need to do with the Quest class – store the quantity of each item that is needed to complete the quest.

For the player’s quests, we also need a place to record whether or not the player has completed the quest.

In the Monster class, we want a list of the items that can be “looted” from the monster. This is called the “loot table”. We need to store the item, the percentage of times it is “dropped”, and if the item is a default loot item – in case the random number generator says that none of the loot items were dropped.

There are different ways to do this, but I’m going to show you a way that uses some additional classes.

So, let’s create these new classes, then add them as “list” properties to the existing Player, Monster, and Quest classes.

 

NOTE: In the video, the Quest class is missing the RewardItem property from the last lesson (shown at 6:52). You should keep that property in your class.

 


Link to video on YouTube

 

Step 1: Start Visual Studio Express 2013 for Desktop, and open the solution.

 

Step 2: Create these four new classes by right-clicking on the Engine project and selecting “Add”, then “Class…”. The four classes to create are named:

 

InventoryItem.cs

 

LootItem.cs

 

PlayerQuest.cs

 

QuestCompletionItem.cs

 

NOTE: We use a new datatype for two of these classes – “bool”. This stands for “Boolean” and is used to store a value of “true” or “false”. For example, in the PlayerQuest class, the “IsCompleted” property will store a value of “false”, until the player finishes the quest. Then we’ll store “true” in it.

 

Step 3: Now that we have the new classes, we can create properties for them in the Player, Quest, and Monster classes.

Edit the Player class, by double-clicking on it in the Engine project.

Look at line two of the Player class. In order to use lists, one variable or property to hold a collection of objects that are the same class, we need to have this line included. This is where your program will find everything it needs, in order to work with collections.

Add these two properties to the Player class:

Now you have two new properties that can hold lists containing InventoryItem and PlayerQuest objects.

Then, in the constructor code, add these new lines:

These two lines set the value of the new properties to empty lists. If we didn’t do this, the value for those properties would be “null” – nothing. By setting them to an empty list, we can add items to them later, because you can add objects to an empty list, but you can’t add object to nothing (null).

 

Player.cs (complete, with changes)

 

 

Step 4: Edit the Quest class. Add this new property:

In the constructor, add this new line, so the QuestCompletionItems list will be ready to have objects added to it:

 

Quest.cs (complete, with changes)

 

 

Step 5: Edit the Monster class, by adding this property:

And add this to the constructor, so the new property isn’t “null”:

NOTE 1: We didn’t need to set the string, integer, and Boolean properties to a default value because those datatypes have a built-in default value. However, Lists are null (non-existent), until you set them to an empty list (a new List object, with no values in it yet).

NOTE 2: We didn’t create new parameters in the constructor to pass in values for these new properties. We could have, but we are going to populate them a little differently – a way that I think is a little easier for someone new to programming.

 

Monster.cs (complete, with changes)

 

 

Summary

Almost every program I’ve written has included some type of list of collection. In the real world, we usually work with several items (e.g., display all the deposits into, and payments from, a bank account).

We’ll show how to add to, and work with, the objects in these lists very soon.

 

Source code for this lesson

GitHub (new classes): https://gist.github.com/ScottLilly/161a5812ae4843563d6b

GitHub (updated classes): https://gist.github.com/ScottLilly/582dc1e93794d4d3c48e

 

Dropbox (new classes): Lesson 10.1A – https://www.dropbox.com/sh/r4p48mlpulxm951/AAAOIjiBYT7x_BeT9c7JIOaQa?dl=0

Dropbox (updated classes): Lesson 10.1B – https://www.dropbox.com/sh/ghceu0z37ljdszm/AAD9hKurtUImkLNUxwXZhi3ka?dl=0

 

 

Next lesson: Lesson 11.1 – Using a static class

Previous lesson: Lesson 09.1 – Using your classes as datatypes

All lessons: Learn C# by Building a Simple RPG Index

 

47 thoughts on “Lesson 10.1 – Creating collections of objects

  1. Is there a reason why we’re using Lists instead of other types that can store multiple things like arrays or dictionary(not sure if C# has this)?

    1. I generally use Lists when I want to store any type of collections. They’re easy to work with, especially with LINQ.

      They enforce type safety (only allow objects of a certain type), unlike arrays (which allow you to store objects of any type, in any element of the array). They are easy to add to, or remove from, unlike arrays that need to make copies to change the number of objects they hold.

      C# does have dictionaries. However, with dictionaries, you can only have one entry for each key value. For the Inventory, that key would probably be the inventory item object’s ID property. But I might want to change how the inventory works in the future, to have a limited number of slots, and have items that can only be stacked to a certain value in an inventory slot.

      For example, the player is limited to 20 inventory slots, and they can only stack 10 health potions in an inventory slot. If the player had 11 potions, there would be two inventory slots that hold the same item type, and have the same key value – which you can’t have in a dictionary. You can see how I’d handle this with the code here: https://scottlilly.com/how-to-build-stackable-inventory-for-a-game-in-c/

      So, for me, unless I have a specific need to use a different type of collection, I generally use a typed List (only holds object of one datatype).

  2. For some reason when using the player class in the UI the error log says that the player class, as seen here, does contain a constructor that takes 5 arguments, it is on line 23

      1. Error 5 ‘Engine.Player’ does not contain a constructor that takes 5 arguments C:\Users\Peter_school\Documents\DoFE\C#\VerySimpleGame\VerySimpleGame\VerySimpleUI.cs 23 23 VerySimpleGame

  3. I also got these inconsistent accessibility errorsError 4 Inconsistent accessibility: property type 'System.Collections.Generic.List' is less accessible than property 'Engine.Quest.QuestCompletionItems' C:\Users\Peter_school\Documents\DoFE\C#\VerySimpleGame\Engine\Quest.cs 16 42 Engine
    Error 1 Inconsistent accessibility: property type 'System.Collections.Generic.List' is less accessible than property 'Engine.Player.Quests' C:\Users\Peter_school\Documents\DoFE\C#\VerySimpleGame\Engine\Player.cs 14 34 Engine
    Error 3 Inconsistent accessibility: property type 'System.Collections.Generic.List' is less accessible than property 'Engine.Monster.LootTable' C:\Users\Peter_school\Documents\DoFE\C#\VerySimpleGame\Engine\Monster.cs 16 31 Engine
    Error 2 Inconsistent accessibility: property type 'System.Collections.Generic.List' is less accessible than property 'Engine.Player.Inventory' C:\Users\Peter_school\Documents\DoFE\C#\VerySimpleGame\Engine\Player.cs 15 36 Engine

    1. Make sure the classes and properties are defined as “public”. If the “public” is not included, that can cause this type of problem.

      If that does not fix the problem, can you upload your Player.cs, InventoryItem.cs, LootItem.cs, PlayerQuest.cs, and QuestCompletion.cs files somewhere I can review them (such as gist.github.com)?

  4. I’m using this tutorial to try and help me with an assignment.  It is a very good tutorial but as soon as things get complicated I get lost.  I don’t seem to be able to learn programming at all.  I need to program a c# calendar and I’ve failed it once already, now I’m on my 2nd chance.  I can’t read what the code is doing once it gets to a certain stage.

    1. It might help if you use the debugger while you run the program. If you set a breakpoint before any confusing areas, you’ll have x-ray vision into what the program is doing. It will let you see the functions/commands that are being run, and the values of the variables. You can slowly move through the the program, one line at a time, with the F10 and F11 keys.

      Here is a post on how to use the debugger How to use the Visual Studio 2013 debugger.

  5. Is there an easier way to do this instead of using properties (getters/setters) for someone who is new and confused?

    1. There are also “fields”, which can be used in place of properties. However, there are limitations with using them, so most programmers use properties.

      What are you finding confusing about properties?

      1. Different person, but I’m not sure I understand the {get; set;}. Are “get” and “set” functions that C# recognizes based on a library?

        And so the constructor takes a value based on what parameters are entered, sets that equal to the properties or variables (of the Player class for example), then the {get; set; } takes that value and does something, i’m not sure what, that sends the value…where? To the _player class that’s a child of the Player “blueprint” class, where it can be set equal to the controller on the form, yes. But how? Also, is _player a class and the variables within it are…objects? because the “_player.Gold…” seems like an object due to the period.

        I’m trying to connect concepts that I’ve sort of ignored a lack of understanding to, because usually it starts to make sense through time. Am I on the right track?

        1. I’ll answer some questions from the middle, to start.

          To get the terms precise, “Player” is the class – the blueprint. “_player” is an object, which is an instance of the “Player” class (and is also a variable). “Gold” is one of the properties of the Player class, which makes it a property of the “_player” object. The “.” before “Gold” is an indicator that “Gold” is a property of the “_player” object. Properties can also be objects (which can have their own properties). So, you could have code like this:

          _player.CurrentWeapon.Name

          In that line, “_player” is an object, “CurrentWeapon” is a property of the _player object; however, it is also an object, which has its own “Name” property.

          “get” and “set” are not exactly functions (for a technical answer). However, you can think of them as built-in “functions” to store values into, or read values from, a property. We have “get” and “set” because we sometimes need to do something extra, when accessing a property value. We can add the extra code into the “get” and “set” code – which we will do that in some later lessons.

          When you see “{get; set;}” after a property, that means the property does not use any special code. This is often called an “auto-property”. When you set a property’s value, the value is actually stored in a “backing variable”. However, for auto-properties, that is all done inside the .NET framework (and not visible in the source code).

          You can create your own backing variable for a property. The code would look like this:

          private string _name;

          public string Name
          {
          get { return _name; }
          set { _name = value; }
          }

          So, when you write myObject.Name = "Scott" in another part of your program, the string “Scott” is passed to the “set”, as the “value”. The value is stored into the backing variable “_name”. Whenever your program tries to get the value from the property, the getter returns the value from the “_name” variable.

          I just posted a video for the WPF version of this tutorial, which might help you understand what is happening. It talks about databinding (which you don’t need to know about right now). However, I also show how to convert an auto-property to a property with a backing variable – and explain how some of the values flow. The video is here:

          Please let me know if cleared it up, or if you still have questions.

    1. Thank you. I’ve got that fixed now. I’m glad you’re enjoying the lessons. It’s nice to see that they been out for a little over a year, and they’re still going strong.

  6. Hi Scott,

    Rather than just copying everything which will teach most people next to nothing, what I’m trying to is after your lessons I delete the classes and reproduce them myself.

    While trying to rebuild the ones from this lessons, I’ve come up with a question: how come you are creating a new type (InventoryItem) rather than just adding the Quantity property to the Item class and use a List<Item> as an inventory in the Player class?

    Thanks in advance for explaining.

  7. While thinking about my last question I figured it might be meant as a form of distinguishing between all items and those in inventory, but if thats the case then why not use inheritance to create an “InventoryItem” and only add the quantity property rather than also the Details property containing the original Item’s reference anyway?

    1. That’s a good question, that gets into some slightly more advanced principles.

      In programming, there is something called the Single Responsibility Principle. It says that each class should only have a single purpose.

      The purpose of the Inventory class is to hold the data describing a type of game item, so we can read it later, to display on the screen. The properties in the Inventory class should never change. In fact, we could change the “set”s for the properties in the Inventory class to “private set”s, to ensure nothing could ever change those property values. The Inventory class is used for immutable objects – objects that never change after being constructed/instantiated.

      The purpose of the InventoryItem class is to hold the player’s current inventory, including the quantity of that item, which will change. So, it’s purpose is to be “mutable” It, and its properties, can change.

      We could combine them into one class, and change our functions (including how we would get an Inventory object from the World class), especially in a small program like this one. But, mixing a 100% read-only object with an object that needs to change can be a source of confusion, and problems, in larger programs.

      Please let me know if that doesn’t clear up why I wrote the classes that way.

  8. Hi Scott,

    Having errors on lines 95 and 105:

    ‘Cannot implicitly convert type ‘Engine.Item’ to ‘int’

    Seems like it doesn’t understand ItemByID. No idea why.

    regards.

    r.

  9. I had a brain meltdown in this lesson.

    Previously, we made “Item” and “HealingPotion” classes and reduced the HealingPotion class of unnecessary properties that were also in Item. I totally understand this and it makes complete sense.

    But now, when we have “Item” and “QuestCompletionItem”, even though logically you would think you would do the same thing as HealingPotion (they are both items and probably have properties that funnel up into Item class, we’re NOT doing the : Item this time. This time we’re…..creating lists? But when you are creating a game like this, how do you decide if something is an inherited class or a list? I guess the fact that they are all classes is what’s driving me nuts. 🙂

    1. Weapon and HealingPotion are child classes of Item because they are special types of Items. However, QuestCompletionItem is not a type of Item. It’s something different.

      Think of it like visiting Amazon.com. When you’re searching for things to buy, you’re looking at their list of Items – similar to the _items list in the World class.

      When you place an order, the order needs to store a list of the items you’re ordering, PLUS the quantity of each item you’re ordering. For example, this could be your order:

      6 T-shirts
      2 Hats
      1 Gaming mouse

      We could say, that Order has three “line items”, or “order items”. If we created an OrderItem class, to hold these lines, it would have two properties: one to hold the Item being ordered (T-shirt), the second to hold the Quantity ordered (6). The Order object would have an OrderItems property that is a List, so it can hold however many OrderItem objects the Order has.

      That’s also what the QuestCompletionItem class does. It holds the Item that needs to be turned in, plus the Quantity of that Item that is needed. The Quest class holds a List of the QuestCompletionItems, in case we want to have a Quest that needs multiple Items turned in. For example, to complete a Quest, you might need to turn in:

      5 rat tails
      2 pieces of fur

      For that Quest object, the QuestCompletionItems list would have two QuestCompletionItem objects. The first QuestCompletionItem.Details would be the “rat tail” Item object, and the Quantity would be 5. The second QuestionCompletionItem.Details would be the “piece of fur” Item object, and the Quantity would be 2.

      Let me know if you still have questions about this.

    1. Hello. The ‘CurrentHitPoints = currentHitPoints’ should have been removed from the Monster class in step 6 of Lesson 08.2. That’s when we switch the Monster class to use the CurrentHitPoints property of the base “LivingCreature” class.

  10. Typo on step 5
    And add this to the constructor, so the new property it isn’t “null”:
    supposed to be:
    And add this to the constructor, so the new property isn’t “null”:

  11. I can’t thank you enough for this amazing tutorial, i’ve already completed it and worked like a charm! i’m doing it a second time, this time taking my team to understand throughly what’s going on, but here i’m Lost.

    Why in the List class InventoryItem there’s a reference to the class Items but “Details” doesn’t exist in the class Item, whats the purpose of calling the Class Item and passing Details there in the InventoryItem class.

    i know it’s a though question to ask when you don’t really know how explain it yourself (non-english speaker )

    So,

    ****Class Item

    public Item(int id, string name, string namePlural)
    {
    ID = id;
    Name = name;
    NamePlural = namePlural;
    //There’s no Details in the construct nor in the get-set
    }

    ****
    Class InventoryItem
    public class InventoryItem
    {
    //Why reference class Item here? does this pass this value unto the class what’s going //on here.
    public Item Details { get; set; }
    public int Quantity { get; set; }
    }

    Thank you very much for this amazing tutorial Scott.

    1. In the InventoryItem class, the Details property’s datatype is “Item”. That means, the “Detail” property will hold an object that is an Item. I could have named the property “Item”, but I do not like to have a property name that is the same as a class name (the datatype). So, I used “Details” for its name.

      The InventoryItem class does not represent an Item. It is more like a line on a shopping list – with an item, and a quantity that you want to purchase. Like this:

      2 Pizzas
      1 Cake

      In the shopping list example, “Pizzas” and “Cake” are Item objects. If I wanted the name of the second item in the list, the code might look like this (I’m not typing this in Visual Studio, so there might be some errors):


      Item pizza = new Item(1, "Pizza", "Pizzas"); // Create a "Pizza" object
      Item cake = new Item(2, "Cake", "Cakes"); // Create a "Cake" object

      List shoppingList = new List(); // Create a shopping list (like the Player Inventory)

      InventoryItem firstLine = new InventoryItem();
      firstLine.Details = pizza; // The item for this "line" (InventoryItem) on the shopping list is a pizza (Item object)
      firstLine.Quantity = 2;

      shoppingList.Add(firstLine); // Add this "line" to the list

      InventoryItem secondLine = new InventoryItem();
      secondLine.Details = cake; // The item for the second "line" (InventoryItem) is a cake (Item object)
      secondLine.Quantity = 1;

      shoppingList.Add(secondLine); // Add this "line" to the list

      // Get the name of the second item on the shopping list
      InventoryItem test = shoppingList[1]; // This returns the second InventoryItem (line) from the shoppingList (lists start counting rows with zero)
      Item testItem = test.Details; // The Details property of the InventoryItem object is an Item object
      string testItemName = testItem.Name; // Because testItem is an Item object, we can "get" its properties

      Please tell me if you still have questions.

  12. Codes under each header are in incorrect order.

    PlayerQuest.cs has code for LootItem
    QuestCompletionItem.cs has code for PlayerQuest
    LootItem.cs has code for QuestCompletion.

  13. Hi Scott,

    Thanks for your awesome tutorial. I am new to unity and making a small game by following your step-by-step lessons.

    Just found a spelling mistake in this lesson.

    “QuestCompeltionItem.cs” is supposed to be “QuestCompletionItem.cs”, is that right?

    Again, thanks for sharing your experiences and hopefully I can finish my first SuperAdventure in Unity.

  14. i´m getting this error “The type or namespace name ‘LootItem’ could not be found (are you missing a using directive or an assembly reference?” on line 16 and 26. its lokking for ´lootItem´.

    1. Check the LootItem class, and see if it is “public”. Another possible problem is if the class is named differently (including if it uses different upper or lower case letters).

      If that does not fix the error, please tell me.

  15. Hello Scott !
    I’m trying to understand line 11 in PlayerQuest. Does PlayerQuest take a Quest object and put it into “Details” or how does “PlayerQuest” and “Quest” relate to each other?

    Thank you !

    1. Hi Tom,

      Yes, PlayerQuest stores a Quest object in the Details property. I could have named that property “Quest”, instead of “Details”, but then the property name would be the exact same as the datatype – and I don’t like doing that.

      The purpose of the PlayerQuest object is to record the player’s status for the quests they have. PlayerQuest could have had only two properties: QuestID, and IsCompleted. But we store the complete Quest object so we can easily use the other values of the Quest object – for example, to bind the Quest name to the UI, to know the quest completion items (to see if the player can complete the quest), etc. If we only stored the QuestID, we would need to add more code in all the other places, to get those other values we want to use.

      Does that make sense, or is there something that you could use some more details about?

Leave a Reply

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