31
\$\begingroup\$

So I'm making a game where you can send ships to locations to sell or buy resources like wood, iron, gold, etc.

Now I was wondering how the resources should be created in de game. I came up with 2 options

  1. Create a class for each resource:

    public class ResourceBase {
        private int value;
        // other base properties
    }
    
    public class Gold : ResourceBase {
         public Gold {
             this.value = 40 // or whatever
         }
    }
    
  2. Create instances of a Resource class

    public class Resource {
        string name;
        int value;
    
        public Resource(string name, int value) {
            this.name = name;
            this.value = value;
         }
     }
    
    // later on...
    
    Resource gold = new Resource("Gold",40);
    

With the second option it's possible to fill the game resources from a resources.json file, I kinda like that structure.

New ideas / design patterns / structures are always welcome!

EDIT: It's a little bit like Assassins Creed Black Flag's companion app. See the resource bar on the image below

EDIT 2: I've done some more research on the "load items/resources from JSON file" and found this blog: Power of JSON in Game Development - Items. It shows the best of option 1 and option 2. You're still able to add functionality to each resource :)

enter image description here

\$\endgroup\$
  • 1
    \$\begingroup\$ Minecraft has separate classes for each resource (not that you should) \$\endgroup\$ – MCMastery Jun 14 '16 at 15:53
  • \$\begingroup\$ @MCMastery ohh is minecraft open-source? I would like to have a look into that structure. And thx for mentioning \$\endgroup\$ – MDijkstra Jun 14 '16 at 16:00
  • 1
    \$\begingroup\$ It is not, but it is always being de-obfuscated for servers... see github.com/Wolf-in-a-bukkit/Craftbukkit/tree/master/src/main/… \$\endgroup\$ – MCMastery Jun 14 '16 at 16:01
  • 12
    \$\begingroup\$ Don't use a string like that. It's all too easy to mistype "gold" with "glod" or similar, and it's a huge pain to debug. Use an enum instead. \$\endgroup\$ – Nolonar Jun 15 '16 at 11:32
  • \$\begingroup\$ Minecraft isn't technically open source, but it has nearly completely been decompiled and deobfuscated. You shouldn't be able to find a deobfuscated source anywhere on the Internet, but you can download and run the process to do it from files.minecraftforge.net (the MDK download is the one you want) \$\endgroup\$ – JamEngulfer Jun 15 '16 at 13:32
51
\$\begingroup\$

Go with the second approach, simply due to the fact that you can introduce new resource types or items at any time without having to rewrite or update code (data driven development).


Edit:

To elaborate a bit more on why this is in general good practice, even if you're 100% sure some value won't ever change.

Let's take the console game example mentioned in the comments, because this provides a very good reason on why you shouldn't hard-code anything, unless you really have (or want) to, e.g. encryption keys, your own name, etc.

When releasing a game on consoles, these usually have to go through a review process, where the console manufacturer's own QA will test the game, play through it, look for issues, etc. This is mandatory and costs money, a lot of money. I think I've once read that a release might cost 30,000-50,000$ just for the certification alone.

Now imagine you're pushing your game for release, paying the 30,000$ and wait. And suddenly you notice that some of your game values are literally broken. Let's say you can buy iron bars for 50 gold and sell them for 55 gold.

What do you do? If you've hard-coded that stuff, you'll have to create a new update/release version, which will have to go through review once again, so worst case you'll pay once again for the re-certification! I bet Ubisoft won't mind, but for your own little indie games developer pocket… ouch!

But imagine the game would just occasionally check for updated game definitions (e.g. in the form of a JSON file). Your game could download and use the latest version at any time without requiring a new update. You could fix that imbalance/exploit/bug at any time without requiring re-certification or any other money paid. Just some minor design decision, you didn't think was worth it, just saved you a 5 digit sum of money! Isn't that awesome? :)

Just don't get me wrong. This also applies to other types of software and platforms. Downloading updated data files is in general a lot more easy and doable for the standard user, no matter whether it's a game or some kind of application/tool. Imagine a game installed under Program Files in Windows. Your updater needs administrator rights to modify anything and you can't modify running programs. With DDD your program just downloads the data and uses it. The player might not even notice there's been an update.

\$\endgroup\$
  • 6
    \$\begingroup\$ +1 for DDD. This is very fitting for such things as resources. \$\endgroup\$ – Kromster says support Monica Jun 14 '16 at 7:36
  • \$\begingroup\$ @Funnydutchman if someone's answer helped you, it is customary on Stack Exchange to upvote their answer by clicking the arrow above the number to the left. If an answer helped you the most, you should click the checkmark below the bottom arrow to accept their answer. Both actions give the answerer a bonus in reputation and make usable answers more visible to the community. \$\endgroup\$ – Nzall Jun 14 '16 at 14:24
  • 2
    \$\begingroup\$ I don't see how'd you have to rewrite code with the first way; all you'd do is add a class (unless I'm missing something). \$\endgroup\$ – SirPython Jun 14 '16 at 20:56
  • 4
    \$\begingroup\$ And here is the difference between Object Oriented code and Object Obsessed code. Just because you can create an object hierarchy doesn't necessarily mean you should. \$\endgroup\$ – corsiKa Jun 15 '16 at 15:36
  • 2
    \$\begingroup\$ @AgustínLado If I had a dollar for every time a client said "This will never changed" and it changed, I could take us both to a decent restaurant for dinner (although we'd have to skimp a little on the tip, so maybe only a mediocre restaurant... still enough times for dinner for two though.) \$\endgroup\$ – corsiKa Jun 15 '16 at 15:37
29
\$\begingroup\$

A rule of thumb is that you use different classes when objects require different code and instances of the same class when the objects only require different values.

When the resources have different game mechanics which are unique to them, it might make sense to represent them with classes. For example, when you have Plutonium which has a half-life time and Rabbits which procreate according to the fibonacci sequence, then it might make sense to have a base-class Resource with a method Update implemented as a no-op and two derived classes HalfLifeResource and SelfGrowingResource which override that method to decrease/increase the value (and maybe also include logic to govern what happens when you store both next to each other).

But when your resources are really just dumb numbers, then it makes no sense to implement them with anything more fancy than a simple variable.

\$\endgroup\$
  • \$\begingroup\$ Thanks @Phillipp for your answer. This is what I was thinking. But the resources won't really change in terms of properties/methods, so I will go with option 2 for now \$\endgroup\$ – MDijkstra Jun 14 '16 at 15:46
14
\$\begingroup\$

I would like to add there are two extra options:
Interface: you can consider it if the resource class would be just a "storage" for 5 integers and each other entity would have different logic regarding resources (e.g. player spend/loot, city produces resource). In that case you might not want a class at all - you merely wanted to expose that some entity has a variable others can interact with:

interface IHasResources {
  int Gold { get; set; }
  int Wood { get; set; }
  int Cloth { get; set; }
  // ....
}

class Player : IHasResources { }
class City: IHasResources { }

in addition, this has the upside you can loot a city with exactly the same code as you would loot enemy fleet, promoting code reuse.
Downside is, implementing such interface can prove tedious if many classes implements it, so you might end up using interfaces but not for original problem, solving it with option 1 or 2:

interface IHasResources { 
   IEnumerable<Resource> Resources { get; }
}

Instances (with enum): in some cases, it would be desirable to use enum instead of string when defining resource type:

enum Resource {
   Gold, Wood, Cloth, //...
}

class ResourceStorage {
   public Resource ResourceType { get; set; }
   public int Value { get; set; }
}

this will provide a bit of "safety", because your IDE will give you list of all valid resources when assigning it after writing the dot ResourceType = Resource., in addition there are more "tricks" with enums you might need, like explicit Type or composition/flags which I can imagine to be useful when crafting:

[Flags]
enum Metal {
 Copper = 0x01/*01*/, Tin = 0x02/*10*/, Bronze = 0x03/*11*/
}
Metal alloy = Metal.Copper; //lets forget Value(s) for sake of simplicity
alloy |= Metal.Tin; //now add a bit of tin
Console.WriteLine(alloy); //will print "Bronze"


As for what is best - there is no silver bullet, but personally I would lean towards any form of instances, they might not be as fancy as interface but they are simple, doesn't clutter inheritance tree and are widely understood.

\$\endgroup\$
  • 2
    \$\begingroup\$ Thank you for your answer @wondra I like the enum option and how you made that bronze out of copper and tin ;p \$\endgroup\$ – MDijkstra Jun 14 '16 at 15:55
  • 2
    \$\begingroup\$ I joined this community specifically so I could up-vote the enum solution. You can also look into using the enum Description attribute to map from some JSON representation of the enum (my-great-enum) to some C# representation of the enum (MyGreatEnum). \$\endgroup\$ – Dan Forbes Jun 14 '16 at 20:26
  • \$\begingroup\$ Apparently I have been out of the Java-loop too long. Since when is it allowed to have field declarations for interfaces? As far as I know those are automatically static and final, and thus not much use for purposes described in the question. \$\endgroup\$ – M.Herzkamp Jun 15 '16 at 13:44
  • 2
    \$\begingroup\$ @M.Herzkamp it is not a field, it a property - and Java does not support properties. Question is tagged C#, C# allow properties in interfaces - properties are methods under-hood after all. I am afraid same goes for flags annotation, not supported in Java though I am not completely sure. \$\endgroup\$ – wondra Jun 15 '16 at 15:41
  • \$\begingroup\$ Thanks for clearing that up! I missed the C# tag and the code in the question looked so damnably alike Java :D \$\endgroup\$ – M.Herzkamp Jun 16 '16 at 9:27
2
\$\begingroup\$

You should use option 1, which has 3 advantages:

  1. It's easier to instantiate a resource - instead of having to pass the type of the resource as a parameter, you would just do, for example, new Gold(50)
  2. If you need to give special behaviour to a resource, you can by changing it's class.
  3. It's easier to check if a resource is a certain type - resource instanceof Gold
\$\endgroup\$
  • \$\begingroup\$ All the answers have their pros and cons, it's hard to choose which is better. I've made an edit to the question, what do you think of that structure? \$\endgroup\$ – MDijkstra Jun 15 '16 at 20:08
1
\$\begingroup\$

If your resources have no business logic of their own, or special case scenarios with how they interact with the environment, other resources (besides trade which you've covered) or the player then option two makes adding new ones very easy.

If you need to consider situations like what would happen if you mix two resources for crafting, if the resources can have vastly different properties (a bucket of water is very different than a lump of coal) or other complex economic interactions, then approach 1 is far more flexible for the engine, but not the data.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.