views:

291

answers:

8

I'm building a simple game which consists of Mobiles -- the in-game characters (Mobs). Each mob can perform certain functions. In order to give that functionality to the Mob, I've created a Behavior.

For example, let's say a mob needs to move around the game field, I would give it the MoveBehavior - this is added to an internal list of Behaviors for the mob class:

// Defined in the Mob class
List<Behavior> behaviors;

// Later on, add behavior...
movingMob.addBehavior(new MovingBehavior());

My question is this. Most behaviors will manipulate something about the mob. In the MoveBehavior example, it will change the mob's X,Y position in the world. However, each behavior needs specific information, such as "movementRate" -- where should movementRate be stored?

Should it be stored in the Mob class? Other Mobs may attempt to interact with it by slowing/speeding up the mob and it's easier to access at the mob level... but not all mobs have a movementRate so it would cause clutter.

Or should it be stored in the MoveBehavior class? This hides it away, making it a little harder for other mobs to interact with - but it doesn't clutter up a non-moving mob with extra and un-used properties (for example, a tower that doesn't move would never need to use the movementRate).

A: 

IMHO, the movement rate is associated with the movingBehavior rather than with a Mob itself, and as you said, it doesn't necessarily move. So the variable should be associated with the behavior, a change in the movementRate is a change to his Behavior, not to the mob himself.

You could also create a base Mob class, and derive a MovingMob one. But I guess, this doesn't really apply, once apparently you can have an arbitrary combination of different behaviors...

-- EDIT --

First, apparently you won't have the same type of behavior twice in the same Mob (like, no mob has two movementBehaviors at the same type), so a set is a better option in this case, as it avoids duplicates

You could have a method in each mob like

    public Behavior GetBehavior(Type type)
    {
        foreach (var behavior in behaviorHashSet)
        {
            if ( behavior.GetType() == type)
                return behavior;

        }
        return null;
    }

Then you could do whatever you want with this behavior once you have a Mob. Also, you could change the GetHashCode() and Equals() method to ensure you have no duplicate, or make the GetBehavior method even faster (constant time)

Samuel Carrijo
So in the case where I have another mob, say one that makes all mobs around it move faster - how would that mob manipulate the behavior's movement rate? The mob knows about the other mobs, but not about the other's mobs behaviors (plus, looping through a list of behaviors to find and change the rate seems overly complex).
Chu
Then create a simpler way to access individual behaviors. The problem in this case is your underlying design then. Perhaps the list of behaviors shouldn't be a simple List. Or perhaps it should be a List plus a bunch of helper functions (say, a generic Get<BehaviorType>() member function). Or perhaps it should be a Dictionary instead of a List. Or perhaps, the basic idea that each Mob class has a bunch of independent behaviors is flawed, and has to be changed.In order to affect the movement behavior of others, it makes sense that you have to know about their movement behavior, doesn't it?
jalf
+1  A: 

If it is related to the behavior, and only makes sense in the context of that behavior, then it should be stored as part of it.

A movement rate only makes sense for something that can move. Which means it should be stored as part of the object that represents its ability to move, which seems to be your MoveBehavior.

If that makes it too hard to access, it sounds more like a problem with your design. Then the question is not "should I cheat, and place some of the variables inside the Mob class instead of the behavior it belongs to", but rather "how do I make it easier to interact with these individual behaviors".

I can think of several ways to implement this. The obvious is a simple member function on the Mob class which allows you to select individual behaviors, something like this:

class Mob {
  private List<Behavior> behaviors;
  public T Get<T>(); // try to find the requested behavior type, and return it if it exists
}

Others can then do something like this:

Mob m;
MovementBehavior b = m.Get<MovementBehavior();
if (b != null) {
  b.SetMovementRate(1.20f);
}

You might also place some of this outside the Mob class, creating a helper function which modifies the movement rate if it exists, and does nothing otherwise:

static class MovementHelper {
  public static SetMovementRate(Mob m, float movementrate){
    MovementBehavior b = m.Get<MovementBehavior();
    if (b != null) {
      b.SetMovementRate(1.20f);
    }
  }
}

and then others could use it like this:

MovementHelper.SetMovementRate(m, 1.20f);

which would provide easy access to modifying the behavior, but without cluttering up the Mob class. (Of course, it'd be tempting to turn this into an extension method, but that might lead to too much assorted clutter in the Mob class' public interface. It may be preferable to make it clear that this is helper functionality that resides outside the Mob class itself)

jalf
I'm with you jalf - I feel it should be part of the MoveBehavior. Any thoughts on how I can accomplish an easy way of interaction with the rate should it need to change as a result of some external action against the mob?
Chu
It depends a lot on the rest of your design. The simple way woudl be to give the Mob class a simple Get function which allows you to retrieve any single behavior from it. If I want to change the movement behavior of a Mob m, I'd call m.Get<MovementBehavior>(), and if it doesn't return null, I have the behavior I want to work on. That would solve this specific problem, but there may be other complexities to consider. What would you do if two different behaviors on the same mob need to share data, for example? It probably isn't realistic to assume that each behavior exists in a vacuum.
jalf
A: 

Edit: The below answer only really makes sense if you're not instancing a new MovingBehavior for every mob, but just have a singleton MovingBehavior.

I'd say that the mob (ugh, I hate that word for game NPCs, but it's not your fault) should, when addBehavior() is called, get a BehaviorState object that's returned from addBehavior() and that it keeps around, and is keyed to the behavior added. Then provide an interface for MovingBehavior to easily retrieve its BehaviorState object from movingMob, and it stores whatever it needs to store there.

chaos
So you suggest that the BehaviorState be responsible for storing the moveRate and that the state be stored in the Mob object? Would a mob with 4-5 Behaviors each have their own BehaviorState or is it a single state object which stores all possible data for all possible behaviors?
Chu
1) Right. 2) I was thinking that each added behavior would have its own state tracking, so that the data structures involved could be designed for the needs of the behaviors. If it were just one state object, might as well just give the mob itself some generic state tracking functionality and let the behaviors use that. (Which may work fine too.)
chaos
sean riley's answer is pretty much what I'm talking about with the latter option.
chaos
A: 

So what are you trying to do?

What's the simplest way for you to store the movement rate data?

If it is only needed in the MoveBehavior class then it should be in there:

public class MoveBehavior {
    public int MovementRate { get; set; }
}

If it is needed inherently by the Mob class then it will be easier exposed through the Mob class:

public class Mob {
    public int MovementRate { get; set; }
}

public class MoveBehavior {
    public MoveBehavior(Mob mob) { MobInEffect = mob; }

    public Mob MobInEffect {get; set;}

    // can access MovementRate through MovInEffect.MovementRate
}

So it all depends on what you're trying to achieve with this behavior logic. I'd recommend you push the design decision until you really need to do it one way or another. Concentrate on doing it simple first and refactor later. Usually more often than not, doing early design guesswork can lead to overcomplicated architecture.


A more pragmatic solution…

What I mean is that you implement whatever you wanted from movement the in the Mob class first:

public class Mob {

    // Constructors and stuff here

    public void Move(long ticks) 
    {
        // do some voodoo magic with movement and MovementRate here
    }

    protected int MovementRate { get; set; }
}

And when that works, rip out that implementation to a MoveBehavior class if you really need to:

public class Mob {

    // Constructors and stuff here

    public MoveBehavior Moving { set; get; }

    public void Move(long ticks) 
    {
        Moving.Move(ticks, this);
    }
}

public class MoveBehavior {
    protected int MovementRate { get; set; }

    public void Move(long ticks, Mob mob)
    {
        // logic moved over here now
    }
}

After that if you really need to do more than one type of behavior but they share a common interface then create that interface by then and let the behaviors implement that.

Spoike
+4  A: 

This is the classic "behavioral composition" problem. The trade-off is that the more independent the behaviors are, the more difficult it is for them to interact with each other.

From a game programming viewpoint, the simplest solution is a decide on a set of "core" or "engine" data, and put that in the main object, then have the behaviors be able to access and modify that data - potentially through a functional interface.

If you want behavior specific data, that's fine, but to avoid collisions in the names of variables, you may want to make the interface for accessing it include the behavior name. Like:

obj.getBehaviorValue("movement", "speed")

obj.setBehaviorValue("movement", "speed", 4)

That way two behaviors can both define their own variables called speed and not collide. This type of cross-behavior getters and setters would allow communication when it is required.

I'd suggest looking at a scripting language like Lua or Python for this..

sean riley
+2  A: 

You could borrow a pattern from WPF (attached properties). The WPF guys needed a way to sort of attach properties to controls at run time. (for example, if you put a control inside a grid, it would be nice for the control to have a Row property -- they pseudo did this with attached properties.

It works something like: (note this probably doesn't precisely match WPF's implementation, and I'm leaving out the dependency property registration, as you aren't using XAML)


public class MoveBehavior: Behavior
{
  private static Dictionary<Mob, int> MovementRateProperty;

  public static void SetMovementRate(Mob theMob, int theRate)
  {
     MovementRateProperty[theMob] = theRate;
  }

  public static int GetMovementRate(Mob theMob)
  {
      // note, you will need handling for cases where it doesn't exist, etc
      return MovementRateProperty[theMob];
  }
}

The thing here is that the Behavior owns the property, but you don't have to go spelunking to get it Here's some code that retrieves a mob's movement rate:


// retrieve the rate for a given mob
int rate = MoveBehavior.GetMovementRate(theMob);
// set the rate for a given mob
MoveBehavior.SetMovementRate(mob, 5);
JMarsch
A: 

If i was designing something like this i would try out using interfaces to define which behaviors a mob has:

public interface IMovable
{
    int MovementRate { get; set; }
    void MoveTo(int x, int y);
}

public class Monster : Mob, IMovable
{
    public int MovementRate { get; set; }

    public void MoveTo(int x, int y)
    {
         // ...
    }
}

This way you can check if a mob can move by doing something like this:

Monster m = new Monster();
if (m is IMovable)
{
    m.MoveTo(someX, someY);
}
Jason Miesionczek
+2  A: 

Take a look at component systems/entity systems design:

http://www.devmaster.net/articles/oo-game-design/

By far the best I've seen till now.

Smart people say it's the only way to go with larger games, but it requires a shift in how you think about OOP.

Mikeon