Click here to Skip to main content
15,886,963 members
Articles / Programming Languages / C#

Dynamic Strategy State Pattern

Rate me:
Please Sign up or sign in to vote.
5.00/5 (2 votes)
11 Dec 2023CPOL1 min read 4.4K   7  
Example of use of this pattern in game development
Dynamic Strategy State Pattern helps to activate and deactivate strategies within a state when switching to another state, based on whether the other state contains that strategy or not.

Introduction

This pattern helps activate and deactivate strategies within a state when switching to another state, depending on whether the other state contains that strategy or not.

The current article will describe an example of use of this pattern in game development.

Example Situation

The player has three states: Character State, Car State, and Cinematic State. Each state has an array of strategies categorized by functionality and placed in corresponding array indices known as channels. These channels are activated or deactivated when switching between states. Some strategies will be shared among player states:

Image 1

In current example strategy channel is defined as C# Enum (integer value can be used as well):

C#
public enum StrategyChannel
{
    Camera = 0,
    Input = 1,
    Controls = 2
}

Each channel strategy can have any implementation and in current example is defined as interface:

C#
public interface IStrategyChannel
{
    StrategyChannel Channel { get; }
    void Activate();
    void Deactivate();
}

Example implementation of IStrategyChannel for CarCamera:

C#
public class CarCamera : IStrategyChannel
{
    public StrategyChannel Channel => StrategyChannel.Camera;

    public void Activate()
    {
        Console.WriteLine("Activate CarCamera");
    }

    public void Deactivate()
    {
        Console.WriteLine("Deactivate CarCamera");
    }
}

While switching to another state, we track the strategy changes in each strategy channel and activate or deactivate corresponding strategies if any changes occur:

Image 2

State class implementation:

C#
public class PlayerState
{
    private const int CHANNELS = 3;
    private readonly IStrategyChannel[] _strategyChannels = new IStrategyChannel[CHANNELS];

    public void SwitchToState(PlayerState state)
    {
        for (var i = 0; i < CHANNELS; i++)
        {
            var currentStrategy = _strategyChannels[i];
            var targetStrategy = state._strategyChannels[i];

            //strategy been changed
            if (currentStrategy != targetStrategy)
            {
                if (currentStrategy != null)
                {
                    currentStrategy.Deactivate();
                }

                if (targetStrategy != null)
                {
                    targetStrategy.Activate();
                }
            }
        }
    }

    public void SetChannelStrategy(IStrategyChannel strategy)
    {
        _strategyChannels[(int)strategy.Channel] = strategy;
    }
}

PlayerStatesController builds and keeps all the states, keeps track of current one and initializes initial state:

C#
public class PlayerStatesController
{
    private PlayerState _currentState;
    public readonly PlayerState CharacterState;
    public readonly PlayerState CarState;
    public readonly PlayerState CinematicState;

    public PlayerStatesController()
    {
        CharacterState = new PlayerState();
        CarState = new PlayerState();
        CinematicState = new PlayerState();

        var playerInput = new PlayerInput();
        CharacterState.SetChannelStrategy(new FreeLookCamera());
        CharacterState.SetChannelStrategy(playerInput);
        CarState.SetChannelStrategy(new CarCamera());
        CarState.SetChannelStrategy(playerInput);
        CarState.SetChannelStrategy(new CarControl());
        CinematicState.SetChannelStrategy(new CinematicCamera());
    }

    public void ActivateInitialState(PlayerState state)
    {
        _currentState = state;
        _currentState.InitialActivate();
    }

    public void SwitchToState(PlayerState state)
    {
        _currentState.SwitchToState(state);
        _currentState = state;
    }
}

Usage Example

In this code example, we activate each state sequentially and then switch back to the first one:

C#
var statesController = new PlayerStatesController();

Console.WriteLine("Activating initial CharacterState:");
statesController.ActivateInitialState(statesController.CharacterState);
Console.WriteLine();

Console.WriteLine("Switching to CarState:");
statesController.SwitchToState(statesController.CarState);
Console.WriteLine();

Console.WriteLine("Switching to CinematicState:");
statesController.SwitchToState(statesController.CinematicState);
Console.WriteLine();

Console.WriteLine("Switching to CharacterState:");
statesController.SwitchToState(statesController.CharacterState);
Console.WriteLine();

After executing, we will get the following output:

Activating initial CharacterState:
Activate FreeLookCamera
Activate PlayerInput

Switching to CarState:
Deactivate FreeLookCamera
Activate CarCamera
Activate CarControl

Switching to CinematicState:
Deactivate CarCamera
Activate CinematicCamera
Deactivate PlayerInput
Deactivate CarControl

Switching to CharacterState:
Deactivate CinematicCamera
Activate FreeLookCamera
Activate PlayerInput

Github repository with example code: https://github.com/Stridemann/DynamicStrategyStatePattern.

History

  • 12th December, 2023: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Ukraine Ukraine
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
-- There are no messages in this forum --