Skip to main content
gamedev.net
🔒 Locked

Using Interfaces To Create Modular Code?

Started by 3dmodelerguy Jan 25, 2011 at 3:13 PM 16 replies 3k views
Original Post
3dmodelerguy
3dmodelerguy
Maybe this is just me as I also think there is a better way to do things, but it just seems like that is should be a much better way to implement this than the way I am think.

I am starting to think about the itemization sub-system I am creating for a game (using C# BTW) and the initial way I am think about approaching this seems like it is going to lead to a lot of classes. The way I am thinking about doing this was to start off with a bunch of different interfaces like:

  • IItem
  • IConsumableItem
  • IIngredientItem
  • IEquipableItem
  • IWeaponItem
  • IArmorItem
  • IQuestItem
  • IItemModifier
  • etc…
    But then when I got to thinking about the classes I would have to create, just for quest alone I could have:

    • QuestItem
    • ConsumableQuestItem
    • EquipableQuestItem
    • WeaponQuestItem
    • ArmorQuestItem
    • ModifierQuestItem
    • etc...
      The IIngredientItem version of the QuestItem is the only one that I would probably not have (but then again maybe I would). Now right now QuestItem is the only one that really could have all the interface attached to it, IWeaponItem would never also have IConsumableItem or IArmorItem would never also have IWeaponItem. Is this structure something that just seems completely off or is it more like there are other ways to do it but this way works just as well with it's own pros/cons.
j0mich01
j0mich01
An object can implement multiple interfaces, and interfaces can derive from each other. In your system if you wanted to make a quest item I would do it something like this


class AwesomeSword : IQuestItem, IWeaponItem
{
//Implement Interfaces here
}


Assuming that IWeaponItem is derived from IEquipableItem, then this item would be a quest item that can be equipped as an weapon. Am I understanding you question correctly? Those extra classes don't need to be made because interfaces can be combined on individual classes. Hope this helps.
TheOrzo
TheOrzo
It looks like you're not using interfaces for any real benefit, and just throwing them in there because it seemed like the right thing to do. Apologies if this assumption is wrong, but perhaps you can explain what the contracts for some of these interfaces are?
Antheus
Antheus

  • QuestItem
  • ConsumableQuestItem
  • EquipableQuestItem
  • WeaponQuestItem
  • ArmorQuestItem
  • ModifierQuestItem
  • etc...

Very few of these are suitable for inheritance.

This is what I consider the most flawed perspective of how OO design is taught. Animal tree is not about inheritance. A human does not inherit from an ape and frog does not inherit from a fish. Same for vehicles - a car and a plane do not inherit from common base, despite both having wheels - quite a few planes do not have wheels and some cars could fly.

Most of traits above should be expressed as modifiers.
Meat loaf is consumable.
But Might Meat Loaf is weapon + equipable.
Sword is weapon + equipable.
Decorative armor is armor but not equipable - it's just decoration.

It gets trickier, quest item just being flagged as such isn't interesting, it needs to store which quest it belongs to. So this becomes an attribute or modifier which contains this extra information.
Similar for equippable, it's needs to specify where it can be equipped.

In shop screen, one would then have ability to sell all items, except those with quest item attribute.
In equip screen, clicking on hand would list all items that are equippable in hand slot.

And so on.

So majority of functionality can be trivially expressed with a dictionary of keys indicating supported functionality. Value can then be any extra data needed.
The engine then just goes around an checks these traits.
3dmodelerguy
3dmodelerguy

Most of traits above should be expressed as modifiers.


What exactly do you mean by this? How would I apply a modifier to a class? Could you possibly give me a code example of say adding a weapon modifier to a sword?
j0mich01
j0mich01

[quote name='Antheus' timestamp='1295977422' post='4764542']
Most of traits above should be expressed as modifiers.


What exactly do you mean by this? How would I apply a modifier to a class? Could you possibly give me a code example of say adding a weapon modifier to a sword?
[/quote]

I'm pretty sure he is talking about doing something like this:




enum ItemModifier
{
Quest,
Weapon,
Armor,
Consumable,
Equipable
}


class Item
{
string Name;
Dictionary<ItemModifier, object> Modifiers;

public Item(string name)
{
Name = name;
Modifiers = new Dictionary<ItemModifier, object>();
}


public void AddModifier(ItemModifier modifier, object modifierData)
{
Modifiers.Add(modifier, modifierData);
}

public bool Is(ItemModifier mod)
{
return Modifiers.ContainsKey(mod);
}

public object GetData(ItemModifier mod)
{
if(Modifiers.ContainsKey(mod))
return Modifiers[mod];
return null;
}

}


Then you could create objects like this:



Item i = new Item("Great Broadsword");
i.AddModifiers(ItemModifier.Weapon, 10);
i.AddModifiers(ItemModifier.Equipable, 0);

//Can check modifiers by doing
if(i.Is(ItemModifier.Weapon))
{
//Do stuff here
}



Of course the object that I use as the 'Value' in the Dictionary can be whatever you want. Hope this example helps explain the basic design a little bit better.
Antheus
Antheus

I'm pretty sure he is talking about doing something like this:

Yep.

Of course the object that I use as the 'Value' in the Dictionary can be whatever you want. Hope this example helps explain the basic design a little bit better.[/quote]
Value could be an interface, though it's probably simpler to just keep it some generic container and cast it as needed.

To simplify updates, one could find all items that have certain attribute and reference them from a separate container so they are updated without extra check.


Common properties, such as position, texture, mesh, etc... can be regular members and if they happen to not be used then just set them to null. It's a bit unclean but avoids overgeneralization.
3dmodelerguy
3dmodelerguy
Ok, well that does help but here is another example of trying to make modular code without having to tie 1 system too tightly to another system.

Two systems that I am going to be creating are an itemization system (items, inventory, currency, etc..) and a quest system. 95% of the quest system should be able to work without any knowledge of the inventory system however the quest system is going to have to handle being able to give items and/or currency out as rewards. The best way I can think of doing this is having one master project (I am building each subsystem as a separate project) that is just a collection of all the very high level non-implementation code like interfaces and enums. Then each project for each subsystem would include this master project, this way the quest system (or any other system) will know about any interfaces/enums the itemization system (or any other system) would use. This means that each system would at least compile without having to directly know about any other system besides the master project (however and code that uses any interfaces declared in other systems would obviously not work).

Again, creating a good clean organized codebase is not specialty so I just want to become better at this (without going way over the line and create too organized code to the point where it is bad). Is this a good or decent solution I am think of?
CodeCriminal
CodeCriminal

Ok, well that does help but here is another example of trying to make modular code without having to tie 1 system too tightly to another system.

Two systems that I am going to be creating are an itemization system (items, inventory, currency, etc..) and a quest system. 95% of the quest system should be able to work without any knowledge of the inventory system however the quest system is going to have to handle being able to give items and/or currency out as rewards. The best way I can think of doing this is having one master project (I am building each subsystem as a separate project) that is just a collection of all the very high level non-implementation code like interfaces and enums. Then each project for each subsystem would include this master project, this way the quest system (or any other system) will know about any interfaces/enums the itemization system (or any other system) would use. This means that each system would at least compile without having to directly know about any other system besides the master project (however and code that uses any interfaces declared in other systems would obviously not work).

Again, creating a good clean organized codebase is not specialty so I just want to become better at this (without going way over the line and create too organized code to the point where it is bad). Is this a good or decent solution I am think of?


The prototype (i believe) pattern (that is for your item creation) sounds like something you would need.. basically eliminates the need for creating a miriad of derived item classes just for specific items
instead you provide a POD type, pass it to an Item class and then have that class create the item based on the information you feed it through the POD type. eg.

// I know very little C#... and also this kinda stuff would be loaded from a file and then that data would processed and used to initialise the items..
WeaponItemInfo wii = new WeaponItemInfo;
wii.type = longsword;
wii.damage = ??;
wii.criticalRatio = ??;
// etc...

QuestItemInfo qii = new QuestItemInfo;
qii.type = hairpin;
qii.target = "Maria#004"
// .. etc etc

Inventory.addItem(WeaponItem(wii));
Inventory.addItem(QuestItem(qii));

thats pretty basic but it gets the idea across.. you may be able to reduce the number of classes even more by just having ItemEquipable and ItemConsumable classes and thier prototype counterparts.
I think someone may have already mentioned this method above..... or something close to it...


As you for your master project thing, I would look for another way to do it because that way doesnt sound to healthy and may cause problems down the line..
Telastyn
Telastyn

Two systems that I am going to be creating are an itemization system (items, inventory, currency, etc..) and a quest system.


What possible benefit do you can from decoupling these?

Are you really going to reuse the inventory system in another game? The quest system? Are you going to drop out one system and replace it with another one?


This smells like over-engineering at first glance.
3dmodelerguy
3dmodelerguy

[quote name='3dmodelerguy' timestamp='1296086279' post='4765348']
Two systems that I am going to be creating are an itemization system (items, inventory, currency, etc..) and a quest system.


What possible benefit do you can from decoupling these?

Are you really going to reuse the inventory system in another game? The quest system? Are you going to drop out one system and replace it with another one?


This smells like over-engineering at first glance.
[/quote]

Yea, your probably right. Thinking about it more I am probably trying to decouple these systems for the sake of decoupling. I can probably just include all these projects into one solution called something like gameframework and just allow the projects to depend on each other if needed. Re-writing one system that depends on another system is going to be pretty much the same if the system does not depend on it.

I can however diffidently see me reusing an itemization system, quest system, dialog system with multiple games (and the code in these project are just back-end data type code, not GUI code) as at least 80-90% of the system will probably be the same. When I decide to reuse this in another game that needs extra functionality, I will just add in the functionality to the game framework so that all future games will be able to use that functionality (even if it does not need it, it will be there).
SiS-Shadowman
SiS-Shadowman
You might want to read about entity systems (there are great threads in this forum that explain what they are, discuss pros and cons, etc...however I'm unable to find the one I based my system on). They would help you to decouple the functionality without resorting to a huge inheritance tree.
What was expressed as modifiers before could be thought of as components (ie they are independent classes each encapsulating a single behavior). An entity simply becomes a collection of components.


For example, this is how AwesomeSword could be expressed in an entity system:

Entity awesomeSword = new Entity();
awesomeSword.attach(new QuestComponent(...));
awesomeSword.attach(new SwordComponent(1000dps));


If you're interested, I can send you my entity system approach.
Telastyn
Telastyn

I can however diffidently see me reusing an itemization system, quest system, dialog system with multiple games (and the code in these project are just back-end data type code, not GUI code) as at least 80-90% of the system will probably be the same. When I decide to reuse this in another game that needs extra functionality, I will just add in the functionality to the game framework so that all future games will be able to use that functionality (even if it does not need it, it will be there).


Your goal is to finish this game, not the next one.
3dmodelerguy
3dmodelerguy

[quote name='3dmodelerguy' timestamp='1296125014' post='4765545']
I can however diffidently see me reusing an itemization system, quest system, dialog system with multiple games (and the code in these project are just back-end data type code, not GUI code) as at least 80-90% of the system will probably be the same. When I decide to reuse this in another game that needs extra functionality, I will just add in the functionality to the game framework so that all future games will be able to use that functionality (even if it does not need it, it will be there).


Your goal is to finish this game, not the next one.
[/quote]

Actually my goal is to be able to create games for a living, not just create 1 game. Now I am not saying that I am going to design this game framework so that I know I can create our next 5 games of any type because I don't know what our next game is going to be but I want to put at least put in a tiny bit of planning so that when we go to create our next game, we don't find that we have to completely rewrite every single system again from scratch.
Telastyn
Telastyn

Actually my goal is to be able to create games for a living, not just create 1 game. Now I am not saying that I am going to design this game framework so that I know I can create our next 5 games of any type because I don't know what our next game is going to be but I want to put at least put in a tiny bit of planning so that when we go to create our next game, we don't find that we have to completely rewrite every single system again from scratch.


Look, I know that.

But one of the most common threads amongst these forums, and indeed software development in general is how hard it is to finish. You cannot create games for a living if you cannot finish one.
stupid_programmer
stupid_programmer

Actually my goal is to be able to create games for a living, not just create 1 game. Now I am not saying that I am going to design this game framework so that I know I can create our next 5 games of any type because I don't know what our next game is going to be but I want to put at least put in a tiny bit of planning so that when we go to create our next game, we don't find that we have to completely rewrite every single system again from scratch.


You're going about it in the wrong direction though. Actually finish (and polish) the first five games and gather up the bits that you find you are reusing as you go along instead of trying to create some super library from the start. Only thing you are going to end up doing is adding more and more "features for the future" and get further and further away from completing one game. As Telastyn said you have to be able to finish games to do them as a living. Going back and rewriting code to make it better or easier to extent is part of iterative nature of software development.
3dmodelerguy
3dmodelerguy

[quote name='3dmodelerguy' timestamp='1296141396' post='4765655']
Actually my goal is to be able to create games for a living, not just create 1 game. Now I am not saying that I am going to design this game framework so that I know I can create our next 5 games of any type because I don't know what our next game is going to be but I want to put at least put in a tiny bit of planning so that when we go to create our next game, we don't find that we have to completely rewrite every single system again from scratch.


You're going about it in the wrong direction though. Actually finish (and polish) the first five games and gather up the bits that you find you are reusing as you go along instead of trying to create some super library from the start. Only thing you are going to end up doing is adding more and more "features for the future" and get further and further away from completing one game. As Telastyn said you have to be able to finish games to do them as a living. Going back and rewriting code to make it better or easier to extent is part of iterative nature of software development.
[/quote]


The thing is that I am not going to be adding in features for the future, only as I need them. I already know which systems as a whole are used in a lot of different games genres. So this first game I have will have an inventory system but for right now until this game is complete I will only be adding in features for this particular game. Then when we come up to another game that requires an inventory system, we start off with that and just add to it if the next game requires it new features.
theOcelot
theOcelot
Knowing which design features are used across different genres is not the same as knowing how to code them in a way that can be used in games from different genres.

On the other hand, a certain amount of decoupling is good just for your own sanity while developing, even if you don't try to re-use stuff.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.