I'm making a turn-based game in Unity 3D. Since it's turn-based, I'm trying to implement a state machine to control the general flow of gameplay. The player will control several units, like in games like XCOM, and I think a state machine for each unit may also be useful to control whether a unit is idle, currently selected, destroyed, etc. Weapons could possibly use them as well with states like idle and targeting.
Point is, I'm trying to abstract my state logic as much as possible so it's universal among the different state machines while also DRYing up my code so I'm not repeating things. To accomplish this, I'm of course making use of interfaces, such as my IState interface here:
namespace MechGame.Common.States
{
public interface IState
{
void EnterState();
void ExitState();
void UpdateState();
}
}
The problem comes when I try to enforce fields on derived classes. I've been told many times to think of interfaces as a contract, telling a class "you must implement X, Y, and Z." I know some objects in my state machine logic will need some private member fields. I'd like to declare those fields in my base classes in order to DRY up my code, almost enforcing them the way an interface enforces methods. The problem is how do I declare the types of these object fields in a way that is abstract? For instance, each state machine will have a state manager that extends the StateManager class:
namespace MechGame.Common.States
{
public interface IStateManager
{
State CurrentState { get; }
void NextState();
void TransitionToState(State state);
}
}
using UnityEngine;
namespace MechGame.Common.States
{
public abstract class StateManager : MonoBehaviour, IStateManager
{
protected State _current;
public State CurrentState { get => _current; }
public abstract void NextState();
public virtual void TransitionToState(State state)
{
if(_current != null)
_current.ExitState();
_current = state;
_current.EnterState();
}
public virtual void Update()
{
_current.UpdateState();
}
}
}
So for instance, I've got a class called CombatStateManager which extends StateManager and will control the flow of the game, player turn, enemy turn, etc. The problem is when I need to reference something like the StateManager's CurrentState field, which is of the abstract type State, I need to caste it to something more specific, such as in the OnCellClick method of the following class:
using MechGame.Common.States;
using MechGame.Selection;
namespace MechGame.Combat.States
{
public class CellEventDispatcher : IStateEventDispatcher
{
private CombatStateManager _manager;
public CellEventDispatcher(CombatStateManager manager)
{
_manager = manager;
}
public void DeregisterEventHandlers()
{
SelectionManager.OnCellClick -= OnCellClick;
}
void OnCellClick(int cellIndex, int buttonIndex)
{
((CombatState)_manager.CurrentState).CellEventHandler.OnCellClick(cellIndex, buttonIndex);
}
public void RegisterEventHandlers()
{
SelectionManager.OnCellClick += OnCellClick;
}
}
}
This might not seem like too big a deal, but it's a problem that's beginning to crop up in many places. Basically, like how an interface enforces that some methods must be implemented, I'd like to have my base classes enforce that any children implement some necessary fields. But it's leading to type issues as seen above. Is there no good way to enforce fields on child classes like I want to? If not, how else could I DRY up that code? I don't know much about generics, is that something that would be useful here?
You could implement a generic variant. Though this only works if each StateManager always works with only the same type of state.
For example:
public interface IStateManager<TState> where TState : IState
{
TState CurrentState { get; }
void NextState();
void TransitionToState(TState state);
}
public class StateManager<TState>
: MonoBehaviour, IStateManager<TState> where TState : IState
{
//...
}
public class CombatStateManager : StateManager<CombatState>
{
//...
}
Now when you access CurrentState
on a CombatStateManager
instance, it will always be of CombatState
. The point here being always. So it cannot be another state type (unless derived from CombatState).
Also, as a bit of addon. It is unnecessary to have both, an abstract base class AND an interface. Unless you plan on the interface being a thing that will also be included in other, unrelated, classes an abstract base is enough. Your definition of what an interface does is not wrong but in practice it is better suited to think of it as "addon" functionality or if some sort of "multi inheritance" is what you're after.
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments