diff --git a/Assets/PlayMaker Custom Actions/Logic/FloatAndBoolDecision.cs b/Assets/PlayMaker Custom Actions/Logic/FloatAndBoolDecision.cs
new file mode 100644
index 0000000..ecc660f
--- /dev/null
+++ b/Assets/PlayMaker Custom Actions/Logic/FloatAndBoolDecision.cs
@@ -0,0 +1,102 @@
+using System;
+
+namespace HutongGames.PlayMaker.Actions
+{
+ [ActionCategory(ActionCategory.Logic)]
+ [Tooltip("Make decision by accepting multiple data types. Returns a bool.")]
+ public class FloatAndBoolDecision : FsmStateAction
+ {
+ public enum Operation
+ {
+ Greater,
+ Lower,
+ Equals,
+ GreaterThenEquals,
+ LowerThenEquals
+ }
+
+ [RequiredField] [UIHint(UIHint.Variable)] [Tooltip("The float variable should be tested.")]
+ public FsmFloat targetFloat;
+
+ [RequiredField] [UIHint(UIHint.Variable)] [Tooltip("The tester float variable.")]
+ public FsmFloat testerFloat;
+
+ [RequiredField] [Tooltip("Operation which is used between the target and tester.")]
+ public Operation operation = Operation.Equals;
+
+ [RequiredField]
+ [UIHint(UIHint.Variable)]
+ [Tooltip("Boolean to be tested, which is used with short circuit and statement.")]
+ public FsmBool targetBool;
+
+ [Tooltip("Event to send if the Bool variable is True.")]
+ public FsmEvent isTrue;
+
+ [Tooltip("Event to send if the Bool variable is False.")]
+ public FsmEvent isFalse;
+
+ public bool everyFrame;
+
+ // Code that runs on entering the state.
+ public override void OnEnter()
+ {
+ Fsm.Event(areTheyTrue().Value ? isTrue : isFalse);
+
+ if (!everyFrame)
+ Finish();
+ }
+
+ // Code that runs every frame.
+ public override void OnUpdate()
+ {
+ Fsm.Event(areTheyTrue().Value ? isTrue : isFalse);
+ }
+
+ public override void Reset()
+ {
+ targetFloat = null;
+ testerFloat = null;
+ targetBool = false;
+ isTrue = null;
+ isFalse = null;
+ everyFrame = false;
+ }
+
+ // Perform custom error checking here.
+ public override string ErrorCheck()
+ {
+ return null;
+ }
+
+
+ private FsmBool areTheyTrue()
+ {
+ FsmBool result = new FsmBool();
+
+ switch (operation)
+ {
+ case Operation.Equals:
+ result = targetFloat.Value.Equals(testerFloat.Value) && targetBool.Value;
+ break;
+
+ case Operation.Greater:
+ result = targetFloat.Value > testerFloat.Value && targetBool.Value;
+ break;
+
+ case Operation.Lower:
+ result = targetFloat.Value < testerFloat.Value && targetBool.Value;
+ break;
+
+ case Operation.GreaterThenEquals:
+ result = targetFloat.Value >= testerFloat.Value && targetBool.Value;
+ break;
+
+ case Operation.LowerThenEquals:
+ result = targetFloat.Value <= testerFloat.Value && targetBool.Value;
+ break;
+ }
+
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/PlayMaker Custom Actions/Logic/IntAndBoolDecision.cs b/Assets/PlayMaker Custom Actions/Logic/IntAndBoolDecision.cs
new file mode 100644
index 0000000..66f92ab
--- /dev/null
+++ b/Assets/PlayMaker Custom Actions/Logic/IntAndBoolDecision.cs
@@ -0,0 +1,101 @@
+namespace HutongGames.PlayMaker.Actions
+{
+
+ [ActionCategory(ActionCategory.Logic)]
+ [Tooltip("Make decision by accepting multiple data types. Returns a bool.")]
+ public class IntAndBoolDecision: FsmStateAction
+ {
+ public enum Operation
+ {
+ Greater,
+ Lower,
+ Equals,
+ GreaterThenEquals,
+ LowerThenEquals
+ }
+
+ [RequiredField] [UIHint(UIHint.Variable)] [Tooltip("The float variable should be tested.")]
+ public FsmInt targetVariable;
+
+ [RequiredField] [Tooltip("The tester float variable.")]
+ public FsmInt testerVariable;
+
+ [RequiredField] [Tooltip("Operation which is used between the target and tester.")]
+ public Operation operation = Operation.Equals;
+
+ [RequiredField]
+ [UIHint(UIHint.Variable)]
+ [Tooltip("Boolean to be tested, which is used with short circuit and statement.")]
+ public FsmBool targetBool;
+
+ [Tooltip("Event to send if the Bool variable is True.")]
+ public FsmEvent isTrue;
+
+ [Tooltip("Event to send if the Bool variable is False.")]
+ public FsmEvent isFalse;
+
+ public bool everyFrame;
+
+ // Code that runs on entering the state.
+ public override void OnEnter()
+ {
+ Fsm.Event(areTheyTrue().Value ? isTrue : isFalse);
+
+ if (!everyFrame)
+ Finish();
+ }
+
+ // Code that runs every frame.
+ public override void OnUpdate()
+ {
+ Fsm.Event(areTheyTrue().Value ? isTrue : isFalse);
+ }
+
+ public override void Reset()
+ {
+ targetVariable = null;
+ testerVariable = null;
+ targetBool = false;
+ isTrue = null;
+ isFalse = null;
+ everyFrame = false;
+ }
+
+ // Perform custom error checking here.
+ public override string ErrorCheck()
+ {
+ return null;
+ }
+
+
+ private FsmBool areTheyTrue()
+ {
+ FsmBool result = new FsmBool();
+
+ switch (operation)
+ {
+ case Operation.Equals:
+ result = targetVariable.Value.Equals(testerVariable.Value) && targetBool.Value;
+ break;
+
+ case Operation.Greater:
+ result = targetVariable.Value > testerVariable.Value && targetBool.Value;
+ break;
+
+ case Operation.Lower:
+ result = targetVariable.Value < testerVariable.Value && targetBool.Value;
+ break;
+
+ case Operation.GreaterThenEquals:
+ result = targetVariable.Value >= testerVariable.Value && targetBool.Value;
+ break;
+
+ case Operation.LowerThenEquals:
+ result = targetVariable.Value <= testerVariable.Value && targetBool.Value;
+ break;
+ }
+
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/PlayMaker Custom Actions/Logic/IntAndFloatAndBoolDecision.cs b/Assets/PlayMaker Custom Actions/Logic/IntAndFloatAndBoolDecision.cs
new file mode 100644
index 0000000..8e5d7bb
--- /dev/null
+++ b/Assets/PlayMaker Custom Actions/Logic/IntAndFloatAndBoolDecision.cs
@@ -0,0 +1,111 @@
+using static HutongGames.PlayMaker.Actions.FloatAndBoolDecision;
+
+namespace HutongGames.PlayMaker.Actions.Logic
+{
+
+ ///
+ /// Author is Istvan Nemeth.
+ ///
+ [ActionCategory(ActionCategory.Logic)]
+ [Tooltip("Make decision by accepting multiple data types. Returns a bool.")]
+ public class IntAndFloatAndBoolDecision : FsmStateAction
+ {
+ [RequiredField] [UIHint(UIHint.Variable)] [Tooltip("The int variable should be tested.")]
+ public FsmInt targetInt;
+
+ [RequiredField] [UIHint(UIHint.Variable)] [Tooltip("The tester int variable.")]
+ public FsmInt testerInt;
+
+ [RequiredField]
+ [Tooltip("Operation which is used between the target and tester.")]
+ public Operation intOperation = Operation.Equals;
+
+ [RequiredField] [UIHint(UIHint.Variable)] [Tooltip("The float variable should be tested.")]
+ public FsmFloat targetFloat;
+
+ [RequiredField] [UIHint(UIHint.Variable)] [Tooltip("The tester float variable.")]
+ public FsmFloat testerFloat;
+
+ [RequiredField]
+ [Tooltip("Operation which is used between the target and tester.")]
+ public Operation floatOperation = Operation.Equals;
+
+ [RequiredField]
+ [UIHint(UIHint.Variable)]
+ [Tooltip("Boolean to be tested, which is used with short circuit and statement.")]
+ public FsmBool targetBool;
+
+ [Tooltip("Event to send if the Bool variable is True.")]
+ public FsmEvent isTrue;
+
+ [Tooltip("Event to send if the Bool variable is False.")]
+ public FsmEvent isFalse;
+
+ public bool everyFrame;
+
+ // Code that runs on entering the state.
+ public override void OnEnter()
+ {
+ Fsm.Event(areTheyTrue().Value ? isTrue : isFalse);
+
+ if (!everyFrame)
+ Finish();
+ }
+
+ // Code that runs every frame.
+ public override void OnUpdate()
+ {
+ Fsm.Event(areTheyTrue().Value ? isTrue : isFalse);
+ }
+
+ public override void Reset()
+ {
+ targetFloat = null;
+ testerFloat = null;
+ targetBool = false;
+ isTrue = null;
+ isFalse = null;
+ everyFrame = false;
+ }
+
+ private FsmBool areTheyTrue()
+ {
+ FsmBool result = new FsmBool();
+
+ if (intOperation == Operation.Equals)
+ {
+ result = targetInt.Value.Equals(testerInt.Value);
+ if (floatOperation == Operation.Equals)
+ {
+ result = result.Value && targetFloat.Value.Equals(testerFloat.Value) && targetBool.Value;
+ }
+ }
+
+
+ switch (floatOperation)
+ {
+ case Operation.Equals:
+ result = targetFloat.Value.Equals(testerFloat.Value) && targetBool.Value;
+ break;
+
+ case Operation.Greater:
+ result = targetFloat.Value > testerFloat.Value && targetBool.Value;
+ break;
+
+ case Operation.Lower:
+ result = targetFloat.Value < testerFloat.Value && targetBool.Value;
+ break;
+
+ case Operation.GreaterThenEquals:
+ result = targetFloat.Value >= testerFloat.Value && targetBool.Value;
+ break;
+
+ case Operation.LowerThenEquals:
+ result = targetFloat.Value <= testerFloat.Value && targetBool.Value;
+ break;
+ }
+
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/PlayMaker Custom Actions/Logic/IntAndFloatAndBoolDecision.cs.meta b/Assets/PlayMaker Custom Actions/Logic/IntAndFloatAndBoolDecision.cs.meta
new file mode 100644
index 0000000..93ba7ec
--- /dev/null
+++ b/Assets/PlayMaker Custom Actions/Logic/IntAndFloatAndBoolDecision.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 55403f4e06ef4d7b8cca6843fba1ad10
+timeCreated: 1613971230
\ No newline at end of file
diff --git a/Assets/PlayMaker Custom Actions/Logic/MultiTagSwitch.cs b/Assets/PlayMaker Custom Actions/Logic/MultiTagSwitch.cs
new file mode 100644
index 0000000..0ac0da9
--- /dev/null
+++ b/Assets/PlayMaker Custom Actions/Logic/MultiTagSwitch.cs
@@ -0,0 +1,68 @@
+using UnityEngine;
+
+namespace HutongGames.PlayMaker.Actions
+{
+
+ [ActionCategory(ActionCategory.Logic)]
+ [Tooltip("Iterate over the tags on the given gameobject. Defines for each an exit point as a switch flow control statement.")]
+ public class MultiTagSwitch : FsmStateAction
+ {
+ [RequiredField]
+ [UIHint(UIHint.Variable)]
+ [Tooltip("The GameObject to be tested.")]
+ public FsmGameObject gameObject;
+
+ [UIHint(UIHint.Tag)]
+ [Tooltip("The Tag to check for.")]
+ [CompoundArray("Tag switches", "Compare Tag", "Send Event")]
+ public FsmString[] compareTo;
+
+ [Tooltip("On compare success, the event to be sent.")]
+ public FsmEvent[] sendEvent;
+
+ [Tooltip("Repeat every frame.")]
+ public bool everyFrame;
+
+ [Tooltip("Store the matching Tag in a String variable.")]
+ public FsmString storeTag;
+
+ public override void Reset()
+ {
+ gameObject = null;
+ compareTo = new FsmString[1];
+ sendEvent = new FsmEvent[1];
+ everyFrame = false;
+ storeTag = null;
+ }
+
+ // Code that runs on entering the state.
+ public override void OnEnter()
+ {
+ CompareAllTags();
+
+ if (!everyFrame)
+ Finish();
+ }
+
+ // Code that runs every frame.
+ public override void OnUpdate()
+ {
+ CompareAllTags();
+ }
+
+ void CompareAllTags()
+ {
+ for (int i=0; i
+ /// Author is Istvan Nemeth.
+ ///
+ [ActionCategory(ActionCategory.StateMachine)]
+ [Tooltip(
+ "Accepts a list of fsm names, the gameobject contains the specified fsms and the corresponding bools -> fsms to be enabled or not.")]
+ public class MultiFsmEnabler : FsmStateAction
+ {
+ [RequiredField] [Tooltip("The GameObject which contains the defined fsms.")]
+ public FsmOwnerDefault FsmGameObject;
+
+ [Tooltip("List of FSMs to be enabled/disabled.")]
+ [CompoundArray("Tag switches", "Fsm Name", "Enable/Disable")]
+ public FsmString[] FsmNames;
+
+ [Tooltip("Turn all of the defined fsms to enabled/disabled.")]
+ public FsmBool[] ToBeEnabled;
+
+ private PlayMakerFSM _fsmComponent;
+
+ public override void Reset()
+ {
+ FsmGameObject = null;
+ FsmNames = null;
+ ToBeEnabled = null;
+ }
+
+ // Code that runs on entering the state.
+ public override void OnEnter()
+ {
+ DoEnableFsm();
+
+ Finish();
+ }
+
+ private void DoEnableFsm()
+ {
+ var ownerGameObject = FsmGameObject.OwnerOption == OwnerDefaultOption.UseOwner
+ ? Owner
+ : FsmGameObject.GameObject.Value;
+
+ if (ownerGameObject == null) return;
+
+ var i = 0;
+ foreach (var currentFsmName in FsmNames)
+ {
+ var fsmName = currentFsmName.Value;
+ if (!string.IsNullOrEmpty(fsmName))
+ {
+ var fsmComponents = ownerGameObject.GetComponents();
+ foreach (var component in fsmComponents)
+ {
+ if (component.FsmName.Equals(fsmName))
+ {
+ _fsmComponent = component;
+ _fsmComponent.enabled = ToBeEnabled[i].Value;
+ break;
+ }
+ }
+ }
+ else
+ {
+ _fsmComponent = ownerGameObject.GetComponent();
+ }
+
+
+ if (_fsmComponent == null) return;
+
+ i++;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/PlayMaker Custom Actions/StateMachine/MultiFsmEnabler.cs.meta b/Assets/PlayMaker Custom Actions/StateMachine/MultiFsmEnabler.cs.meta
new file mode 100644
index 0000000..714c736
--- /dev/null
+++ b/Assets/PlayMaker Custom Actions/StateMachine/MultiFsmEnabler.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 52e9ceaa2851ed044bd7c72ea10ff3fe
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/PlayMaker Custom Actions/Time/TimeDiffCalculator.cs b/Assets/PlayMaker Custom Actions/Time/TimeDiffCalculator.cs
new file mode 100644
index 0000000..9bfa754
--- /dev/null
+++ b/Assets/PlayMaker Custom Actions/Time/TimeDiffCalculator.cs
@@ -0,0 +1,44 @@
+using System;
+using UnityEngine;
+
+namespace HutongGames.PlayMaker.Actions
+{
+ [ActionCategory(ActionCategory.Time)]
+ [Tooltip("Compares two time values (given in String datatype in HH:mm:ss format) and gives back its difference converted into Float datatype.")]
+ public class TimeDiffCalculator : FsmStateAction
+ {
+ [Tooltip("Starting time")]
+ public FsmString StartTime;
+
+ [Tooltip("Ending time")]
+ public FsmString EndTime;
+
+ [Tooltip("The difference of ending time subtracting starting time, stored in seconds (float datatype).")]
+ public FsmFloat Result;
+
+ public override void OnEnter()
+ {
+ if (FsmString.IsNullOrEmpty(StartTime))
+ {
+ if (FsmString.IsNullOrEmpty(EndTime))
+ {
+ var start = DateTime.Parse(StartTime.ToString());
+ var end = DateTime.Parse(EndTime.ToString());
+
+ Debug.Log("start is " + start + " and end is " + end);
+
+ Result = end.Subtract(start).Seconds;
+ Debug.Log("Result is " + Result);
+ }
+ }
+ Finish();
+ }
+
+ public override void Reset()
+ {
+ StartTime = string.Empty;
+ EndTime = string.Empty;
+ Result = float.NaN;
+ }
+ }
+}
diff --git a/Assets/PlayMaker Custom Actions/Time/TimeDiffCalculator.cs.meta b/Assets/PlayMaker Custom Actions/Time/TimeDiffCalculator.cs.meta
new file mode 100644
index 0000000..5e5a6fe
--- /dev/null
+++ b/Assets/PlayMaker Custom Actions/Time/TimeDiffCalculator.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e32bc1633b6c08a4296ce4b89d26b3a8
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/PlayMaker Custom Actions/Transform/GearGameObjectsEachOther.cs b/Assets/PlayMaker Custom Actions/Transform/GearGameObjectsEachOther.cs
new file mode 100644
index 0000000..78a3684
--- /dev/null
+++ b/Assets/PlayMaker Custom Actions/Transform/GearGameObjectsEachOther.cs
@@ -0,0 +1,123 @@
+using System;
+using System.Collections;
+using UnityEngine;
+
+namespace HutongGames.PlayMaker.Actions
+{
+ /*
+ * This Action is useful if the target gameobject is looking in the Z axis direction.
+ */
+ [ActionCategory(ActionCategory.Transform)]
+ [Tooltip("Gears two gameobjects to each other, using by their 'Y' rotation. This Action is useful if the target gameobject is looking in the Z axis direction.")]
+ public class GearGameObjectsEachOther : FsmStateAction
+ {
+ private const float OneCircleConst = 360;
+ private const float RightAngleConst = 90;
+
+ private const string RightConst = "Right";
+
+ [RequiredField]
+ [UIHint(UIHint.Variable)]
+ [Tooltip("Base GameObject which 'Y' rotation's value is taken as base value.")]
+ public FsmGameObject BaseGameObject;
+
+ [RequiredField]
+ [Tooltip("Target GameObject which 'Y' rotation's value equals with base gameobject's Y rotation value.")]
+ public FsmOwnerDefault TargetGameObject;
+
+ [RequiredField]
+ [UIHint(UIHint.Variable)]
+ [Tooltip("Turning speed of the target gameobject. How much time will the target game object will before it takes the next turning portion. Negative value will be turned to its absolute value.")]
+ public FsmFloat TurningSpeed;
+
+ [RequiredField]
+ [UIHint(UIHint.Variable)]
+ [Tooltip("Turning degree of the target gameobject. How much value will be added to the previous rotation value? Negative value will be turned to its absolute value.")]
+ public FsmFloat TurningDegree;
+
+ public override void Reset()
+ {
+ BaseGameObject = null;
+ TargetGameObject = null;
+ TurningSpeed = null;
+ TurningDegree = null;
+ }
+
+ public override void OnEnter()
+ {
+ if(GearEachOther())
+ Fsm.Event(FsmEvent.Finished);
+ }
+
+ private bool GearEachOther()
+ {
+ var targetGameObjectVector = Fsm.GetOwnerDefaultTarget(TargetGameObject).gameObject.transform.eulerAngles;
+
+ var baseGameObjectVector = BaseGameObject.Value.transform.eulerAngles;
+
+ var baseGameObjectY = baseGameObjectVector.y;
+
+ if (BaseGameObject.Value.HasTag(RightConst))
+ baseGameObjectY -= OneCircleConst;
+
+ StartCoroutine(TurnTargetToBaseYRotation(NormalisedYRotation(baseGameObjectY), targetGameObjectVector));
+
+ return baseGameObjectY.Equals(targetGameObjectVector.y);
+ }
+
+ private float NormalisedYRotation(float baseY)
+ {
+ if (baseY >= OneCircleConst)
+ {
+ return baseY - (OneCircleConst * ((int) (baseY / OneCircleConst)));
+ }
+ return baseY;
+ }
+
+ private IEnumerator TurnTargetToBaseYRotation(float baseY, Vector3 target)
+ {
+ var x = target.x;
+ var z = target.z;
+
+ float targetRotationToBe;
+ float targetY = NormalisedYRotation(target.y);
+
+ switch (Math.Sign(baseY))
+ {
+ // NEGATIVE CASE
+ case -1:
+
+ targetRotationToBe = baseY - RightAngleConst;
+
+ for (float k = 0; k > targetRotationToBe; k = (k - Math.Abs(TurningDegree.Value)))
+ {
+ var y = Mathf.Abs(targetY - k) * -1;
+
+ Fsm.GetOwnerDefaultTarget(TargetGameObject).gameObject.transform.eulerAngles =
+ new Vector3(x, y, z);
+ //Quaternion.Euler(x, y, z);
+ yield return new WaitForSeconds(Math.Abs(TurningSpeed.Value));
+ }
+
+ break;
+
+ case 0:
+ break;
+
+
+ // POSITIVE CASE
+ case 1:
+ targetRotationToBe = baseY + RightAngleConst;
+
+ for (float k = 0; k < targetRotationToBe; k = (k + Math.Abs(TurningDegree.Value)))
+ {
+ Fsm.GetOwnerDefaultTarget(TargetGameObject).gameObject.transform.rotation =
+ Quaternion.Euler(x, (targetY + k), z);
+ yield return new WaitForSeconds(Math.Abs(TurningSpeed.Value));
+ }
+
+ break;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/PlayMaker Custom Actions/Transform/TurnTowardsGameObject.cs b/Assets/PlayMaker Custom Actions/Transform/TurnTowardsGameObject.cs
new file mode 100644
index 0000000..fc6e431
--- /dev/null
+++ b/Assets/PlayMaker Custom Actions/Transform/TurnTowardsGameObject.cs
@@ -0,0 +1,128 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace HutongGames.PlayMaker.Actions
+{
+ ///
+ /// Author is Istvan Nemeth.
+ ///
+ public class TurnTowardsGameObject: FsmStateAction
+ {
+ private const float HalfCircle = 180;
+ private const float WholeCircle = 360;
+
+ [RequiredField]
+ [Tooltip("The gameobject what will be rotated toward the target gameobject.")]
+ public FsmOwnerDefault Myself;
+
+ [RequiredField]
+ [UIHint(UIHint.FsmGameObject)]
+ [Tooltip(
+ "Target gameobject, where I will turn. So my Z axis will points towards the target gameobject's Z direct.")]
+ public FsmGameObject TargetGameObject;
+
+ [RequiredField]
+ [UIHint(UIHint.Variable)]
+ [Tooltip("Turning speed of the target gameobject." +
+ " How much time will the target game object will before it takes the next turning portion." +
+ " Negative value will be turned to its absolute value.")]
+ public FsmFloat TurningSpeed;
+
+ [RequiredField]
+ [UIHint(UIHint.Variable)]
+ [Tooltip(
+ "Turning degree of the target gameobject." +
+ " How much value will be added to the previous rotation value?" +
+ " Negative value will be turned to its absolute value.")]
+ public FsmFloat TurningDegree;
+
+ [UIHint(UIHint.Variable)] public FsmFloat yAngle;
+
+ private Vector3 _rotation;
+
+
+ public override void Reset()
+ {
+ TargetGameObject = null;
+ TurningSpeed = null;
+ TurningDegree = null;
+ }
+
+ public override void OnEnter()
+ {
+ var turnDegree = ChangeSign(TurningDegree.Value);
+ TurningDegree.Value = turnDegree;
+
+ var turnSpeed = ChangeSign(TurningSpeed.Value);
+ TurningSpeed.Value = turnSpeed;
+
+ var targetObject = TargetGameObject.Value;
+ var myself = Fsm.GetOwnerDefaultTarget(Myself);
+
+ if (null == targetObject)
+ return;
+
+ if (null == myself)
+ return;
+
+ if (!yAngle.IsNone)
+ StartCoroutine(TurnVehicleTowardsTarget(myself, targetObject, turnDegree, turnSpeed));
+
+ //Fsm.Event(FsmEvent.Finished);
+ }
+
+ private float ChangeSign(float toBeChanged)
+ {
+ return Math.Sign(toBeChanged) == -1 ? toBeChanged *= -1 : toBeChanged;
+ }
+ private IEnumerator TurnVehicleTowardsTarget(GameObject myself, GameObject target, float degree, float speed)
+ {
+ _rotation = myself.transform.localEulerAngles;
+
+ var x = _rotation.x;
+ var z = _rotation.z;
+ var mySignedCurrYRot = SignedCurrentYRotation(_rotation.y);
+
+ switch (Math.Sign(mySignedCurrYRot))
+ {
+ case -1:
+ for (var i = 0f; i < mySignedCurrYRot; i = (i + Math.Abs(degree)))
+ {
+ yield return SetMyAngles(myself, speed, x, i, z);
+ }
+ break;
+
+ // if mySignedCurrYRot = 120;
+ case 1:
+ for (var i = mySignedCurrYRot; i > 0; i = (i - Math.Abs(degree)))
+ {
+ yield return SetMyAngles(myself, speed, x, i, z);
+ }
+ break;
+
+ case 0: break;
+ }
+ }
+ private static object SetMyAngles(GameObject myself, float speed, float x, float y, float z)
+ {
+ myself.transform.eulerAngles = new Vector3(x, y, z);
+ return new WaitForSeconds(Math.Abs(speed));
+ }
+ private static float SignedCurrentYRotation(float y)
+ {
+ float myCurrentYRotation;
+ if (y > HalfCircle)
+ {
+ myCurrentYRotation = -(WholeCircle - y);
+ }
+ else
+ {
+ myCurrentYRotation = y;
+ }
+
+ return myCurrentYRotation;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/PlayMaker Custom Actions/Transform/TurnTowardsGameObject.cs.meta b/Assets/PlayMaker Custom Actions/Transform/TurnTowardsGameObject.cs.meta
new file mode 100644
index 0000000..282d637
--- /dev/null
+++ b/Assets/PlayMaker Custom Actions/Transform/TurnTowardsGameObject.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 19bec6a4ac264b198edfaed22539b10f
+timeCreated: 1614574461
\ No newline at end of file