Jump to content
  • Advertisement
Sign in to follow this  
Mizmoon

Best option for storing item types

This topic is 644 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm rather new to game development and have been thinking about the different options for saving item types in your game. I've seen some people do this, sort of clunky and strictly OOP approach:
 
 

class Item {
    /* General item stuff here */
}
 
class Weapon : Item {
    /* Weapon stuff here */
}
 
class Sword : Weapon {
 
}
 
class Claymore : Weapon {
    /* This is the specific item type */
}

 

My own preference is doing something more like this:
 

class ItemType {

    /* Stats such as cost, rarity, etc. */

}

 

class WeaponType : ItemType {

    /* Added stats such as damage, attack speed */

}

 

class Item {

    protected ItemType type;

}

 

class Weapon : Item {

    public Weapon(WeaponType type) {

        this.type = type;

    }

}

 

Or thirdly - more lightweight, but with the added disadvantage that you have to store redundant information:
 

static class ItemTypes {

    private static float[] damage;

    private static float[] cost;

 

    public static float GetCost(int ItemTypeId) {

        return cost[ItemTypeId];

    }

 

    public static float GetDamage(int ItemTypeId) {

        return damage[ItemTypeId];

    }

}

 

Are any of these options to prefer over the other?

Edited by khalvr

Share this post


Link to post
Share on other sites
Advertisement

There are an enormous number of options, each has their own pros and cons.

 

The version you mention as your preference appears to be using hard-coded values for all the parameters and values.  This can work well for small systems with a limited number of items, and it has a benefit that the values can potentially be directly compiled into the code for various compiler optimizations.  The drawback is that these systems do not scale well, and you can only have a limited number of the items.  It becomes time consuming to create new objects, time consuming to fine-tune each object independently.

 

The first one you list, a strict inheritance tree for each type, takes that even further. If every weapon is truly unique this can make sense, such as the various items in a Legend of Zelda game. It breaks down quickly if you want to merge behavior with multiple items, and it makes it extremely difficult to introduce new objects.

 

The last one you list, arrays of data, is similar to the way most bigger game engines choose to operate. There is a broad system that can handle a wide range of values, and the actual numeric values are pure data that gets loaded from data files and adjusted as needed.  Creating new objects and variations on existing objects merely requires adjusting some values in a data file, or passing the desired parameters to a factory method.

Share this post


Link to post
Share on other sites

Most of the time you would like to use interfaces (there's no multiple inheritance in C#, so you are limiting yourself, but you can add properties with getters and setters to interfaces and get almost identical results if getters/setters get inlined by the compiler (properties are just fancy functions)). Until now I used plain classes as you are doing right now, but it seems that using scriptable objects can make serialization easier (Unity3D context). The third thing seems like a spreaded bad practice... almost like most of what's seen about OOP, but inculcated by DOD... why? it's badly structured, it's hard to mantain, it's diffucult to extend, you need to keep track of indexes, there's almost no need to have contiguous memory storage of items. 

Edited by RenzoCoppola

Share this post


Link to post
Share on other sites

I approach this with a few guidelines in mind:

  • There needs to be a clear division between instances of an object and the blueprint/prototype/archetype/definition that instances are based on (http://gameprogrammingpatterns.com/type-object.html)
  • Different classes in code should only exist if each needs to have specific code for different behaviour (so you don't have separate Claymore and ShortSword classes - both do exactly the same thing in terms of actions, just differing in quantitative and descriptive ways)
  • Anything else that can reasonably be defined via data differences rather than by deriving different classes is preferable.

Share this post


Link to post
Share on other sites

 

I approach this with a few guidelines in mind:

  • There needs to be a clear division between instances of an object and the blueprint/prototype/archetype/definition that instances are based on (http://gameprogrammingpatterns.com/type-object.html)
  • Different classes in code should only exist if each needs to have specific code for different behaviour (so you don't have separate Claymore and ShortSword classes - both do exactly the same thing in terms of actions, just differing in quantitative and descriptive ways)
  • Anything else that can reasonably be defined via data differences rather than by deriving different classes is preferable.

 

 

Thanks, that's a really nice link! It answers most of the questions quite well.

 

There are an enormous number of options, each has their own pros and cons.

 

The version you mention as your preference appears to be using hard-coded values for all the parameters and values.  This can work well for small systems with a limited number of items, and it has a benefit that the values can potentially be directly compiled into the code for various compiler optimizations.  The drawback is that these systems do not scale well, and you can only have a limited number of the items.  It becomes time consuming to create new objects, time consuming to fine-tune each object independently.

 

I think you misunderstand me here, the idea of the second method is that you deserialize item classes directly from XML into instances of the ItemType class. Inside the type class, you have a method for returning an instance of the Item object linked to the type. You can have an infinite number of items; do you mean that it's hard to add new properties to your item types?

 

 

Most of the time you would like to use interfaces (there's no multiple inheritance in C#, so you are limiting yourself, but you can add properties with getters and setters to interfaces and get almost identical results if getters/setters get inlined by the compiler (properties are just fancy functions)). Until now I used plain classes as you are doing right now, but it seems that using scriptable objects can make serialization easier (Unity3D context). The third thing seems like a spreaded bad practice... almost like most of what's seen about OOP, but inculcated by DOD... why? it's badly structured, it's hard to mantain, it's diffucult to extend, you need to keep track of indexes, there's almost no need to have contiguous memory storage of items. 

 

The reason i mentioned the third option is because you can cut down the size of the reference by half. Let's say that you have a game revolving around large quantities of units, or a projectile class with few fields other than the type, where memory is a factor. In this case, a 32 bit int id value is a very efficient way of storing the link to the type. When loading in the types, you can simply have an incrementing counter for each type you add and use this as the id. When saving the game, in to a save file, you can use a more specific id (mapped to the "runtime id") to make sure that adding more types won't screw up the save. In theory, you could even use ushort values, but sadly things like memory alignment and ALUs being optimized for 32 bit ints mitigate the benefits of this.

 

Thanks for the tip on scriptable objects, i was not aware of these before, will check it out!

 

 

Oh, and an additional question - when saving the item classes, i was tempted to make a separate folder for each type containing all its resources, including a XML/JSON file for that specific type. Is this a good way to structure things, or is it more efficient to just have all types defined in a single long "ItemTypes.xml" file?

Edited by khalvr

Share this post


Link to post
Share on other sites

Yep, whatever works is good.

 

My problem with the normal inheritance tree is that it never works..

 

class Item{}

 

class Weapon : Item {}

 

class Shield : Item {}

Now what do you do with a buckler shield which is both a shield and a weapon. Also if you have some form of inventory that stores Items you get into trying to cast them to more specialized to see if weapon etc. All ugly. 

 

This then tends to lead you to ECS or a collection of NVP so you can store a grab bag of named attributes. Both these solve the buckler shield issue and the code smell of trying to cast or run "is a" test on your items to see if they are weapons and the like.

Share this post


Link to post
Share on other sites

The ISA relationship means something different than most people think.

 

ISA means the thing is completely, fully, absolutely interchangeable for the specified purposes. An example many game programmers are used to is video cards.  If you request a D3D11Device then you've got a base class. It doesn't matter what the exact card is, they should be exactly interchangeable. It doesn't matter if it a card manufactured two years ago, a card manufactured last month, or a card manufactured 50 years in the future, all of them should implement the interface you expect.

 

You have designed that a thing can be multiple things, it can be a weapon, and it can also potentially be a shield, and it can be an item, you've clearly described a composition relationship. Component designs are the natural way to express composition.  That may not mean the full ECS systems some people think of, but it does strongly suggest composition from a code perspective.

 

Normal inheritance does work for things where they are all interchangeable for a specified purpose.  Quite commonly there is a base GameObject that can apply to all things existing in the game simulator. They are all interchangeable in the fact that they can all be placed in the simulator. For ECS systems there is often a base component or behavior or similar base class that they share, they are all interchangeable in that they can be composed inside an object with the same interface.  All physics objects, be the parametric shapes or standard geometric shapes or custom meshes, all of them may share a base type.  In general, inheritance trees are shallow but wide; you may have 2 or 3 layers but a large number (maybe even hundreds) of sub-types derived from the base. 

Share this post


Link to post
Share on other sites

The ISA relationship means something different than most people think.

 

ISA means the thing is completely, fully, absolutely interchangeable for the specified purposes.....

 

 

you managed to sum up what I was trying to get at above far more succinctly :) 

Share this post


Link to post
Share on other sites

The ISA relationship means something different than most people think.

 

ISA means the thing is completely, fully, absolutely interchangeable for the specified purposes.

 

Although i agree with everything you say, "ISA" means the exact same thing as inheritance. Weapon -> ISA -> Item is no doubt correct.

 

I really like component designs and tend to use them whenever i can, but the issue for me is that the performance tends to be worse for some purposes. For instance, if you create a missile projectile and need to add the components "RocketMovement", "RocketExplode", "RocketHoming", etc - you end up with a large amount of 64 bit references linking these together. Then again, the benefits can sometimes outweigh the disadvantages.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!