My Tiles are Evil


As mentioned in my Refactoring for AI post, things are starting to get a bit complicated. The fundamental problem lies in the fact that my code is based on a dungeon generator design and not on a game design.

I make use of a generic map class that encapsulates an array of tiles. For dungeon generation I use an interface IDungeonTile to describe my dungeon tile behaviour.

    public interface IDungeonTile

    {

        bool IsObstacle { get; }

        bool IsWalkable { get; }

    }

The dungeon tiles know if they are obstacles and if they are walkable. My dungeon generator would start with a map filled with tiles full of rock (i.e. where IsObstable is true and IsWalkable is false) and then start to carve out corridors, place rooms and add doors.

My tiles also handled the logic for when a player would bump into them. Closed door tiles would know to open themselves, walls would repel the player etc. Notice that I only talk about the player, this was due to the fact that it was all I was thinking about at the time. The following code shows the logic of the ClosedDoorTile class for when something bumps into it.

        public bool HandleBump(DungeonMap dungeon, Point location)

        {

            if (IsObstacle)

            {

                dungeon[location] = new OpenDoorTile();

                return false;

            }

 

            return true;

        }

My path finding implementation started to raise the first real issues of this design. For the A* algorithm you need to provide a movement cost for the various terrain features. The question I had to ask myself was, what determined the movement cost of over a specific type of terrain? You could simplify this to whether a certain terrain type is an obstacle or not. My current design, where the IDungeonTile objects determined if they were obstacles or not, meant that I cold only support one type of movement cost for all entities (or actors).

My A* implementation required some further information that needed to be abstracted into some kind of common behaviour. For this I created the IMovementBehaviour interface listed below.

    public interface IMovementBehaviour

    {

        /// <summary>

        /// The directions available for movement. Use this to constrain the movement behaviour to certain directions when moving.

        /// </summary>

        Direction[] DirectionsAvailable { get; }

        /// <summary>

        /// Retrieves the movement cost for this behaviour based on the terrain type.

        /// </summary>

        /// <param name=”dungeonTile”>The dungeon terrain type to evaluate</param>

        /// <returns>The movement cost calculated for the dungeon terrain type</returns>

        double GetTerrainMovementCost(IDungeonTile dungeonTile);

        /// <summary>

        /// Determines if this movement behaviour can walk on a specific terrain type

        /// </summary>

        /// <param name=”dungeonTile”>The dungeon terrain type to evaluate</param>

        /// <returns>Returns true if the behaviour can walk on the terrain type</returns>

        bool CanWalkOnTerrain(IDungeonTile dungeonTile);

        /// <summary>

        /// Handles the interaction with a dungeon terrain type at a specific location

        /// </summary>

        /// <param name=”dungeon”>The dungeon representing the terrain</param>

        /// <param name=”location”>The location to bump into</param>

        /// <returns>Return true if the bump was successful</returns>

        bool HandleBump(DungeonMap dungeon, Point location);

    }

The idea behind the movement behaviours was to abstract how something moves around the dungeon away from the terrain itself. I envisaged that a PlayerMovementBehaviour would react completely different to a GhostMovementBehaviour for example. With the IMovementBehaviour interface I was able to do this by

  • limiting the directions available for movement,
  • terrain movement cost based on terrain type,
  • determining if a particular terrain type was movable,
  • and handling what happened if a particular “movement behaviour” bumped into something.

(I am not completely convinced that HandleBump should belong with movement behaviour.)

My IDungeonTiles now no longer define if they are obstacles or not. Rather I envisage them to define some kind of property such as whether they are solid rock, water, fire, etc. The movement behaviour would use these properties to determine if an actor can walk (or move) on the terrain and the associated movement cost for the terrain type.

Of course I now have to refactor all my original code to make use of movement behaviours. The end result will be a more flexible system which is no longer tied to a specific type of actor.

Advertisements

3 thoughts on “My Tiles are Evil

  1. Dirk,

    Very informative. Before I even saw your comment, I noticed a definite code smell with HandleBump being in there — I don’t follow your thinking there at all.

    From a logical standpoint you are defining a behaviour describing a game entity. Adjectives are “forever and always”, i.e. they exist in the present simple tense. So why place an incident-related variable in here?

    In other words I see instantiations of your interface as being adjectives describing behaviour for all entities referencing need to implement that behaviour, rather than describing any single entity’s *state* at a given time.

    Regards,
    Nick

  2. Er, that last paragraph was meant to read,

    “In other words I see instantiations of your interface as being adjectives describing behaviour for any and all entities needing to implement that behaviour, as opposed to describing the *state* of any given entity at a given time.”

    Perhaps I misunderstand your intent there, though.

  3. You are correct. But you must understand the context. The original dungeon generator was developed using a test first approach. I purposely implemented the simplest thing that would make it work. The next step was to refactor. Hence the post šŸ˜›

    The important thing was the learning that the tiles should not determine if they block or slow down an actor. Rather the actor should determine this based on the tile composition. My current thinking is that an actor’s race should determine his movement behavior.

    The tricky thing is how and where to handle actions an actor can perform on a tile. For example opening a door or closing it…

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s