An example would be to have a variable in your player called _currentState:
private var _currentState: IPlayerBehaviour;
IPlayerBehaviour here represents an interface. An interface is a special type of class that you ‘implement’ (similar to extending a class) to say that a bunch of different classes will definitely perform some function. For example, in our case, we want a bunch of classes that have an update function. What the update function does can differ, but we definitely want one in each different Behaviour class. So we create our interface (in FlashDevelop, right click a folder and add > new interface) and add our update function prototype:
package src.entities.player.behaviours
{
/**
* ...
* @author
*/
public interface IPlayerBehaviour
{
function update(player: Player): void;
}
}
This essentially says that any class that ‘implements’ IPlayerBehaviour will have that update function that takes the Player class as an argument. This means you could have as many different behaviours as you need (PlayerServedBehaviour, PlayerSpikedBehaviour etc) and they can all have different update functions, then they will all be ‘compatible’ with our player’s _currentState variable (because it is of type IPlayerBehaviour). So lets create a basic behaviour (right click folder, add > new class, and fill in the interface in the ‘interfaces’ box):
package src.entities.player.behaviours
{
/**
* ...
* @author ...
*/
public class PlayerServedBehaviour implements IPlayerBehaviour
{
public function PlayerServedBehaviour()
{
}
public function update(player: Player): void
{
//logic for this state goes here
}
}
}
Now you have a separate class for that behaviour. You can go ahead and create loads of classes for all the different behaviours. The final thing to do is make the Player class actually use them. I like the idea of using a state map for this - basically, you’ll have a Dictionary of behaviour classes stored in the Player and referenced by names (“serving” will pull out a PlayerServingBehaviour instance, for example), then you set _currentState to that. Finally, in the player’s update, all you need to do is use _currentState.update(this); to call the current behaviour’s update function. The power here lies in the fact that it’s completely flexible; it doesn’t matter what behaviour class is actually in _currentState, it will just call the update function then the class itself handles all the logic (keeping your Player class clean and tidy, and much easier to update).
So at the top of our Player class:
private var _behaviourList: Dictionary;
private var _currentState: IPlayerBehaviour;
private var _stateName: String;
In our player constructor we want to set up the Dictionary to add all the states that exist and link them to a string name:
_behaviourList["blocked"] = new PlayerBlockedBehaviour;
_behaviourList["spiked"] = new PlayerSpikedBehaviour;
_behaviourList["blocking"] = new PlayerBlockingBehaviour;
//set your starting state here
_stateName = "blah";
_currentState = _behaviourList[_stateName];
Finally, in the Player’s update function we add the magic to make it all work:
//update state to match the _stateName.
_currentState = _behaviourList[_stateName];
//run the current state update
if(_currentState != null) _currentState.update(this);
We pass in the ‘this’ reference to our Player class to make sure we can easily change the Player’s variables (x/y etc) in the behaviour class.
Then all you need to do is change the _stateName whenever you want the player’s behaviour to change. Perhaps make it public or give it a setter function, so that other classes can change it (or indeed, behaviour classes can change it as appropriate!). This kind of technique is incredibly flexible and powerful, and once you get used to thinking in terms of interfaces, abstraction etc it can really change the way you think about programming.