using UnityEngine; using System.Collections.Generic; using BehaviorDesigner.Runtime.Tasks; using Tooltip = BehaviorDesigner.Runtime.Tasks.TooltipAttribute; using HelpURL = BehaviorDesigner.Runtime.Tasks.HelpURLAttribute; namespace BehaviorDesigner.Runtime.Tactical.Tasks { [TaskCategory("Tactical")] [TaskDescription("Search for the target by forming two groups and leapfrogging each other. Both groups will start attacking as soon as the target is within sight")] [HelpURL("https://www.opsive.com/support/documentation/behavior-designer-tactical-pack/")] [TaskIcon("Assets/Behavior Designer Tactical/Editor/Icons/{SkinColor}LeapfrogIcon.png")] public class Leapfrog : NavMeshTacticalGroup { [Tooltip("The horizontal separation between agents within the group")] public SharedFloat separation = 2; [Tooltip("The horizontal separation between the two groups")] public SharedFloat groupSeparation = 10; [Tooltip("The distance of one leap")] public SharedFloat leapDistance = 10; private Vector3 destinationOffset; private bool inPosition; private bool canMove; private bool firstMove; private bool moveFirstGroup; private List agentReady = new List(); public override void OnAwake() { base.OnAwake(); Owner.RegisterEvent("UpdateCanMove", UpdateCanMove); Owner.RegisterEvent("UpdateReadyState", UpdateReadyState); } public override void OnStart() { base.OnStart(); moveFirstGroup = false; inPosition = false; firstMove = true; } protected override void FormationUpdated(int index) { base.FormationUpdated(index); var groupIndex = formationIndex % 2; destinationOffset.x = separation.Value * (groupIndex == 0 ? -1 : 1) * (int)(formationIndex / 2); if (groupIndex == 1) { destinationOffset.x += groupSeparation.Value; } canMove = true; } protected override void AddAgentToGroup(Behavior agent, int index) { base.AddAgentToGroup(agent, index); if (leader.Value == null) { agentReady.Add(false); } } private void UpdateCanMove(bool move) { canMove = true; } private void UpdateReadyState(int index, bool ready) { agentReady[index] = ready; var allReady = true; for (int i = 0; i < agentReady.Count; ++i) { if (!agentReady[i]) { allReady = false; break; } } // If all of the agents are ready then send the can move event to the agnets that should move. if (allReady) { moveFirstGroup = !moveFirstGroup; for (int i = 0; i < formationTrees.Count; ++i) { if ((moveFirstGroup && i % 2 == 0) || (!moveFirstGroup && i % 2 == 1)) { continue; } formationTrees[i].SendEvent("UpdateCanMove", true); agentReady[i] = false; } } } public override TaskStatus OnUpdate() { var baseStatus = base.OnUpdate(); if (baseStatus != TaskStatus.Running || !started) { return baseStatus; } // Start attacking if a target is found. if (canAttack) { // An target has been detected. All units should attack. if (MoveToAttackPosition()) { tacticalAgent.TryAttack(); } return TaskStatus.Running; } // An target has been detected. All units should attack. FindAttackTarget(); if (tacticalAgent.CanSeeTarget()) { if (leaderTree != null) { leaderTree.SendEvent("SendAttackEvent", true); } else { SendAttackEvent(true); } return TaskStatus.Running; } // No attacking unit has been found. Move into position and start leapfrogging. if (canMove) { // Move the agents into their starting position if they haven't been there already. if (!inPosition) { var leaderTransform = leader.Value != null ? leader.Value.transform : transform; tacticalAgent.SetDestination(TransformPoint(leaderTransform.position, destinationOffset, leaderTransform.rotation)); if (tacticalAgent.HasArrived()) { // The agent is in position but it may not be facing the target. if (tacticalAgent.RotateTowards(leaderTransform.rotation)) { inPosition = true; canMove = false; if (leaderTree != null) { leaderTree.SendEvent("UpdateReadyState", formationIndex, true); } else { UpdateReadyState(0, true); } } } } else { var leaderTransform = leader.Value != null ? leader.Value.transform : transform; var moveOffset = Vector3.zero; moveOffset.z = (leapDistance.Value * (formationIndex % 2 == 0 && !firstMove ? 2 : 1)); tacticalAgent.SetDestination(TransformPoint(leaderTransform.position, destinationOffset + moveOffset, leaderTransform.rotation)); canMove = false; firstMove = false; } } else { // Notify the leader when the agent has arrived. if (tacticalAgent.HasArrived()) { if (leaderTree != null) { leaderTree.SendEvent("UpdateReadyState", formationIndex, true); } else { UpdateReadyState(0, true); } } } return TaskStatus.Running; } protected override int RemoveAgentFromGroup(Behavior agent) { var index = base.RemoveAgentFromGroup(agent); if (leader.Value == null && index != -1) { agentReady.RemoveAt(index); } return index; } public override void OnBehaviorComplete() { base.OnBehaviorComplete(); Owner.UnregisterEvent("UpdateCanMove", UpdateCanMove); } public override void OnReset() { base.OnReset(); separation = 2; groupSeparation = 10; leapDistance = 10; } } }