using UnityEngine; using UnityEngine.UI; using System.Collections; using MoreMountains.Tools; using System; using System.Collections.Generic; namespace MoreMountains.Tools { public struct MMStateChangeEvent where T: struct, IComparable, IConvertible, IFormattable { public GameObject Target; public MMStateMachine TargetStateMachine; public T NewState; public T PreviousState; public MMStateChangeEvent(MMStateMachine stateMachine) { Target = stateMachine.Target; TargetStateMachine = stateMachine; NewState = stateMachine.CurrentState; PreviousState = stateMachine.PreviousState; } } /// /// Public interface for the state machine. /// public interface MMIStateMachine { bool TriggerEvents { get; set; } } /// /// StateMachine manager, designed with simplicity in mind (as simple as a state machine can be anyway). /// To use it, you need an enum. For example : public enum CharacterConditions { Normal, ControlledMovement, Frozen, Paused, Dead } /// Declare it like so : public StateMachine ConditionStateMachine; /// Initialize it like that : ConditionStateMachine = new StateMachine(); /// Then from anywhere, all you need to do is update its state when needed, like that for example : ConditionStateMachine.ChangeState(CharacterConditions.Dead); /// The state machine will store for you its current and previous state, accessible at all times, and will also optionnally trigger events on enter/exit of these states. /// public class MMStateMachine : MMIStateMachine where T : struct, IComparable, IConvertible, IFormattable { /// If you set TriggerEvents to true, the state machine will trigger events when entering and exiting a state. /// Additionnally, it has options to trigger events for the current state on FixedUpdate, LateUpdate, but also /// on Update (separated in EarlyUpdate, Update and EndOfUpdate, triggered in this order at Update() /// To listen to these events, from any class, in its Start() method (or wherever you prefer), use MMEventManager.StartListening(gameObject.GetInstanceID().ToString()+"XXXEnter",OnXXXEnter); /// where XXX is the name of the state you're listening to, and OnXXXEnter is the method you want to call when that event is triggered. /// MMEventManager.StartListening(gameObject.GetInstanceID().ToString()+"CrouchingEarlyUpdate",OnCrouchingEarlyUpdate); for example will listen to the Early Update event of the Crouching state, and /// will trigger the OnCrouchingEarlyUpdate() method. public bool TriggerEvents { get; set; } /// the name of the target gameobject public GameObject Target; /// the current character's movement state public T CurrentState { get; protected set; } /// the character's movement state before entering the current one public T PreviousState { get; protected set; } public delegate void OnStateChangeDelegate(); /// an event you can listen to to listen locally to changes on that state machine /// to listen to them, from any class : /// void OnEnable() /// { /// yourReferenceToTheStateMachine.OnStateChange += OnStateChange; /// } /// void OnDisable() /// { /// yourReferenceToTheStateMachine.OnStateChange -= OnStateChange; /// } /// void OnStateChange() /// { /// // Do something /// } public OnStateChangeDelegate OnStateChange; /// /// Creates a new StateMachine, with a targetName (used for events, usually use GetInstanceID()), and whether you want to use events with it or not /// /// Target name. /// If set to true trigger events. public MMStateMachine(GameObject target, bool triggerEvents) { this.Target = target; this.TriggerEvents = triggerEvents; } /// /// Changes the current movement state to the one specified in the parameters, and triggers exit and enter events if needed /// /// New state. public virtual void ChangeState(T newState) { // if the "new state" is the current one, we do nothing and exit if (EqualityComparer.Default.Equals(newState, CurrentState)) { return; } // we store our previous character movement state PreviousState = CurrentState; CurrentState = newState; OnStateChange?.Invoke(); if (TriggerEvents) { MMEventManager.TriggerEvent (new MMStateChangeEvent (this)); } } /// /// Returns the character to the state it was in before its current state /// public virtual void RestorePreviousState() { // we restore our previous state CurrentState = PreviousState; OnStateChange?.Invoke(); if (TriggerEvents) { MMEventManager.TriggerEvent (new MMStateChangeEvent (this)); } } } }