Last active
October 30, 2022 18:17
-
-
Save CoffeeVampir3/f0431af759d84ff4e98867995735c371 to your computer and use it in GitHub Desktop.
Example of a real State Machine
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using UnityEngine; | |
namespace Vampire.CG | |
{ | |
[Serializable] | |
public class CardDraggingState : IState | |
{ | |
[SerializeField] | |
private Transform transform; | |
[SerializeField] | |
private CanvasGroup canvasGroup; | |
private Vector3 mouseOffset; | |
public void Enter() | |
{ | |
canvasGroup.blocksRaycasts = false; | |
mouseOffset = transform.position - GraphicsHelper.GetMousePos(); | |
} | |
public void Exit() | |
{ | |
canvasGroup.blocksRaycasts = true; | |
} | |
public void Update() | |
{ | |
mouseOffset.x *= .80f; | |
mouseOffset.y *= .80f; | |
transform.position = GraphicsHelper.GetMousePos() + mouseOffset; | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using _Testing; | |
using UnityEngine; | |
using UnityEngine.EventSystems; | |
using VampireLibrary; | |
namespace Vampire.CG | |
{ | |
public class CardStateMachine : MonoBehaviour, ITargetable, | |
IDragHandler, IBeginDragHandler, IEndDragHandler | |
{ | |
[SerializeReference] | |
private CardDraggingState draggingState = new(); | |
[SerializeReference] | |
private FloatingToTargetState floatToTargetState = new(); | |
[SerializeReference] | |
private DroppedOnValidTargetState droppedOnTargetState = new(); | |
private readonly StateMachine stateMachine = new(); | |
private readonly PtrTo<Vector3> sharedV3 = new(); | |
private bool isDragging = false; | |
public void Awake() | |
{ | |
sharedV3.referenceValue = transform.position; | |
var initialState = new IdleState(); | |
floatToTargetState.Init(sharedV3); | |
droppedOnTargetState.Init(sharedV3); | |
stateMachine.AddTransition(initialState, draggingState, | |
() => isDragging && !droppedOnTargetState.HasSuccessfullyDropped); | |
stateMachine.AddTransition(draggingState, droppedOnTargetState, | |
() => !isDragging && ScanForValidTargets()); | |
stateMachine.AddTransition(droppedOnTargetState, floatToTargetState, | |
() => true); | |
stateMachine.AddTransition(draggingState, floatToTargetState, | |
() => !isDragging); | |
stateMachine.AddTransition(floatToTargetState, initialState, | |
() => floatToTargetState.currentCompletion >= 1f); | |
stateMachine.SetState(initialState); | |
} | |
private bool CheckCosts() | |
{ | |
if (!TryGetComponent<CardCosts>(out var costs)) | |
{ | |
Debug.Log( | |
"Costs: ".ColorString(Color.green) + name + | |
" does not have costs so it is considered free.".ColorString(Color.yellow)); | |
return true; | |
} | |
return costs.CheckCosts(); | |
} | |
private bool CheckOccupation(GameObject dropTarget) | |
{ | |
return false; | |
/* | |
//Is the target occupiable, can we occupy it? | |
var occupier = GetComponent<IOccupier>(); | |
if (occupier == null) | |
return true; | |
return dropTarget is not IOccupiable occupiable || occupiable.CanBeOccupied(); | |
*/ | |
} | |
private bool CheckBehaviours(GameObject dropTarget) | |
{ | |
/* | |
var dropBehaviours = GetComponents<IDroppable>(); | |
foreach (var behaviour in dropBehaviours) | |
{ | |
//Is this behaviour a valid target? | |
if (!dropTarget.CanDropOn(behaviour.ValidTargets)) | |
{ | |
Debug.Log("Targeting: ".ColorString(Color.green) + name + "'s behaviour of type: " + behaviour.GetType() + | |
" was not valid to be dropped on ".ColorString(Color.red) + dropTarget); | |
continue; | |
} | |
droppedOnTargetState.dropBehaviour = behaviour; | |
return true; | |
} | |
*/ | |
return false; | |
} | |
private bool ScanForValidTargets() | |
{ | |
/* | |
bool droppedOnAnything = transform.TryRaycastFor<IDropTarget>(out var dropTargets); | |
if (!droppedOnAnything) return false; | |
foreach (var dropTarget in dropTargets) | |
{ | |
cardEffectDropTarget.currentTarget = dropTarget.TargetObject; | |
droppedOnTargetState.dropTarget = dropTarget; | |
if (CheckBehaviours(dropTarget) //Order matters | |
&& CheckOccupation(dropTarget) | |
&& CheckCosts()) | |
{ | |
return true; | |
} | |
} | |
*/ | |
return false; | |
} | |
private void Update() => stateMachine.Tick(); | |
//Targetable. | |
public void OnPointerClick(PointerEventData eventData) | |
{ | |
if (!TargetingHelper.ValidateTarget(GetComponent<ObjectSymbols>())) | |
{ | |
eventData.Use(); | |
return; | |
} | |
TargetingHelper.OnTargeted(this); | |
eventData.Use(); | |
} | |
public bool canDrag = false; | |
//Draggable. | |
public void OnBeginDrag(PointerEventData eventData) | |
{ | |
if (!canDrag) | |
{ | |
OnPointerClick(eventData); | |
return; | |
} | |
if (!TargetingHelper.ValidateTarget(GetComponent<ObjectSymbols>())) | |
{ | |
eventData.Use(); | |
return; | |
} | |
transform.SetAsLastSibling(); | |
isDragging = true; | |
} | |
public void OnEndDrag(PointerEventData eventData) | |
{ | |
isDragging = false; | |
} | |
public void OnDrag(PointerEventData eventData) | |
{ | |
//Empty | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace VampireLibrary | |
{ | |
public static class DictionaryExtension | |
{ | |
/// <summary> | |
/// Adds an item to the given dictionary where the value is a list. If the list | |
/// is uninitialized it creates a new list before adding the item. | |
/// </summary> | |
public static void AddListItem<IndexType, ListType, ValueType> | |
(this Dictionary<IndexType, ListType> dict, IndexType key, ValueType value) | |
where ListType : IList, new() | |
{ | |
if (!dict.TryGetValue(key, out var val)) | |
{ | |
val = new ListType(); | |
dict.Add(key, val); | |
} | |
val.Add(value); | |
} | |
public static void AddHashItem<IndexType, HashType, ValueType> | |
(this Dictionary<IndexType, HashType> dict, IndexType key, ValueType value) | |
where HashType : HashSet<ValueType>, new() | |
{ | |
if (!dict.TryGetValue(key, out var val)) | |
{ | |
val = new HashType(); | |
dict.Add(key, val); | |
} | |
val.Add(value); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using UnityEngine; | |
namespace Vampire.CG | |
{ | |
[Serializable] | |
public class DroppedOnValidTargetState : IState | |
{ | |
public GameObject dropTarget; | |
public IDroppable dropBehaviour; | |
[NonSerialized] | |
public bool HasSuccessfullyDropped = false; | |
private PtrTo<Vector3> floatTarget; | |
[SerializeField] | |
private GameObject cardObject; | |
[SerializeField] | |
private EffectTarget effectDropTarget; | |
public void Init(PtrTo<Vector3> sharedV3) | |
{ | |
floatTarget = sharedV3; | |
} | |
public void PayCardCosts() | |
{ | |
if (!cardObject.TryGetComponent<CardCosts>(out var costs)) | |
return; | |
costs.PayCosts(); | |
} | |
public void Execute() | |
{ | |
/* | |
var occupier = cardObject.GetComponent<IOccupier>(); | |
if (occupier != null && dropTarget is IOccupiable occupiable) | |
occupiable.Occupy(occupier); | |
floatTarget.referenceValue = dropTarget.Position; | |
effectDropTarget.currentTarget = dropTarget.TargetObject; | |
PayCardCosts(); | |
HasSuccessfullyDropped = true; | |
dropBehaviour.Run(dropTarget); | |
*/ | |
} | |
public void Enter() | |
{ | |
Execute(); | |
} | |
public void Exit() | |
{ | |
//Empty | |
} | |
public void Update() | |
{ | |
//Empty | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using UnityEngine; | |
namespace Vampire.CG | |
{ | |
[Serializable] | |
public class FloatingToTargetState : IState | |
{ | |
[SerializeField] | |
private float floatSpeed = 1f; | |
[SerializeField] | |
private float floatPower = 1f; | |
[SerializeField] | |
private Transform transform; | |
[NonSerialized] | |
public float currentCompletion = 0; | |
private Vector3 startPosition; | |
private PtrTo<Vector3> endPosition; | |
private float travelRate; | |
private float dist; | |
public void Init(PtrTo<Vector3> sharedTargetPosition) | |
{ | |
endPosition = sharedTargetPosition; | |
} | |
public void Enter() | |
{ | |
startPosition = transform.position; | |
currentCompletion = 0f; | |
dist = Vector2.Distance(endPosition.referenceValue, startPosition); | |
travelRate = Mathf.Pow(dist, floatPower * floatPower) * floatSpeed; | |
} | |
public void Exit() | |
{ | |
//empty | |
} | |
public void Update() | |
{ | |
currentCompletion += Time.deltaTime * travelRate * travelRate; | |
transform.position = Vector2.Lerp(startPosition, endPosition.referenceValue, currentCompletion); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Vampire.CG | |
{ | |
public class IdleState : IState | |
{ | |
public void Enter() | |
{ | |
//Empty | |
} | |
public void Exit() | |
{ | |
//Empty | |
} | |
public void Update() | |
{ | |
//Empty | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Vampire | |
{ | |
public interface IState | |
{ | |
void Enter(); | |
void Exit(); | |
void Update(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Vampire.CG | |
{ | |
public class PtrTo<T> | |
where T : struct | |
{ | |
public T referenceValue; | |
public PtrTo() { } | |
public PtrTo(T initialValue) | |
{ | |
referenceValue = initialValue; | |
} | |
public static implicit operator T(PtrTo<T> val) => val.referenceValue; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using VampireLibrary; | |
namespace Vampire | |
{ | |
public class StateMachine | |
{ | |
private IState currentState; | |
private readonly Dictionary<IState, List<Transition>> allTransitions = new(); | |
private readonly List<Transition> anyTransitions = new(); | |
private static readonly List<Transition> emptyTransitions = new(0); | |
private List<Transition> currentTransitions; | |
private record Transition | |
{ | |
public readonly IState state; | |
public readonly Func<bool> condition; | |
public Transition(IState state, Func<bool> condition) | |
{ | |
this.state = state; | |
this.condition = condition; | |
} | |
} | |
public void AddTransition(IState from, IState to, Func<bool> condition) | |
=> allTransitions.AddListItem(from, new Transition(to, condition)); | |
public void AddAnyTransition(IState to, Func<bool> condition) | |
=> anyTransitions.Add(new Transition(to, condition)); | |
public void SetState(IState targetState) | |
{ | |
currentState?.Exit(); | |
currentState = targetState; | |
currentTransitions = | |
!allTransitions.TryGetValue(currentState, out var transitions) ? | |
emptyTransitions : transitions; | |
currentState.Enter(); | |
} | |
public void Tick() | |
{ | |
foreach (var trans in anyTransitions) | |
{ | |
if (!trans.condition.Invoke()) continue; | |
SetState(trans.state); | |
goto StateChanged; | |
} | |
foreach (var trans in currentTransitions) | |
{ | |
if (!trans.condition.Invoke()) continue; | |
SetState(trans.state); | |
goto StateChanged; | |
} | |
StateChanged: | |
currentState.Update(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment