1
\$\begingroup\$

I'm developing a tile-based game creation library in C# and along with it I'm creating a game to test all of its functions.

This is a screenshot of the game: enter image description here

You've probably never heard of the original, but my game is a clone of Deadly Rooms of Death (DROD), which you can find at http://caravelgames.com


Anyway, as you can see from the screenshot, my game (and my library) is all about 2D puzzle games on a grid, rigorously turn based.

My library has this structure:

  • Entity: class that gets updated every turn and every game elements inherits from
  • Tile: class that holds entities (any amount of entities)
  • TileManager: class that contains a 2D array of all the tiles
  • Field: class that contains a TileManager and all the methods for querying it and finding entities

For fast queries and flexibility, I'm using interfaces and inheritance to group entities. Let me explain with code for the roach entity:

public class TDLMonster : TDEntity, IHasTarget, IDrawn, IHasDirection, ISolid, 
IHitByWeapon, IHitByMonster
{
...
}

The TDLMonster class inherits from TDEntity (which inherits from Entity). It also uses interfaces as a way to mark certain properties of elements.

public interface IHitByWeapon : IEntity
{
    void HitBySword(IWeapon mWeapon);
}

This is the IHitByWeapon interface. After every turn, weapons check if there is any instance of IHitByWeapon on their tile. If there is, call HitBySword. Here's the code that does this:

 public virtual void CheckHits()
    {
        List<Entity> hitEntities = Field.GetEntities(X, Y, typeof (IHitByWeapon));
        for (int i = 0; i < hitEntities.Count; i++)
        {
            IHitByWeapon hitEntity = (IHitByWeapon) hitEntities[i];
            hitEntity.HitByWeapon(this);
        }
    }

Field.GetEntities(x, y, type) returns a list of entities of that type in a specific tile. When an entity is initialized, my library gets all the types and interfaces using the following code snippet, then puts the entity in dictionaries having types and interfaces as keys:

public static List<Type> GetAllTypes(Entity mEntity)
    {
        List<Type> types = new List<Type> {mEntity.GetType()};
        Type[] nestedTypes = mEntity.GetType().GetNestedTypes();
        types.AddRange(nestedTypes);
        Type[] interfaces = mEntity.GetType().GetInterfaces();
        types.AddRange(interfaces);

        foreach (Type type in nestedTypes) types.AddRange(type.GetInterfaces());

        return types;
    }
...
foreach (Type type in GetAllTypes(this))
            {
                if (!Tile.TileManager.GroupedEntities.ContainsKey(type))              Tile.TileManager.GroupedEntities.Add(type, new List<Entity>());
                Tile.TileManager.GroupedEntities[type].Add(this);
            }

My library/game is relies completely on types and interfaces.

This is a good thing because I know what methods and properties entities have, and because different entities can share the same interface. I can have 20 different types of monsters, but as long as they implement IHitByWeapon they will all get killed by the player's sword.

This is a bad thing because it relies on hardcoding elements, and I cannot manipulate interfaces at runtime. Let's say I have a door element. I need it to be solid when the door is closed and to be non-solid when the door is open. If the element implements ISolid I cannot remove it. The same problem applies if I suddenly want a flying roach. I would have to code another element that implements ICanFly, because I cannot add an interface to an entity at runtime.

I've thought about using a List of stuff like "solid", "enemy", "player" - but it's not flexible enough, since I cannot have interface-like methods or properties, and I need to guess if the entity has a method or use the dynamic keyword.


How can I solve this problem? Is there something that lets me have the ease of use as interfaces but also the flexibility as List?

Let me know if you need more information or parts of the source code.

\$\endgroup\$
6
  • \$\begingroup\$ Here is what you are doing wrong: the entire entity system. Here is the defining characteristic of an entity system: it uses components to achieve the absolute minimum amount of inheritance necessary. You have somehow created an entity/component system that is rooted deeply in inheritance. This is like creating a car that takes only water for fuel, except first it converts it into petrol and billows black smoke from all eight exhaust pipes. \$\endgroup\$ Commented Apr 21, 2012 at 11:54
  • \$\begingroup\$ @JonathanHobbs Great... I do not know where to being solving this though - any pointers? \$\endgroup\$ Commented Apr 21, 2012 at 13:48
  • 1
    \$\begingroup\$ Restart your research into how entity systems work. In the diagram in Evolve Your Heirarchy, the colourful graph represents individual component types which you create instances of, not types an Entity inherits from. If your entity is drawn, it contains a component that is drawable in a list of components. If it has a hitbox, it has a hitbox component; if it has a weapon it has a weapon component, etc. These are individual objects handling their own responsibilities: the entity is often little more than a container. \$\endgroup\$ Commented Apr 21, 2012 at 14:19
  • \$\begingroup\$ Basically the only reason you have this problem is because you're using inheritance. The entire reason entity systems exist is because the inheritance model was not flexible enough. Components create that flexibility. If you haven't read Evolve Your Heirarchy you should read it now. \$\endgroup\$ Commented Apr 21, 2012 at 14:22
  • \$\begingroup\$ I'm reading it right now, and it is interesting. Just to be sure, though, will a component-based entity system allow me to easily query all entities who have a specific component? This is quite crucial to find entities very quickly in a big map. \$\endgroup\$ Commented Apr 21, 2012 at 14:54

3 Answers 3

1
\$\begingroup\$

It seems like a backwards step to use inheritance for a entity system. Check out the Artemis framework created by Arni Arent and Tiago Costa. It's in Java, but someone has been porting it to C# (I believe they are a user here too). I can't speak for the quality of the conversion to C# since I haven't looked at it. However, the original is very nice. It uses longs to keep track of which entities have which components and belong to which systems (that means a maximum of 64, one for each bit). It's a pretty nice system and I learned a lot from reading the code when I was implementing my own entity/component framework. With this system it's easy to add and remove components, but as Jeremiah said, it would be better to have a solid component which can change states. That's a far better option that adding or removing components.

Further, the Artemis framework supports groups and tags. This means you can make a "Player" group or tag and treat it specially depending on the situation. There is a sample game that has its source available as well.

\$\endgroup\$
0
\$\begingroup\$

I would maybe let your interfaces change the properties they set. In your example, ISolid shouldn't indicate that the object is solid; it should let you set the Solid property. Then you put ISolid on anything whose solidity can change.

\$\endgroup\$
0
\$\begingroup\$

You're saying that since everything is defined by its type and not by its attributes then nothing can change dynamically. That's because you're (ab)using interfaces as they were attributes to put together your mix of stuff that an entity can do/be.

I get the reason why you're trying to use interfaces since you said you have some sort of a grid-base creation library.

My suggestion is to take a step back and look the whole picture: you want to cover almost any tile based game. So first off you need an expanded game logic.

In any tile based game you're going to have tiles. Fairly obvious. You may have Pawns, or even implement them as particular states for tile.

Using a set of states/attributes, maybe even 'compressing' them as other users have suggested, thus achieving some sort of finite-state machine could be the right direction since the basic game logic is pretty simple.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .