using UnityEngine; using System.Collections; using System.Collections.Generic; using System.Text.RegularExpressions; namespace BehaviorDesigner.Runtime.Tactical { public class BehaviorSelection : MonoBehaviour { public GameObject agentGroup; public GameObject enemyGroup; public GameObject defendObject; public GUISkin descriptionGUISkin; private Dictionary> agentBehaviorTreeGroup = new Dictionary>(); private Dictionary> enemyBehaviorTreeGroup = new Dictionary>(); private Health[] enemyHealth; private enum BehaviorSelectionType { Attack, Charge, MarchingFire, Flank, Ambush, ShootAndScoot, Leapfrog, Surround, Defend, Hold, Retreat, Reinforcements, Last } private BehaviorSelectionType selectionType = BehaviorSelectionType.Attack; private BehaviorSelectionType prevSelectionType = BehaviorSelectionType.Attack; public void Start() { for (int i = 0; i < agentGroup.transform.childCount; ++i) { var child = agentGroup.transform.GetChild(i); var agentTrees = child.GetComponents(); for (int j = 0; j < agentTrees.Length; ++j) { var group = agentTrees[j].Group; List groupBehaviorTrees; if (!agentBehaviorTreeGroup.TryGetValue(group, out groupBehaviorTrees)) { groupBehaviorTrees = new List(); agentBehaviorTreeGroup.Add(group, groupBehaviorTrees); } groupBehaviorTrees.Add(agentTrees[j]); } } enemyHealth = enemyGroup.GetComponentsInChildren(); var behaviorTrees = enemyGroup.GetComponentsInChildren(); for (int i = 0; i < behaviorTrees.Length; ++i) { List list; if (enemyBehaviorTreeGroup.TryGetValue(behaviorTrees[i].Group, out list)) { list.Add(behaviorTrees[i]); } else { list = new List(); list.Add(behaviorTrees[i]); enemyBehaviorTreeGroup[behaviorTrees[i].Group] = list; } } SelectionChanged(); } public void OnGUI() { GUILayout.BeginVertical(GUILayout.Width(300)); GUILayout.BeginHorizontal(); if (GUILayout.Button("<-")) { prevSelectionType = selectionType; selectionType = (BehaviorSelectionType)(((int)selectionType - 1) % (int)BehaviorSelectionType.Last); if ((int)selectionType < 0) selectionType = BehaviorSelectionType.Reinforcements; SelectionChanged(); } GUILayout.Box(SplitCamelCase(selectionType.ToString()), GUILayout.Width(220)); if (GUILayout.Button("->")) { prevSelectionType = selectionType; selectionType = (BehaviorSelectionType)(((int)selectionType + 1) % (int)BehaviorSelectionType.Last); SelectionChanged(); } GUILayout.EndHorizontal(); GUILayout.Box(Description(), descriptionGUISkin.box); GUILayout.EndVertical(); } private string Description() { string desc = ""; switch (selectionType) { case BehaviorSelectionType.Attack: desc = "Moves to the closest target and starts attacking as soon as the agent is within distance."; break; case BehaviorSelectionType.Charge: desc = "Charges towards the target. The agents will start attacking when they are done charging."; break; case BehaviorSelectionType.MarchingFire: desc = "Move towards the target. The agents will start attacking when they are within distance."; break; case BehaviorSelectionType.Flank: desc = "Flanks the target from the left and right."; break; case BehaviorSelectionType.Ambush: desc = "Wait for the group of targets to pass before attacking."; break; case BehaviorSelectionType.ShootAndScoot: desc = "Attacks the target and moves position after a short amount of time."; break; case BehaviorSelectionType.Leapfrog: desc = "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"; break; case BehaviorSelectionType.Surround: desc = "Surrounds the enemy and starts to attack after all agents are in position"; break; case BehaviorSelectionType.Retreat: desc = "Retreats in the opposite direction of the target"; break; case BehaviorSelectionType.Defend: desc = "Defends the object within a defend radius. Will seek and attack a target within a specified radius"; break; case BehaviorSelectionType.Hold: desc = "Defends the object within a defend radius. Will seek and attack a target for as long as it takes"; break; case BehaviorSelectionType.Reinforcements: desc = "The attacking agent will request for reinforcements after waiting a moment. " + "The reinforcement agents will move towards the requesting agent and start attacking the targets when within distnace."; break; } return desc; } private void SelectionChanged() { StopCoroutine("EnableBehavior"); for (int i = 0; i < agentBehaviorTreeGroup[(int)prevSelectionType].Count; ++i) { agentBehaviorTreeGroup[(int)prevSelectionType][i].DisableBehavior(); } if (enemyBehaviorTreeGroup.ContainsKey((int)prevSelectionType)) { var trees = enemyBehaviorTreeGroup[(int)prevSelectionType]; for (int i = 0; i < trees.Count; ++i) { trees[i].DisableBehavior(); } } StartCoroutine("EnableBehavior"); } private static string SplitCamelCase(string s) { var r = new Regex(@"(?<=[A-Z])(?=[A-Z][a-z])|(?<=[^A-Z])(?=[A-Z])|(?<=[A-Za-z])(?=[^A-Za-z])", RegexOptions.IgnorePatternWhitespace); s = r.Replace(s, " "); return (char.ToUpper(s[0]) + s.Substring(1)).Trim(); } private IEnumerator EnableBehavior() { defendObject.SetActive(false); switch (selectionType) { case BehaviorSelectionType.Attack: case BehaviorSelectionType.Charge: case BehaviorSelectionType.MarchingFire: case BehaviorSelectionType.Flank: SetPosRot(new Vector3(-16.85f, 0, 38.9f), new Vector3(0, 0, 0), new Vector3(-16.9f, 0f, 58.75f), new Vector3(0, 180, 0), new Vector3(-12.8f, 36.75f, 43.35f)); SetChildPosRot(new Vector3[] { new Vector3(0, 0, -4.25f), new Vector3(0.55f, 0, -5.55f), new Vector3(-4.1f, 0, -3.88f), new Vector3(2.5f, 0, -5.7f), new Vector3(-3.6f, 0, -4.75f)}, new Vector3[] { new Vector3(0, 0, 0), new Vector3(0, 0, 0), new Vector3(0, 0, 0), new Vector3(0, 0, 0), new Vector3(0, 0, 0) }, new Vector3[] { new Vector3(-1.65f, 0, 0), new Vector3(0, 0, 0) }, new Vector3[] { new Vector3(0, 0, 0), new Vector3(0, 0, 0) }); break; case BehaviorSelectionType.Ambush: SetPosRot(new Vector3(33.75f, 0, -13.05f), new Vector3(0, 270, 0), new Vector3(29.9f, 0, -4.5f), new Vector3(0, 180, 0), new Vector3(30, 36.75f, -15f)); SetChildPosRot(new Vector3[] { new Vector3(0, 0, -4.25f), new Vector3(0.55f, 0, -5.55f), new Vector3(-1.75f, 0, -7.66f), new Vector3(2.5f, 0, -5.7f), new Vector3(-1.1f, 0, -5.6f) }, new Vector3[] { new Vector3(0, 0, 0), new Vector3(0, 0, 0), new Vector3(0, 0, 0), new Vector3(0, 0, 0), new Vector3(0, 0, 0) }, new Vector3[] { new Vector3(-1.65f, 0, 0), new Vector3(0, 0, 0) }, new Vector3[] { new Vector3(0, 0, 0), new Vector3(0, 0, 0) }); break; case BehaviorSelectionType.ShootAndScoot: SetPosRot(new Vector3(-20.25f, 0, -16.87f), new Vector3(0, 0, 0), new Vector3(-16.9f, 0, -4.5f), new Vector3(0, 180, 0), new Vector3(-21, 36.75f, -12.5f)); SetChildPosRot(new Vector3[] { new Vector3(0, 0, -4.25f), new Vector3(0.55f, 0, -5.55f), new Vector3(-4.1f, 0, -3.88f), new Vector3(2.5f, 0, -5.7f), new Vector3(-3.6f, 0, -4.75f)}, new Vector3[] { new Vector3(0, 0, 0), new Vector3(0, 0, 0), new Vector3(0, 0, 0), new Vector3(0, 0, 0), new Vector3(0, 0, 0) }, new Vector3[] { new Vector3(-1.65f, 0, 0), new Vector3(0, 0, 0) }, new Vector3[] { new Vector3(0, 0, 0), new Vector3(0, 0, 0) }); break; case BehaviorSelectionType.Leapfrog: SetPosRot(new Vector3(-20.25f, 0, -30.87f), new Vector3(0, 0, 0), new Vector3(-16.9f, 0, -4.5f), new Vector3(0, 180, 0), new Vector3(-21, 36.75f, -22.5f)); SetChildPosRot(new Vector3[] { new Vector3(0, 0, -4.25f), new Vector3(0.55f, 0, -5.55f), new Vector3(-4.1f, 0, -3.88f), new Vector3(2.5f, 0, -5.7f), new Vector3(-3.6f, 0, -4.75f) }, new Vector3[] { new Vector3(0, 0, 0), new Vector3(0, 0, 0), new Vector3(0, 0, 0), new Vector3(0, 0, 0), new Vector3(0, 0, 0) }, new Vector3[] { new Vector3(-1.65f, 0, 0), new Vector3(0, 0, 0) }, new Vector3[] { new Vector3(0, 0, 0), new Vector3(0, 0, 0) }); break; case BehaviorSelectionType.Surround: SetPosRot(new Vector3(-10, 0, -18.5f), new Vector3(0, 0, 0), new Vector3(-10.9f, 0, -7.25f), new Vector3(0, 180, 0), new Vector3(-10.9f, 36.75f, -7.25f)); SetChildPosRot(new Vector3[] { new Vector3(0, 0, -4.25f), new Vector3(0.55f, 0, -5.55f), new Vector3(-4.1f, 0, -3.88f), new Vector3(2.5f, 0, -5.7f), new Vector3(-3.6f, 0, -4.75f) }, new Vector3[] { new Vector3(0, 0, 0), new Vector3(0, 0, 0), new Vector3(0, 0, 0), new Vector3(0, 0, 0), new Vector3(0, 0, 0) }, new Vector3[] { new Vector3(-1.65f, 0, 0), new Vector3(0, 0, 0) }, new Vector3[] { new Vector3(0, 0, 0), new Vector3(0, 0, 0) }); break; case BehaviorSelectionType.Retreat: SetPosRot(new Vector3(-16.85f, 0, -2.7f), new Vector3(0, 0, 0), new Vector3(-16.9f, 0, -4.5f), new Vector3(0, 180, 0), new Vector3(-21, 36.75f, -22.5f)); SetChildPosRot(new Vector3[] { new Vector3(0, 0, -4.25f), new Vector3(0.55f, 0, -5.55f), new Vector3(-4.1f, 0, -3.88f), new Vector3(2.5f, 0, -5.7f), new Vector3(-3.6f, 0, -4.75f) }, new Vector3[] { new Vector3(0, 0, 0), new Vector3(0, 0, 0), new Vector3(0, 0, 0), new Vector3(0, 0, 0), new Vector3(0, 0, 0) }, new Vector3[] { new Vector3(-1.65f, 0, 0), new Vector3(0, 0, 0) }, new Vector3[] { new Vector3(0, 0, 0), new Vector3(0, 0, 0) }); break; case BehaviorSelectionType.Defend: case BehaviorSelectionType.Hold: defendObject.SetActive(true); SetPosRot(new Vector3(-16.85f, 0, 38.9f), new Vector3(0, 0, 0), new Vector3(-16.9f, 0f, 58.75f), new Vector3(0, 180, 0), new Vector3(-12.8f, 36.75f, 43.35f)); SetChildPosRot(new Vector3[] { new Vector3(-2f, 0, -4.25f), new Vector3(0.55f, 0, -5.55f), new Vector3(-4.1f, 0, -3.88f), new Vector3(2.5f, 0, -5.7f), new Vector3(-3.6f, 0, -4.75f) }, new Vector3[] { new Vector3(0, 0, 0), new Vector3(0, 0, 0), new Vector3(0, 0, 0), new Vector3(0, 0, 0), new Vector3(0, 0, 0) }, new Vector3[] { new Vector3(-8.75f, 0, 50f), new Vector3(25.1f, 0, -7.0f) }, new Vector3[] { new Vector3(0, 180, 0), new Vector3(0, 320, 0) }); break; case BehaviorSelectionType.Reinforcements: SetPosRot(new Vector3(-16.85f, 0, 38.9f), new Vector3(0, 0, 0), new Vector3(-16.9f, 0f, 58.75f), new Vector3(0, 180, 0), new Vector3(-12.8f, 36.75f, 43.35f)); SetChildPosRot(new Vector3[] { new Vector3(3, 0, 16.4f), new Vector3(0.55f, 0, -5.55f), new Vector3(-4.1f, 0, -3.88f), new Vector3(2.5f, 0, -5.7f), new Vector3(-3.6f, 0, -4.75f) }, new Vector3[] { new Vector3(0, 335, 0), new Vector3(0, 0, 0), new Vector3(0, 0, 0), new Vector3(0, 0, 0), new Vector3(0, 0, 0) }, new Vector3[] { new Vector3(-1.65f, 0, 0), new Vector3(0, 0, 0) }, new Vector3[] { new Vector3(0, 0, 0), new Vector3(0, 0, 0) }); break; } yield return new WaitForSeconds(0.1f); for (int i = 0; i < enemyHealth.Length; ++i) { enemyHealth[i].ResetHealth(); } if (enemyBehaviorTreeGroup.ContainsKey((int)selectionType)) { var trees = enemyBehaviorTreeGroup[(int)selectionType]; for (int i = 0; i < trees.Count; ++i) { trees[i].EnableBehavior(); } } for (int i = 0; i < agentBehaviorTreeGroup[(int)selectionType].Count; ++i) { agentBehaviorTreeGroup[(int)selectionType][i].EnableBehavior(); } } private void SetPosRot(Vector3 agentGroupPosition, Vector3 agentGroupRotation, Vector3 enemyGroupPosition, Vector3 enemyGroupRotation, Vector3 cameraPosition) { agentGroup.transform.position = agentGroupPosition; agentGroup.transform.eulerAngles = agentGroupRotation; enemyGroup.transform.position = enemyGroupPosition; enemyGroup.transform.eulerAngles = enemyGroupRotation; Camera.main.transform.position = cameraPosition; } private void SetChildPosRot(Vector3[] agentPositions, Vector3[] agentRotations, Vector3[] enemyPositions, Vector3[] enemyRotations) { for (int i = 0; i < agentGroup.transform.childCount; ++i) { var child = agentGroup.transform.Find("Agent " + (i + 1)); child.localPosition = agentPositions[i]; child.localEulerAngles = agentRotations[i]; } for (int i = 0; i < enemyGroup.transform.childCount; ++i) { var child = enemyGroup.transform.Find("Enemy " + (i + 1)); child.localPosition = enemyPositions[i]; child.localEulerAngles = enemyRotations[i]; } } } }