How can I use Generics to make this FSM more flexible? - c#

I am trying to make an FSM that can be a bit more flexible by having a StateController base class that other controllers (PlayerController, AiController, etc) can derive from. So far the arguments passed have heavy dependencies with do not allow for such flexibility, hence, generics. Only thing is I cannot wrap my mind around the approach.
Here is the code for the FSM as is without the flexibility I was hoping to achieve.
StateController.cs
using UnityEngine;
public class StateController : MonoBehaviour
{
public State currentState;
public State previousState;
public State remainInState;
private void Start()
{
if (!currentState)
return;
currentState.OnEnter(this);
}
private void FixedUpdate()
{
if (!currentState)
return;
currentState.OnFixedUpdate(this);
}
private void Update()
{
if (!currentState)
return;
currentState.OnUpdate(this);
}
private void LateUpdate()
{
if (!currentState)
return;
currentState.OnLateUpdate(this);
}
public void ChangeState(State nextState)
{
if (nextState != remainInState)
{
currentState.OnExit(this);
previousState = currentState;
currentState = nextState;
currentState.OnEnter(this);
}
}
}
State.cs
using UnityEngine;
[CreateAssetMenu(menuName = ("State Controller/State"))]
public class State : ScriptableObject
{
public StateAction[] onEnter;
public StateAction[] onFixed;
public StateAction[] onUpdate;
public StateAction[] onLate;
public StateAction[] onExit;
public StateTransition[] transitions;
public void OnEnter(StateController controller)
{
ExecuteActions(controller, onEnter);
}
public void OnFixedUpdate(StateController controller)
{
ExecuteActions(controller, onFixed);
}
public void OnUpdate(StateController controller)
{
ExecuteActions(controller, onUpdate);
CheckTransitions(controller);
}
public void OnLateUpdate(StateController controller)
{
ExecuteActions(controller, onLate);
}
public void OnExit(StateController controller)
{
ExecuteActions(controller, onExit);
}
private void ExecuteActions(StateController controller, StateAction[] actions)
{
for (int i = 0; i < actions.Length; i++)
{
actions[i].Execute(controller);
}
}
private void CheckTransitions(StateController controller)
{
for (int i = 0; i < transitions.Length; i++)
{
bool result = transitions[i].condition.CheckCondition(controller);
if (result == true)
{
controller.ChangeState(transitions[i].trueState);
}
else
controller.ChangeState(transitions[i].falseState);
}
}
}
StateAction.cs
using UnityEngine;
public abstract class StateAction : ScriptableObject
{
public abstract void Execute(StateController controller);
}
StateCondition.cs
using UnityEngine;
public abstract class StateCondition : ScriptableObject
{
public abstract bool CheckCondition(StateController controller);
}
StateTransition.cs
[System.Serializable]
public class StateTransition
{
public StateCondition condition;
public State trueState;
public State falseState;
}
But I want the flexibilty to use StateController.cs as a base class and be able to derive another class from it (obviously making it abstract and using protected virutal void for functions) like a PlayerController script...
using UnityEngine;
public class PlayerController : StateController
{
//Other Variables Specific to this class
protected override void Start()
{
base.Start();
}
//Other Functions Specific to this class
}
I figured the use of Generics could help with this since I have another script which can only take a StateController as an argument...
[CreateAssetMenu(menuName = ("State Controller/Action Test"))]
public class ActionTest : StateAction
{
public override void Execute(StateController controller /*place PlayerController Here instead */)
{
Debug.Log(controller.currentState);
}
}
Generics could help but it's implementation is daunting to me. Here is an example of the above script with generics used to allow PlayerController to be passed in but would not remedy the system as State.cs would need T as an argument.
public abstract class StateAction<T> : ScriptableObject where T : StateController
{
public abstract void Execute(T controller);
}
using UnityEngine;
[CreateAssetMenu(menuName = ("State Controller/Action Test"))]
public class ActionTest : StateAction<PlayerController>
{
public override void Execute(PlayerController controller)
{
Debug.Log(controller.currentState);
}
}
I know the FSM needs to be restructured but is there anyone else who managed to rework a similar FSM with this approach?

Updated based on the comments
Try:
public abstract class StateAction : ScriptableObject
{
public abstract void Execute(StateController controller);
}
public abstract class StateAction<T> : StateAction
where T : StateController
{
public abstract void Execute(T controller);
public sealed override Execute(StateController controller)
{
if (controller is T)
{
Execute((T)controller);
}
}
}
Then you can have variables like:
StateAction[] onEnter;

Related

Unity ScriptableObject, UnityEvent & GenericObject usage

I would like to combine ScriptableObject along with UnityEvent and GenericObject usage. My ultimate goal is to create generic event and listener and then use ScriptableObject to create specific events e.g. GameObject, int and etc. and handle these with respective listeners.
Here is the code I have so far:
EventTemplate.cs
using System.Collections.Generic;
using UnityEngine;
public class EventTemplate<T> : ScriptableObject {
private List<ListenerTemplate<T>> listeners = new List<ListenerTemplate<T>>();
public void Raise(T go) {
for (int i = listeners.Count - 1; i >= 0; i--) {
listeners[i].OnEventRaised(go);
}
}
public void RegisterListener(ListenerTemplate<T> listener) {
listeners.Add(listener);
}
public void UnregisterListener(ListenerTemplate<T> listener) {
listeners.Remove(listener);
}
}
ListenerTemplate.cs
using UnityEngine;
using UnityEngine.Events;
[System.Serializable]
public class ResponseEvent<T> : UnityEvent<T> { }
public class ListenerTemplate<T> : MonoBehaviour {
//[SerializeField]
public EventTemplate<T> gameEvent;
//[SerializeField]
public ResponseEvent<T> response;
private void OnEnable() {
gameEvent.RegisterListener(this);
}
private void OnDisable() {
gameEvent.UnregisterListener(this);
}
public void OnEventRaised(T go) {
response.Invoke(go);
}
}
Now, when I have both generic types, I created one Event and one Listener for int type.
These are two files:
EventInt.cs
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "New Event Template", menuName = "Stage Management/Event Templates/Event Int")]
public class EventInt : EventTemplate<int> {
}
and ListenerInt.cs
using UnityEngine;
using UnityEngine.Events;
[System.Serializable]
public class ResponseInt : ResponseEvent<int> { }
public class ListenerInt : ListenerTemplate<int> {
}
then my expectation was, once I add ListenerInt.cs to specific game component via Editor, I will able to access gameEvent and response in the same fashion I can access them as if I define UnityEvent for int type.
However, the reality is that I cannot see / access neither gameEvent nor response via the Editor.
Unity serialization doesn't work on generics T.
you would need to explicitely create an inherited non-generic type for everything you want to serialize in the Inspector. You would need e.g. a
[Serializable] public class IntEvent : UnityEvent<T> { }
in order to be able to serialize it.
In order to do what you want (kind of) I would do this:
First use an interface like
public interface IEventListener<in T>
{
void OnEventRaised(T value);
}
Then make your ListenerTemplate
public abstract class ListenerTemplate<T> : MonoBehaviour, IEventListener<T>
{
// These have to be provided by the inheritor
public abstract UnityEvent<T> unityEvent { get; }
public abstract EventTemplate<T> gameEvent { get; }
private void OnEnable()
{
gameEvent.RegisterListener(this);
}
private void OnDisable()
{
gameEvent.UnregisterListener(this);
}
public void OnEventRaised(T value)
{
unityEvent.Invoke(value);
}
}
As you can see any class inheriting from ListenerTemplate<T> will have to somehow provide both the UnityEvent<T> and the EventTemplate<T>.
So e.g.
// The specific scriptable object doesn't change it just inherits
[CreateAssetMenu(fileName = "New Event Template", menuName = "Stage Management/Event Templates/Event Int")]
public class EventInt : EventTemplate<int>{ }
and
// Specific override for the UnityEvent
[Serializable] public class IntUnityEvent : UnityEvent<int> { }
public class ListenerInt : ListenerTemplate<int>
{
[SerializeField] private EventInt eventInt;
[SerializeField] private IntUnityEvent intUnityEvent;
// override and populate the two abstract properties
// with the references from the serialized fields
public override UnityEvent<int> unityEvent => intUnityEvent;
public override EventTemplate<int> gameEvent => eventInt;
}
This at least reduces the implementation overhead to these two fields for every inheritor and according specific implementations of EventTemplate and UnityEvent.
Finally the EventTemplate<T> just has to use a list of IEventListener instead
public abstract class EventTemplate<TValue> : ScriptableObject
{
private readonly List<IEventListener<TValue>> listeners = new List<IEventListener<TValue>>();
public void Raise(TValue go)
{
// actually why iterate backwards?
for (int i = listeners.Count - 1; i >= 0; i--)
{
listeners[i].OnEventRaised(go);
}
}
public void RegisterListener(ListenerTemplate<TValue> listener)
{
listeners.Add(listener);
}
public void UnregisterListener(ListenerTemplate<TValue> listener)
{
listeners.Remove(listener);
}
}

Inheritance, need advices

Let's say I have an ai or player, I want him to be able to use different weapons.
My design with weapons:
public class Weapon()
{
public virtual void FireWeapon(){} // this is useless for melee weapons
public virtual void SwingMelee(){} // this is useless for guns
public virtual void Reload(){} // this is also useless for melee weapons
}
Then in the ai controller class I simply call the function I want him to do.
This is where the ugly part is (I think)...
Controller class have a list containing some different weapons of ai and a weapon which is being used.
public class WeaponController
{
private List<Weapon> someWeapons;
private Weapon aWeapon;
public void Main()
{
if(/*"Some action or a button click" &&*/ aWeapon.CanSwingMelee() )
aWeapon.SwingMelee();
if(/*"Some action or a button click" &&*/ aWeapon.CanReload() )
aWeapon.Reload();
}
}
What is the better way to implement this? do you have any advices?
Seems that for every different action in a new weapon, I need to implement a function in the most parent Weapon class and I don't think it's a good idea...
The capability of an in-game object can be represented by an interface; you can check if a capability is present by attempting to cast to the interface. What's more, these interfaces can overlap, e.g. both melee and ranged weapons might both have an Attack method.
So for example:
public interface IWeapon
{
void Attack();
}
public interface IRangedWeapon
{
bool IsInRange(ITargetable target);
}
public interface IRequiresAmmunition
{
void Reload();
int AmmoRemaining { get; set; }
}
public class Sword : IWeapon
{
public virtual void Attack() { //code }
}
public class Rifle : IWeapon, IRequiresAmmunition, IRangedWeapon
{
public virtual void Attack() { //code }
public virtual void Reload() { //code }
public virtual int AmmoRemaining { get { } set { } }
public virtual bool IsInrange (ITargetable target) { //code }
}
public class LaserGun: IWeapon, IRangedWeapon
{
public virtual void Attack() { //code }
public virtual bool IsInrange (ITargetable target) { //code }
}
public class WeaponController
{
private List<IWeapon> someWeapons;
private IWeapon aWeapon;
private ITargetable currentTarget;
public void Weapon_OnUse()
{
if (!currentTarget.IsHostile) return;
if (this.IsInMeleeRange(currentTarget))
{
aWeapon.Attack();
return;
}
var w = aWeapon as IRangedWeapon;
if (w != null && w.IsInRange(currentTarget)
{
aWeapon.Attack();
return;
}
context.HUD.Warn("Out of range");
}
public void Weapon_OnReload()
{
var w = aWeapon as IRequiresAmmunition;
if (w != null)
{
w.Reload();
context.HUD.DisplayAmmo(w.AmmoRemaining);
}
}
}
This seems like what abstract classes and inheritance is for:
public abstract class Weapon {
public abstract void Attack();
public abstract void Reload();
}
public class MeleeWeapon : Weapon {
public override void Attack() {
// swing sword
}
public override void Reload() {
// ignore reload
}
}
public class GunWeapon : Weapon {
public override void Attack() {
// fire gun
}
public override void Reload() {
// load weapon from inventory
}
}
public class WeaponController {
private List<Weapon> someWeapons;
private Weapon aWeapon;
public void Main() {
if (/*"Some action or a button click" */)
aWeapon.Attack();
else if (/* some other button click */)
aWeapon.Reload();
}
}
I don't recommend an approach that requires you to create new interfaces for every new behavior and check the type of the weapon. What about something like this:
(This is a very rough draft.)
public abstract class Weapon
{
protected Weapon(WeaponCommandStrategy[] commandStrategies)
{
CommandStrategies = commandStrategies;
}
protected IEnumerable<WeaponCommandStrategy> CommandStrategies { get; }
public void Use(WeaponCommand command)
{
var strategy = CommandStrategies.FirstOrDefault(c => c.Command == command);
strategy?.Execute();
}
}
public enum WeaponCommand
{
Fire,
Swing,
Reload
}
public abstract class WeaponCommandStrategy
{
public WeaponCommand Command { get; private set; }
protected WeaponCommandStrategy(WeaponCommand command)
{
Command = command;
}
public abstract void Execute();
}
Now you can give a weapon whatever behaviors you want it to have in the form of various instances of WeaponCommandStrategy. If a command is sent to a weapon, it executes it. If it doesn't support a command it ignores it. You could add a property to a weapon exposing the available commands so that you could display a list of available commands.
public class Sword : Weapon
{
// Perhaps use dependency injection here
public Sword()
: base(new WeaponCommandStrategy[] { new SwordSwingStrategy() })
{
}
}
public class SwordSwingStrategy : WeaponCommandStrategy
{
public SwordSwingStrategy() : base(WeaponCommand.Swing) { }
public override void Execute()
{
// Do whatever it does
}
}
This essentially makes a Weapon a composition of various things that a weapon can do. If several weapons behave similarly they can share strategies vs. having code duplicated between various weapons.

Inheritance chain in state machine branching out

I am trying to do a state machine to controll ai behaviour in Unity 3D.
My question is regarding inheritance. Im trying to set up some base logic that handles how and why states shoul be changed. But further down the inheritance line i need different kind of characters to be able to do character speicfic things. But im not able to do this with inheritnance.
Can someone confirm that my thinking is not how its done? then i know to find another solution.
PSEUDO CODE:
// STATE CONTROLLERS CONTROLL THE CHARACETER BY CHOOSING WITCH STATE THEY SHOULD BE IN
abstract class StateController {
StateBase state;
int HitPoints;
int Hunger:
abstract void Update()
{
CheckIfStateShouldChange();
state.UpdateState(this);
}
}
WolfStateController : StateController {
WolfState state;
override void Update()
{
base.Update();
state.Update(this);
}
}
SheepStateController : StateController {
SheepState state;
override void Update()
{
base.Update();
state.Update(this);
}
}
// STATES CONTAINS LOGIC FOR BEHAVIOUR IN A CERTAIN STATE
StateBase {
virtual void UpdateState( StateController controller)
{
// Does things all inheriting classes should do
}
}
WolfState : StateBase {
override void UpdateState( WolfStateController wolfstate)
{
base.UpdateState(WolfStateController wolfstate)
//Does wolf specific things that needs to be done in all WolfStates
}
}
WolfStalkAndHuntState : WolfState {
override void UpdateState( WolfStateController wolfstate)
{
base.UpdateState(WolfStateController wolfState);
//Hunts sheep and attacks on sight
}
}
SheepState : StateBase {
override void UpdateState( SheepStateController sheepState)
{
//Does sheepy things
}
}
SheepReproduceState : SheepState {
override void UpdateState( SheepStateController sheepState)
{
base.UpdateState(SheepStateController sheepState);
// Looks for mate and gets freaky
}
}
I would suggest something like this:
Controller Class:
public class SoliderController : MonoBehaviour
{
[HideInInspector] public sState currentState;
[HideInInspector] public FireState fireState;
[HideInInspector] public IdleState idleState;
[HideInInspector] public ChaseState chaseState;
private void Awake()
{
fireState = new FireState(this);
idleState = new IdleState(this);
chaseState = new ChaseState(this);
}
private void Start ()
{
currentState = idleState;
}
private void Update()
{
currentState.Update();
}
Interface:
public abstract class sState
{
public abstract void Update();
public abstract void ToChaseState();
public abstract void ToIdleState();
public abstract void ToFireState();
}
Example Class
public class IdleState : sState
{
private readonly SoliderController controller;
public IdleState(SoliderController soliderController)
{
controller = soliderController;
}
public override void Update()
{
Patrol();
//Condition to change state
if (*expresion1*)
ToChaseState();
if (*expresion2*)
ToFireState();
}
private void Patrol()
{
//Your Logic for the behvaiour wanted.
}
public override void ToChaseState()
{
controller.currentState = controller.chaseState;
}
public override void ToFireState()
{
controller.currentState = controller.fireState;
}
public override void ToIdleState()
{
Debug.LogWarning("Can't transition to same state");
}
}
This way checking for changes is way easier and also you can impletement state specific behaviours as well. Also, adding a new state goes really easy, you just implement the new Class and ToNewState method in the interface.
Hope it helped.

Method from interface takes one parameter but it will be used with 2 different objects

I have to do a simple rpg game and there are 2 types of entities: heroes and monsters. Attack method will be implemented in both classes and it is contained by a interface called IAttack. The problem is that this method take a Monster type as parameter for hero class and a Hero type as parameter for monster class.
The code looks something like this:
The interface:
interface IAttack
{
void Attack(Object oponnnent);
}
The Hero class(which implements IAttack):
public void Attack(Monster opponent)
{
//code goes here
}
The Monster class(which implements IAttack):
public void Attack(Hero opponent)
{
//code goes here
}
The problem is I can not pass different types of arguments.
You could make an abstract class that Monster and Hero dervice from.
public abstract class PlayerType
{
public abstract int Health();
}
Then in your interface use the new abstract type:
interface IAttack
{
void Attack(PlayerType oponnnent);
}
Monster class:
public class Monster : PlayerType, IAttack
{
public override int Health()
{
return 100;
}
public void Attack(PlayerType hero)
{
}
}
Hero class:
public class Hero : PlayerType, IAttack
{
public override int Health()
{
return 500; // He is a hero afterall ;)
}
public void Attack(PlayerType monster)
{
}
}
Why not have two interfaces? Something that can attack and something that can be attacked?
public interface IAttackable
{
void OnAttacked(IAttacker attacker);
}
public interface IAttacker
{
void OnAttack(IAttackable opponet);
}
public class Hero : IAttacker, IAttackable
{
public void OnAttack(IAttackable opponet)
{
}
public void OnAttacked(IAttacker attacker)
{
}
}
public class Monster : IAttacker, IAttackable
{
public void OnAttack(IAttackable opponet)
{
}
public void OnAttacked(IAttacker attacker)
{
}
}

Can I create a delegate for the entire class c#

I have used delegates to represent methods - but I now have many classes that have same methods (but different code in those methods).
Is there a way to delegate the entire class?
Pseudo code:
class myModelA
{
void update()
{
}
}
class myModelB
{
void update()
{
}
}
delegate class myModel;
if (x==1)
myModel = myModelA;
else
myModel = myModelB;
myModel.update();
I know I can delegate the "üpdate" method BUT in real world I have lots of methods and I would rather just simply delegate the class.
EDIT1 based on Jon Skeet's answer
BUT how do I declare a public variable? (non public variables compile OK)
public interface IModel
{
double myDouble; <<<< this gives an error
void Update();
}
public class MyModelA : IModel
{
public double myDouble;
public void Update() { ... }
}
public class MyModelB : IModel
{
public double myDouble;
public void Update() { ... }
}
No, in this case you don't want a delegate - you want an interface.
You create an interface which all of your classes implement:
public interface IModel
{
void Update();
}
public class MyModelA : IModel
{
public void Update() { ... }
}
public class MyModelB : IModel
{
public void Update() { ... }
}
Then:
IModel model;
if (x == 1)
{
model = new MyModelA();
}
else
{
model = new MyModelB();
}
model.Update();
As Jon Skeet, I think you need to use interfaces.
A little changed code from
http://www.dotnetperls.com/interface
using System;
interface IPerl
{
void Read();
}
class TestA : IPerl
{
public void Read()
{
Console.WriteLine("Read TestA");
}
}
class TestB : IPerl
{
public void Read()
{
Console.WriteLine("Read TestB");
}
}
class Program
{
static void Main()
{
IPerl perl = new TestA(); // Create instance.
perl.Read(); // Call method on interface.
}
}

Categories

Resources