diff --git a/Assets/Enemies/TestEnemy/Graphs/TestEnemyCombatFSM.asset b/Assets/Enemies/TestEnemy/Graphs/TestEnemyCombatFSM.asset index 12888ac..737f110 100644 --- a/Assets/Enemies/TestEnemy/Graphs/TestEnemyCombatFSM.asset +++ b/Assets/Enemies/TestEnemy/Graphs/TestEnemyCombatFSM.asset @@ -12,10 +12,13 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: f945e777233a59f4aba40aeca29093a6, type: 3} m_Name: TestEnemyCombatFSM m_EditorClassIdentifier: NodeCanvas::NodeCanvas.StateMachines.FSM - _serializedGraph: '{"type":"NodeCanvas.StateMachines.FSM","nodes":[{"_actionList":{"executionMode":1,"actions":[]},"_color":{"r":1.0,"g":0.42,"b":0.32,"a":1.0},"_name":"Circling + _serializedGraph: '{"type":"NodeCanvas.StateMachines.FSM","nodes":[{"_actionList":{"executionMode":1,"actions":[{"pauseIntervalRange":{"_value":{"x":1.0,"y":3.0}},"unpauseIntervalRange":{"_value":{"x":1.0,"y":3.0}},"speedRange":{"_value":{"x":1.0,"y":3.0}},"speedChangeIntervalRange":{"_value":{"x":1.0,"y":3.0}},"speedSmoothing":{},"strafeDirectionChangeInterval":{"_value":{"x":1.0,"y":3.0}},"directionSmoothing":{},"$type":"Reset.Units.CircleTarget"}]},"_color":{"r":1.0,"g":0.42,"b":0.32,"a":1.0},"_name":"Circling Movement","_position":{"x":589.0,"y":145.0},"$type":"NodeCanvas.StateMachines.ActionState","$id":"0"},{"_name":"Try - to Attack","_position":{"x":991.0,"y":421.0},"$type":"NodeCanvas.StateMachines.EmptyState","$id":"1"},{"_nestedBT":{"_value":1},"executionMode":0,"_variablesMap":[{"_targetSubGraphVariableID":"c96d2e2b-e09d-4abc-a287-3e6821b42c4b","_canWrite":true,"_type":"UnityEngine.Animator","_name":"bodyAnimator","_targetVariableID":"00b78a8a-fb9e-44d0-b9a8-81c6992abd41"}],"_position":{"x":1213.0,"y":503.0},"$type":"NodeCanvas.StateMachines.NestedBTState","$id":"2"},{"_onEnterList":{"executionMode":1,"actions":[]},"_onUpdateList":{"executionMode":1,"actions":[]},"_onExitList":{"executionMode":1,"actions":[]},"_color":{"r":1.0,"g":0.42,"b":0.32,"a":1.0},"_name":"Walk - To Target","_position":{"x":256.0,"y":-182.0},"$type":"NodeCanvas.StateMachines.SuperActionState","$id":"3"}],"connections":[{"_condition":{"timeout":{"_value":1.0},"$type":"NodeCanvas.Tasks.Conditions.Timeout"},"_sourceNode":{"$ref":"0"},"_targetNode":{"$ref":"1"},"$type":"NodeCanvas.StateMachines.FSMConnection"},{"_condition":{"target":{"_name":"target","_targetVariableID":"4a2889be-2a8a-4a68-ab49-fa456e2c9a19"},"maxDistance":{"_value":15.0},"layerMask":{"_value":{"value":255}},"awarnessDistance":{},"viewAngle":{"_value":90.0},"_invert":true,"$type":"NodeCanvas.Tasks.Conditions.CanSeeTarget"},"_sourceNode":{"$ref":"0"},"_targetNode":{"$ref":"3"},"$type":"NodeCanvas.StateMachines.FSMConnection"},{"_condition":{"probability":{"_value":1.0},"maxValue":{"_value":4.0},"$type":"NodeCanvas.Tasks.Conditions.Probability"},"_sourceNode":{"$ref":"1"},"_targetNode":{"$ref":"2"},"$type":"NodeCanvas.StateMachines.FSMConnection"},{"_sourceNode":{"$ref":"1"},"_targetNode":{"$ref":"0"},"$type":"NodeCanvas.StateMachines.FSMConnection"},{"_condition":{"target":{"_name":"target","_targetVariableID":"4a2889be-2a8a-4a68-ab49-fa456e2c9a19"},"maxDistance":{"_value":15.0},"layerMask":{"_value":{"value":255}},"awarnessDistance":{},"viewAngle":{"_value":90.0},"$type":"NodeCanvas.Tasks.Conditions.CanSeeTarget"},"_sourceNode":{"$ref":"3"},"_targetNode":{"$ref":"0"},"$type":"NodeCanvas.StateMachines.FSMConnection"}],"canvasGroups":[],"localBlackboard":{"_variables":{"testModelAnimator":{"_name":"testModelAnimator","_id":"7222df40-dd9e-41ce-a85a-a5221009180e","_isPublic":true,"$type":"NodeCanvas.Framework.Variable`1[[UnityEngine.Animator, + to Attack","_position":{"x":991.0,"y":421.0},"$type":"NodeCanvas.StateMachines.EmptyState","$id":"1"},{"_nestedBT":{"_value":1},"executionMode":0,"_variablesMap":[{"_targetSubGraphVariableID":"c96d2e2b-e09d-4abc-a287-3e6821b42c4b","_canWrite":true,"_type":"UnityEngine.Animator","_name":"bodyAnimator","_targetVariableID":"00b78a8a-fb9e-44d0-b9a8-81c6992abd41"}],"_position":{"x":1213.0,"y":503.0},"$type":"NodeCanvas.StateMachines.NestedBTState","$id":"2"},{"_onEnterList":{"executionMode":1,"actions":[]},"_onUpdateList":{"executionMode":1,"actions":[]},"_onExitList":{"executionMode":1,"actions":[]},"foldEnter":true,"_color":{"r":1.0,"g":0.42,"b":0.32,"a":1.0},"_name":"Walk + To Target","_position":{"x":256.0,"y":-182.0},"$type":"NodeCanvas.StateMachines.SuperActionState","$id":"3"},{"_actionList":{"executionMode":1,"actions":[]},"_color":{"r":1.0,"g":0.42,"b":0.32,"a":1.0},"_name":"Target + Too Close","_position":{"x":279.0,"y":375.0},"$type":"NodeCanvas.StateMachines.ActionState","$id":"4"},{"_conditionList":{"conditions":[]},"_actionList":{"executionMode":1,"actions":[{"facingDirection":{"label":"Facing + Direction","value":{"_value":{"$content":0,"$type":"Reset.Units.UnitFacingDirection"}},"changeValue":{"_value":1}},"rotationSpeed":{"label":"Rotation + Speed","changeValue":{},"value":{},"_changeSmoothing":{},"_smoothing":{},"_changeEasing":{},"_easing":{}},"$type":"Reset.Units.ChangeRotationSettings"}]},"_position":{"x":713.0,"y":-3.0},"$type":"NodeCanvas.StateMachines.OnFSMEnter"}],"connections":[{"_condition":{"timeout":{"_value":1.0},"$type":"NodeCanvas.Tasks.Conditions.Timeout"},"_sourceNode":{"$ref":"0"},"_targetNode":{"$ref":"1"},"$type":"NodeCanvas.StateMachines.FSMConnection"},{"_condition":{"conditions":[{"target":{"_name":"target","_targetVariableID":"4a2889be-2a8a-4a68-ab49-fa456e2c9a19"},"maxDistance":{"_value":15.0},"layerMask":{"_value":{"value":255}},"awarnessDistance":{},"viewAngle":{"_value":90.0},"_invert":true,"$type":"NodeCanvas.Tasks.Conditions.CanSeeTarget"},{"checkTarget":{"_name":"target","_targetVariableID":"4a2889be-2a8a-4a68-ab49-fa456e2c9a19"},"checkType":1,"distance":{"_value":10.0},"$type":"NodeCanvas.Tasks.Conditions.CheckDistanceToGameObject"}],"$type":"NodeCanvas.Framework.ConditionList"},"_sourceNode":{"$ref":"0"},"_targetNode":{"$ref":"3"},"$type":"NodeCanvas.StateMachines.FSMConnection"},{"_condition":{"checkTarget":{"_name":"target","_targetVariableID":"4a2889be-2a8a-4a68-ab49-fa456e2c9a19"},"distance":{"_value":2.0},"$type":"NodeCanvas.Tasks.Conditions.CheckDistanceToGameObject"},"_sourceNode":{"$ref":"0"},"_targetNode":{"$ref":"4"},"_isDisabled":true,"$type":"NodeCanvas.StateMachines.FSMConnection"},{"_condition":{"probability":{"_value":1.0},"maxValue":{"_value":4.0},"$type":"NodeCanvas.Tasks.Conditions.Probability"},"_sourceNode":{"$ref":"1"},"_targetNode":{"$ref":"2"},"$type":"NodeCanvas.StateMachines.FSMConnection"},{"_sourceNode":{"$ref":"1"},"_targetNode":{"$ref":"0"},"$type":"NodeCanvas.StateMachines.FSMConnection"},{"_condition":{"target":{"_name":"target","_targetVariableID":"4a2889be-2a8a-4a68-ab49-fa456e2c9a19"},"maxDistance":{"_value":15.0},"layerMask":{"_value":{"value":255}},"awarnessDistance":{},"viewAngle":{"_value":90.0},"$type":"NodeCanvas.Tasks.Conditions.CanSeeTarget"},"_sourceNode":{"$ref":"3"},"_targetNode":{"$ref":"0"},"$type":"NodeCanvas.StateMachines.FSMConnection"}],"canvasGroups":[],"localBlackboard":{"_variables":{"testModelAnimator":{"_name":"testModelAnimator","_id":"7222df40-dd9e-41ce-a85a-a5221009180e","_isPublic":true,"$type":"NodeCanvas.Framework.Variable`1[[UnityEngine.Animator, UnityEngine.AnimationModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]"},"bodyAnimator":{"_name":"bodyAnimator","_id":"00b78a8a-fb9e-44d0-b9a8-81c6992abd41","_isPublic":true,"$type":"NodeCanvas.Framework.Variable`1[[UnityEngine.Animator, UnityEngine.AnimationModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]"}}}}' _objectReferences: @@ -25,7 +28,7 @@ MonoBehaviour: _version: 3.31 _category: _comments: - _translation: {x: 132, y: 296} + _translation: {x: 295, y: 409} _zoomFactor: 1 _haltSerialization: 0 _externalSerializationFile: {fileID: 0} diff --git a/Assets/Scripts/Units/Graph Tasks/CircleTarget.cs b/Assets/Scripts/Units/Graph Tasks/CircleTarget.cs new file mode 100644 index 0000000..a26809c --- /dev/null +++ b/Assets/Scripts/Units/Graph Tasks/CircleTarget.cs @@ -0,0 +1,164 @@ +using System; +using NodeCanvas.Framework; +using ParadoxNotion.Design; +using Sirenix.Utilities; +using UnityEngine; +using Random = UnityEngine.Random; + + +namespace Reset.Units { + + [Category("Reset/Units")] + [Description("Gives the unit a new direction that causes them to circle the target.")] + public class CircleTarget : ActionTask{ + enum StrafeDirection{ + Forward = 0, Backwards, Left, Right + } + + // References + private IUnitTargetProvider targetProvider; + + // Pause settings + [Description("How long will this unit wait to stop moving and pause")] + public BBParameter pauseIntervalRange; + [Description("How long this unit will within paused movement")] + public BBParameter unpauseIntervalRange; + + // Speed settings + public BBParameter speedRange; + [Description("How long this unit will wait before switching movement speed")] + public BBParameter speedChangeIntervalRange; + [SliderField(0, 1)] + public BBParameter speedSmoothing; + + // Strafe direction settings + [Description("How long this unit will wait before switching strafe direction")] + public BBParameter strafeDirectionChangeInterval; + [SliderField(0, 1)] + public BBParameter directionSmoothing; + + // Pause state management + private bool isPaused; + private float lastPauseEnterTime; + private float lastPauseExitTime; + private float pauseChangeTimeElapsed; + private float nextPauseChangeTime; + + // Direct state management + private float lastDirectionChangeTime; + private float directionChangeTimeElapsed; + private StrafeDirection currentStrafeDirection; + private float nextDirectionChangeTime; + + // Speed state management + private float lastSpeedChangeTime; + private float speedChangeTimeElapsed; + private float currentSpeed; + private float nextSpeedChangeTime; + + //Use for initialization. This is called only once in the lifetime of the task. + //Return null if init was successfull. Return an error string otherwise + protected override string OnInit() { + if (pauseIntervalRange.value == Vector2.zero){ Debug.LogError($"Please set a setting for pause intervals on {agent.name}", agent.gameObject);} + if (unpauseIntervalRange.value == Vector2.zero){ Debug.LogError($"Please set a setting for unpause intervals on {agent.name}", agent.gameObject);} + if (speedRange.value == Vector2.zero){ Debug.LogError($"Please set a setting for speed range on {agent.name}", agent.gameObject);} + if (speedChangeIntervalRange.value == Vector2.zero){ Debug.LogError($"Please set a setting for speed change intervals on {agent.name}", agent.gameObject);} + if (strafeDirectionChangeInterval.value == Vector2.zero){ Debug.LogError($"Please set a setting for strafe direction change intervals on {agent.name}", agent.gameObject);} + + // Get reference to target provider + targetProvider = agent.gameObject.GetComponent(); + + return null; + } + + //This is called once each time the task is enabled. + //Call EndAction() to mark the action as finished, either in success or failure. + //EndAction can be called from anywhere. + protected override void OnExecute(){ + // Reset timers + lastPauseEnterTime = 0f; + lastPauseExitTime = 0f; + pauseChangeTimeElapsed = 0f; + lastDirectionChangeTime = 0f; + directionChangeTimeElapsed = 0f; + + // Initialize with direction + currentStrafeDirection = (StrafeDirection)Random.Range(0, 3); + } + + //Called once per frame while the action is active. + protected override void OnUpdate() { + // Set a new direction + agent.SetNewDirection(GetVector3Direction().ToVector2(), directionSmoothing.value, true); + + // Set paused or set new speed if unpaused + if (isPaused) { + agent.SetNewSpeed(0f, speedSmoothing.value, true); + } else { + agent.SetNewSpeed(currentSpeed, speedSmoothing.value, true); + } + + // Check the timers + CheckForChange(); + } + + // Method to quickly transform a strafe direction into a Vector3 + Vector3 GetVector3Direction(){ + switch (currentStrafeDirection) { + case StrafeDirection.Forward: + return agent.transform.position.DirectionTo(targetProvider.UnitTarget.transform.position); + case StrafeDirection.Backwards: + return -agent.transform.position.DirectionTo(targetProvider.UnitTarget.transform.position); + case StrafeDirection.Left: + return Quaternion.AngleAxis(-90f, Vector3.up) * agent.transform.position.DirectionTo(targetProvider.UnitTarget.transform.position); + case StrafeDirection.Right: + return Quaternion.AngleAxis(90f, Vector3.up) * agent.transform.position.DirectionTo(targetProvider.UnitTarget.transform.position); + default: + return Vector3.zero; + } + } + + void SetNewPauseChangeTime(){ + if (isPaused) { + nextPauseChangeTime = elapsedTime + Random.Range(unpauseIntervalRange.value.x, unpauseIntervalRange.value.y); + } else { + nextPauseChangeTime = elapsedTime + Random.Range(pauseIntervalRange.value.x, pauseIntervalRange.value.y); + } + } + + void SetNewSpeedChangeTime(){ + nextSpeedChangeTime = elapsedTime + Random.Range(speedChangeIntervalRange.value.x, speedChangeIntervalRange.value.y); + } + + void SetNewDirectionChangeTime(){ + nextDirectionChangeTime = elapsedTime + Random.Range(strafeDirectionChangeInterval.value.x, strafeDirectionChangeInterval.value.y); + } + + void CheckForChange(){ + if (nextPauseChangeTime > elapsedTime) { + isPaused = !isPaused; + SetNewPauseChangeTime(); + } + + if (nextSpeedChangeTime > elapsedTime) { + currentSpeed = Random.Range(speedRange.value.x, speedRange.value.y); + SetNewSpeedChangeTime(); + } + + if (nextDirectionChangeTime > elapsedTime) { + currentStrafeDirection = (StrafeDirection)Random.Range(0, 3); + SetNewDirectionChangeTime(); + } + } + + //Called when the task is disabled. + protected override void OnStop() { + + } + + //Called when the task is paused. + protected override void OnPause() { + + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Units/Graph Tasks/CircleTarget.cs.meta b/Assets/Scripts/Units/Graph Tasks/CircleTarget.cs.meta new file mode 100644 index 0000000..589b78d --- /dev/null +++ b/Assets/Scripts/Units/Graph Tasks/CircleTarget.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 9f9afac70b34c5e44b747e86f7491aa3 \ No newline at end of file