HiveBrain v1.2.0
Get Started
← Back to all entries
principlecsharpMajor

Reusability vs simplicity in a small game with a set of interfaces

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
withsimplicityreusabilitygamesmallinterfacesset

Problem

I have a small game I'm working on with a set of interfaces:

IHavePosition:

public interface IHavePosition
{
    T Position { get; set; }
}


ICanMove:

public interface ICanMove : IHavePosition
    {
        IEnumerable> Motors { get; set; }
    }


ICharacter:

public interface ICharacter : IHavePosition, ICanMove
{
    /// 
    /// Gets or sets the action points.
    /// 
    /// 
    /// The action points.
    /// 
    int ActionPoints { get; set; }

    /// 
    /// Gets or sets the maximum action points.
    /// 
    /// 
    /// The maximum action points.
    /// 
    int MaxActionPoints { get; set; }
}


IPlayer:

public interface IPlayer : ICharacter, IResetable
{
    /// 
    /// Gets or sets the gold in the player's inventory.
    /// 
    /// 
    /// The gold.
    /// 
    int Gold { get; set; }
}


IEnemy:

public interface IEnemy : ICharacter
    {

    }


As you can see, they all share a generic parameter TPos, which is the data type used to store an object's position. I keep this generic because I hope to reuse these interfaces in later projects which might have different position storage types (e.g. floating point coordinates versus an integer grid. 3D coordinates versus 2D, etc). The problem, however, is that this means when creating my classes, I need to ferry around this TPos generic construct to places where it doesn't make sense to define it. I'm left creating new Player or Player instances all over the place when really I just want to be creating a Player.

An additional problem is that this might not (conceivably) be the only time I need to do this. What if I want to add a Health field that could be an int, or a float, etc? I'm now creating Player or Enemy instances all over the place.

Since TPos is the same project-wide, is there a better way of doing this than lugging around references to TPos everywhere?

Solution

The problem, however, is that this means when creating my classes, I need to ferry around this TPos generic construct to places where it doesn't make sense to define it. I'm left creating new Player or Player instances all over the place when really I just want to be creating a Player.

Yes, specifying Player each time can be quite frustrating. I'm not sure though if you just want to create just a Player or if you want to create a specific kind of player

You could create a GameOnePlayer class which extends Player, so then you create a GameOnePlayer and not a Player. You might have use for the Abstract Factory Pattern for the creation of these objects.

I think however that your class inheritance will become quite complex, if it isn't complex enough already. Which you state yourself with this:


An additional problem is that this might not (conceivably) be the only time I need to do this. What if I want to add a Health field that could be an int, or a float, etc? I'm now creating Player or Enemy instances all over the place.

It sounds like what you need is to use composition over inheritance. A common way to do this in modern games is to use the Entity Component System approach.

If you go with this approach, both a Player and an Enemy should both just be an Entity. Then you can add whatever components they need, such as a Vector3Position component or a Int2Position component. Also IntHealthComponent and FloatHealthComponent.

There is a Entity Component System library named Artemis which has been ported to C# (I haven't used this myself, but I believe it's suitable enough for your needs). You can also make a Entity Component System implementation yourself, which many others have done on Code Review already (see entity-component-system)

To get an additional understanding of ECS, there is also an excellent question on GameDev StackExchange

Even though ECS is mostly used in real-time games, it is also possible to use it in turn-based games, which I have started doing myself in my Trading Card Game code.

Context

StackExchange Code Review Q#57569, answer score: 20

Revisions (0)

No revisions yet.