Game logic dynamically extendable architecture implementation patterns
- by Vlad
When coding games there are a lot of cases when you need to inject your logic into existing class dynamically and without making unnecessary dependencies.
For an example I have a Rabbit which can be affected by freeze ability so it can't jump.
It could be implemented like this:
class Rabbit
{
public bool CanJump { get; set; }
void Jump()
{
if (!CanJump) return;
...
}
}
But If I have more than one ability that can prevent it from jumping? I can't just set one property because some circumstances can be activated simultanously.
Another solution?
class Rabbit
{
public bool Frozen { get; set; }
public bool InWater { get; set; }
bool CanJump { get { return !Frozen && !InWater; } }
}
Bad. The Rabbit class can't know all the circumstances it can run into. Who knows what else will game designer want to add: may be an ability that changes gravity on an area?
May be make a stack of bool values for CanJump property? No, because abilities can be deactivated not in that order in which they were activated.
I need a way to seperate ability logic that prevent the Rabbit from jumping from the Rabbit itself.
One possible solution for this is making special checking event:
class Rabbit
{
class CheckJumpEventArgs : EventArgs
{
public bool Veto { get; set; }
}
public event EventHandler<CheckJumpEvent> OnCheckJump;
void Jump()
{
var args = new CheckJumpEventArgs();
if (OnCheckJump != null) OnCheckJump(this, args);
if (!args.Veto) return;
...
}
}
But it's a lot of code! A real Rabbit class would have a lot of properties like this (health and speed attributes, etc).
I'm thinking of borrowing something from MVVM pattern where you have all the properties and methods of an object implemented in a way where they can be easily extended from outside. Then I want to use it like this:
class FreezeAbility
{
void ActivateAbility()
{
_rabbit.CanJump.Push(ReturnFalse);
}
void DeactivateAbility()
{
_rabbit.CanJump.Remove(ReturnFalse);
}
// should be implemented as instance member
// so it can be "unsubscribed"
bool ReturnFalse(bool previousValue)
{
return false;
}
}
Is this approach good? What also should I consider? What are other suitable options and patterns? Any ready to use solutions?
UPDATE
The question is not about how to add different behaviors to an object dynamically but how its (or its behavior) implementation can be extended with external logic. I don't need to add a different behavior but I need a way to modify an exitsing one and I also need a possibiliity to undo changes.