From e735d7031af61fa9b175526a993dface4e9ca5bb Mon Sep 17 00:00:00 2001 From: alexanderbianchi Date: Tue, 8 Feb 2022 13:13:20 -0500 Subject: [PATCH] first --- B2 Scripts/PrismManager.cs | 625 ++++++++++++++++++ B2 Scripts/PrismManager.cs.meta | 11 + B2 Scripts/Prisms.meta | 8 + B2 Scripts/Prisms/IrregularPrism.cs | 18 + B2 Scripts/Prisms/IrregularPrism.cs.meta | 11 + B2 Scripts/Prisms/Prism.cs | 12 + B2 Scripts/Prisms/Prism.cs.meta | 11 + B2 Scripts/Prisms/RectPrism.cs | 26 + B2 Scripts/Prisms/RectPrism.cs.meta | 11 + B2 Scripts/Prisms/RegularPrism.cs | 19 + B2 Scripts/Prisms/RegularPrism.cs.meta | 11 + B2 Scripts/Prisms/TriPrism.cs | 26 + B2 Scripts/Prisms/TriPrism.cs.meta | 11 + B3 Scripts/AgentController.cs | 182 +++++ B3 Scripts/AgentController.cs.meta | 11 + B3 Scripts/AgentManager.cs | 95 +++ B3 Scripts/AgentManager.cs.meta | 11 + B3 Scripts/CameraController.cs | 32 + B3 Scripts/CameraController.cs.meta | 11 + B3 Scripts/Curves.meta | 9 + B3 Scripts/Curves/BezierSpline.cs | 290 ++++++++ B3 Scripts/Curves/BezierSpline.cs.meta | 12 + B3 Scripts/Curves/CatmullRomSpline.cs | 144 ++++ B3 Scripts/Curves/CatmullRomSpline.cs.meta | 12 + B3 Scripts/Curves/CubicBezierCurve.cs | 49 ++ B3 Scripts/Curves/CubicBezierCurve.cs.meta | 12 + B3 Scripts/Curves/CurveAnimator.meta | 9 + .../Curves/CurveAnimator/CurveAnimator.cs | 37 ++ .../CurveAnimator/CurveAnimator.cs.meta | 12 + B3 Scripts/Curves/ICurve.cs | 14 + B3 Scripts/Curves/ICurve.cs.meta | 12 + B3 Scripts/Curves/Line.cs | 17 + B3 Scripts/Curves/Line.cs.meta | 12 + B3 Scripts/Curves/QuadBezierCurve.cs | 87 +++ B3 Scripts/Curves/QuadBezierCurve.cs.meta | 12 + B3 Scripts/Curves/nwlwgsh3.dgz~ | 266 ++++++++ B3 Scripts/IsoCameraController.cs | 48 ++ B3 Scripts/IsoCameraController.cs.meta | 11 + B4 Scripts/Agent.cs | 340 ++++++++++ B4 Scripts/Agent.cs.meta | 11 + B4 Scripts/AgentManager.cs | 186 ++++++ B4 Scripts/AgentManager.cs.meta | 11 + B4 Scripts/CameraController.cs | 31 + B4 Scripts/CameraController.cs.meta | 11 + B4 Scripts/EvadeAgent.cs | 254 +++++++ B4 Scripts/EvadeAgent.cs.meta | 11 + B4 Scripts/FollowerAgent.cs | 216 ++++++ B4 Scripts/FollowerAgent.cs.meta | 11 + B4 Scripts/LFAgentManager.cs | 188 ++++++ B4 Scripts/LFAgentManager.cs.meta | 11 + B4 Scripts/LeaderAgent.cs | 219 ++++++ B4 Scripts/LeaderAgent.cs.meta | 11 + B4 Scripts/Parameters.cs | 19 + B4 Scripts/Parameters.cs.meta | 11 + B4 Scripts/PursueAgent.cs | 227 +++++++ B4 Scripts/PursueAgent.cs.meta | 11 + B4 Scripts/SlidingFrictionForceVisualizer.cs | 41 ++ .../SlidingFrictionForceVisualizer.cs.meta | 11 + B4 Scripts/WallManager.cs | 78 +++ B4 Scripts/WallManager.cs.meta | 11 + B5 Scripts/CameraController.cs | 31 + B5 Scripts/CameraController.cs.meta | 11 + B5 Scripts/PlayerController.cs | 25 + B5 Scripts/PlayerController.cs.meta | 11 + B5 Scripts/SequenceEvery.cs | 78 +++ B5 Scripts/SequenceEvery.cs.meta | 11 + B6 Scripts/Agent.cs | 419 ++++++++++++ B6 Scripts/Agent.cs.meta | 11 + B6 Scripts/AgentManager.cs | 249 +++++++ B6 Scripts/AgentManager.cs.meta | 11 + B6 Scripts/AudioManager.cs | 47 ++ B6 Scripts/AudioManager.cs.meta | 11 + B6 Scripts/CameraController.cs | 63 ++ B6 Scripts/CameraController.cs.meta | 11 + B6 Scripts/EvadeAgent.cs | 254 +++++++ B6 Scripts/EvadeAgent.cs.meta | 11 + B6 Scripts/FollowerAgent.cs | 216 ++++++ B6 Scripts/FollowerAgent.cs.meta | 11 + B6 Scripts/LFAgentManager.cs | 188 ++++++ B6 Scripts/LFAgentManager.cs.meta | 11 + B6 Scripts/LeaderAgent.cs | 219 ++++++ B6 Scripts/LeaderAgent.cs.meta | 11 + B6 Scripts/Parameters.cs | 19 + B6 Scripts/Parameters.cs.meta | 11 + B6 Scripts/PointsManager.cs | 55 ++ B6 Scripts/PointsManager.cs.meta | 11 + B6 Scripts/PursueAgent.cs | 227 +++++++ B6 Scripts/PursueAgent.cs.meta | 11 + B6 Scripts/RTSAgentManager.cs | 304 +++++++++ B6 Scripts/RTSAgentManager.cs.meta | 11 + B6 Scripts/RTSEAgentManager.cs | 323 +++++++++ B6 Scripts/RTSEAgentManager.cs.meta | 11 + B6 Scripts/SlidingFrictionForceVisualizer.cs | 41 ++ .../SlidingFrictionForceVisualizer.cs.meta | 11 + B6 Scripts/Sound.cs | 19 + B6 Scripts/Sound.cs.meta | 11 + B6 Scripts/WallManager.cs | 26 + B6 Scripts/WallManager.cs.meta | 11 + B6 Scripts/cPointBehavior.cs | 78 +++ B6 Scripts/cPointBehavior.cs.meta | 11 + 100 files changed, 7228 insertions(+) create mode 100644 B2 Scripts/PrismManager.cs create mode 100644 B2 Scripts/PrismManager.cs.meta create mode 100644 B2 Scripts/Prisms.meta create mode 100644 B2 Scripts/Prisms/IrregularPrism.cs create mode 100644 B2 Scripts/Prisms/IrregularPrism.cs.meta create mode 100644 B2 Scripts/Prisms/Prism.cs create mode 100644 B2 Scripts/Prisms/Prism.cs.meta create mode 100644 B2 Scripts/Prisms/RectPrism.cs create mode 100644 B2 Scripts/Prisms/RectPrism.cs.meta create mode 100644 B2 Scripts/Prisms/RegularPrism.cs create mode 100644 B2 Scripts/Prisms/RegularPrism.cs.meta create mode 100644 B2 Scripts/Prisms/TriPrism.cs create mode 100644 B2 Scripts/Prisms/TriPrism.cs.meta create mode 100644 B3 Scripts/AgentController.cs create mode 100644 B3 Scripts/AgentController.cs.meta create mode 100644 B3 Scripts/AgentManager.cs create mode 100644 B3 Scripts/AgentManager.cs.meta create mode 100644 B3 Scripts/CameraController.cs create mode 100644 B3 Scripts/CameraController.cs.meta create mode 100644 B3 Scripts/Curves.meta create mode 100644 B3 Scripts/Curves/BezierSpline.cs create mode 100644 B3 Scripts/Curves/BezierSpline.cs.meta create mode 100644 B3 Scripts/Curves/CatmullRomSpline.cs create mode 100644 B3 Scripts/Curves/CatmullRomSpline.cs.meta create mode 100644 B3 Scripts/Curves/CubicBezierCurve.cs create mode 100644 B3 Scripts/Curves/CubicBezierCurve.cs.meta create mode 100644 B3 Scripts/Curves/CurveAnimator.meta create mode 100644 B3 Scripts/Curves/CurveAnimator/CurveAnimator.cs create mode 100644 B3 Scripts/Curves/CurveAnimator/CurveAnimator.cs.meta create mode 100644 B3 Scripts/Curves/ICurve.cs create mode 100644 B3 Scripts/Curves/ICurve.cs.meta create mode 100644 B3 Scripts/Curves/Line.cs create mode 100644 B3 Scripts/Curves/Line.cs.meta create mode 100644 B3 Scripts/Curves/QuadBezierCurve.cs create mode 100644 B3 Scripts/Curves/QuadBezierCurve.cs.meta create mode 100644 B3 Scripts/Curves/nwlwgsh3.dgz~ create mode 100644 B3 Scripts/IsoCameraController.cs create mode 100644 B3 Scripts/IsoCameraController.cs.meta create mode 100644 B4 Scripts/Agent.cs create mode 100644 B4 Scripts/Agent.cs.meta create mode 100644 B4 Scripts/AgentManager.cs create mode 100644 B4 Scripts/AgentManager.cs.meta create mode 100644 B4 Scripts/CameraController.cs create mode 100644 B4 Scripts/CameraController.cs.meta create mode 100644 B4 Scripts/EvadeAgent.cs create mode 100644 B4 Scripts/EvadeAgent.cs.meta create mode 100644 B4 Scripts/FollowerAgent.cs create mode 100644 B4 Scripts/FollowerAgent.cs.meta create mode 100644 B4 Scripts/LFAgentManager.cs create mode 100644 B4 Scripts/LFAgentManager.cs.meta create mode 100644 B4 Scripts/LeaderAgent.cs create mode 100644 B4 Scripts/LeaderAgent.cs.meta create mode 100644 B4 Scripts/Parameters.cs create mode 100644 B4 Scripts/Parameters.cs.meta create mode 100644 B4 Scripts/PursueAgent.cs create mode 100644 B4 Scripts/PursueAgent.cs.meta create mode 100644 B4 Scripts/SlidingFrictionForceVisualizer.cs create mode 100644 B4 Scripts/SlidingFrictionForceVisualizer.cs.meta create mode 100644 B4 Scripts/WallManager.cs create mode 100644 B4 Scripts/WallManager.cs.meta create mode 100644 B5 Scripts/CameraController.cs create mode 100644 B5 Scripts/CameraController.cs.meta create mode 100644 B5 Scripts/PlayerController.cs create mode 100644 B5 Scripts/PlayerController.cs.meta create mode 100644 B5 Scripts/SequenceEvery.cs create mode 100644 B5 Scripts/SequenceEvery.cs.meta create mode 100644 B6 Scripts/Agent.cs create mode 100644 B6 Scripts/Agent.cs.meta create mode 100644 B6 Scripts/AgentManager.cs create mode 100644 B6 Scripts/AgentManager.cs.meta create mode 100644 B6 Scripts/AudioManager.cs create mode 100644 B6 Scripts/AudioManager.cs.meta create mode 100644 B6 Scripts/CameraController.cs create mode 100644 B6 Scripts/CameraController.cs.meta create mode 100644 B6 Scripts/EvadeAgent.cs create mode 100644 B6 Scripts/EvadeAgent.cs.meta create mode 100644 B6 Scripts/FollowerAgent.cs create mode 100644 B6 Scripts/FollowerAgent.cs.meta create mode 100644 B6 Scripts/LFAgentManager.cs create mode 100644 B6 Scripts/LFAgentManager.cs.meta create mode 100644 B6 Scripts/LeaderAgent.cs create mode 100644 B6 Scripts/LeaderAgent.cs.meta create mode 100644 B6 Scripts/Parameters.cs create mode 100644 B6 Scripts/Parameters.cs.meta create mode 100644 B6 Scripts/PointsManager.cs create mode 100644 B6 Scripts/PointsManager.cs.meta create mode 100644 B6 Scripts/PursueAgent.cs create mode 100644 B6 Scripts/PursueAgent.cs.meta create mode 100644 B6 Scripts/RTSAgentManager.cs create mode 100644 B6 Scripts/RTSAgentManager.cs.meta create mode 100644 B6 Scripts/RTSEAgentManager.cs create mode 100644 B6 Scripts/RTSEAgentManager.cs.meta create mode 100644 B6 Scripts/SlidingFrictionForceVisualizer.cs create mode 100644 B6 Scripts/SlidingFrictionForceVisualizer.cs.meta create mode 100644 B6 Scripts/Sound.cs create mode 100644 B6 Scripts/Sound.cs.meta create mode 100644 B6 Scripts/WallManager.cs create mode 100644 B6 Scripts/WallManager.cs.meta create mode 100644 B6 Scripts/cPointBehavior.cs create mode 100644 B6 Scripts/cPointBehavior.cs.meta diff --git a/B2 Scripts/PrismManager.cs b/B2 Scripts/PrismManager.cs new file mode 100644 index 0000000..8c4dba3 --- /dev/null +++ b/B2 Scripts/PrismManager.cs @@ -0,0 +1,625 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +public class PrismManager : MonoBehaviour +{ + public int prismCount = 10; + public float prismRegionRadiusXZ = 5; + public float prismRegionRadiusY = 5; + public float maxPrismScaleXZ = 5; + public float maxPrismScaleY = 5; + public GameObject regularPrismPrefab; + public GameObject irregularPrismPrefab; + + private List prisms = new List(); + private List prismObjects = new List(); + private GameObject prismParent; + private Dictionary prismColliding = new Dictionary(); + + private const float UPDATE_RATE = 0.5f; + + #region Unity Functions + + void Start() + { + Random.InitState(0); //10 for no collision + + prismParent = GameObject.Find("Prisms"); + for (int i = 0; i < prismCount; i++) + { + var randPointCount = Mathf.RoundToInt(3 + Random.value * 7); + var randYRot = Random.value * 360; + var randScale = new Vector3((Random.value - 0.5f) * 2 * maxPrismScaleXZ, (Random.value - 0.5f) * 2 * maxPrismScaleY, (Random.value - 0.5f) * 2 * maxPrismScaleXZ); + var randPos = new Vector3((Random.value - 0.5f) * 2 * prismRegionRadiusXZ, (Random.value - 0.5f) * 2 * prismRegionRadiusY, (Random.value - 0.5f) * 2 * prismRegionRadiusXZ); + + GameObject prism = null; + Prism prismScript = null; + if (Random.value < 0.5f) + { + prism = Instantiate(regularPrismPrefab, randPos, Quaternion.Euler(0, randYRot, 0)); + prismScript = prism.GetComponent(); + } + else + { + prism = Instantiate(irregularPrismPrefab, randPos, Quaternion.Euler(0, randYRot, 0)); + prismScript = prism.GetComponent(); + } + prism.name = "Prism " + i; + prism.transform.localScale = randScale; + prism.transform.parent = prismParent.transform; + prismScript.pointCount = randPointCount; + prismScript.prismObject = prism; + + prisms.Add(prismScript); + prismObjects.Add(prism); + prismColliding.Add(prismScript, false); + } + + StartCoroutine(Run()); + } + + void Update() + { + #region Visualization + + DrawPrismRegion(); + DrawPrismWireFrames(); + +#if UNITY_EDITOR + if (Application.isFocused) + { + UnityEditor.SceneView.FocusWindowIfItsOpen(typeof(UnityEditor.SceneView)); + } +#endif + + #endregion + } + + IEnumerator Run() + { + yield return null; + + while (true) + { + foreach (var prism in prisms) + { + prismColliding[prism] = false; + } + + foreach (var collision in PotentialCollisions()) + { + if (CheckCollision(collision)) + { + prismColliding[collision.a] = true; + prismColliding[collision.b] = true; + + ResolveCollision(collision); + } + } + + yield return new WaitForSeconds(UPDATE_RATE); + } + } + + #endregion + + #region Incomplete Functions + private class Point + { + public float x; + public float y; + } + private class AABB + { + public Point min; + public Point max; + public Prism PrismObject; + } + + private class Pair + { + public AABB bbox; + public float key; + } + + private class minHeap + { + public Pair[] contained; + public int sizeHeap; + private int alloced; + public minHeap() + { + sizeHeap = 0; + alloced = 20; + contained = new Pair[alloced]; + } + public Pair removeMin() + { + if (sizeHeap <= 0) + { + return null; + } + if (sizeHeap == 1) + { + sizeHeap--; + return contained[0]; + } + + Pair root = contained[0]; + contained[0] = contained[sizeHeap - 1]; + sizeHeap--; + heapify(0); + return root; + } + + public Pair getMin() + { + return contained[0]; + } + + private void heapify(int i) + { + int l = left(i); + int r = right(i); + int smallest = i; + if (l < sizeHeap && contained[l].key < contained[i].key) + { + smallest = l; + } + if (r < sizeHeap && contained[r].key < contained[i].key) + { + smallest = r; + } + if (smallest != i) + { + swap(i, smallest); + heapify(smallest); + } + } + private void swap(int x, int y) + { + Pair temp = contained[x]; + contained[x] = contained[y]; + contained[y] = temp; + } + + private int left(int i) + { + return i * 2 + 1; + } + private int right(int i) + { + return i * 2 + 2; + } + + private void expand() + { + alloced = 2 * alloced; + Pair[] temp = new Pair[alloced]; + for (int i = 0; i < contained.Length; i++) + { + temp[i] = contained[i]; + } + contained = temp; + + } + + public void addPair(AABB addition, float key) + { + + sizeHeap++; + if (sizeHeap >= alloced) + { + expand(); + } + Pair entry = new Pair(); + entry.bbox = addition; + entry.key = key; + contained[sizeHeap - 1] = entry; + int val = sizeHeap - 1; + while (val != 0 && entry.key < contained[val / 2].key) + { + swap(val, val / 2); + val = val / 2; + } + } + + } + + private IEnumerable PotentialCollisions() + { + //Create list of bounding boxes (might be better to make the min/max points inherit to the Prism class) + + SortedList boundingBoxes = new SortedList(); + for (int i = 0; i < prisms.Count; i++) + { + Point Max = new Point + { + //Also better to run the GetComponent at the creation of a prism class so it doesnt have to be re-run every frame + x = prisms[i].GetComponent().bounds.max.x, + y = prisms[i].GetComponent().bounds.max.y, + + }; + Point Min = new Point + { + x = prisms[i].GetComponent().bounds.min.x, + y = prisms[i].GetComponent().bounds.min.y, + }; + AABB boundingBox = new AABB { max = Max, min = Min, PrismObject = prisms[i] }; + boundingBoxes.Add(Min.x, boundingBox); + } + + // Initialize Stack TODO: Create MinHeap Stack + minHeap currentCollisions = new minHeap(); + + // Initialize all Potential collisions + List collisions = new List(); + + // Loop through the sorted Bounding Boxes + foreach (KeyValuePair kvp in boundingBoxes) + { + // Using a sorted stack to keep track of potentially intersecting objects + + /* Remove each object from the stack that ends befor the currend bounding box starts + while (currentCollisions.sizeHeap > 0 && kvp.Value.min.x > currentCollisions.getMin().key) + { + currentCollisions.removeMin(); + } + */ + // For each object in the stack, add it as a potential colision + for (int i = 0; i < currentCollisions.sizeHeap; i++) + { + Pair collision = currentCollisions.contained[i]; + + collisions.Add(new PrismCollision + { + a = kvp.Value.PrismObject, + b = collision.bbox.PrismObject, + }); + } + + // Add current bounding box to the stack for future bounding boxes to check against + currentCollisions.addPair(kvp.Value, kvp.Value.max.x); + } + + return collisions; + } + + /* + This function will get the farthest point in the Prism x that is in direction d. + The function loops through each point in Prism X and calculates the dot product + against direction. If the point is the largest it saves that point and returns it. + */ + private Vector3 GetFarthestPoint(Prism x, Vector3 d) + { + float largestDistance = -1000000000000f; + Vector3 farthestPoint = Vector3.zero; + foreach (Vector3 point in x.points) + { + float newDistance = Vector3.Dot(point, d); + if (newDistance > largestDistance) + { + largestDistance = newDistance; + farthestPoint = point; + } + } + return farthestPoint; + } + /* + The support function gets the farthest point in both directions d for each Prism + and returns the difference between them. + */ + private Vector3 support(Prism a, Prism b, Vector3 d) + { + Vector3 point1 = GetFarthestPoint(a, d); + Vector3 point2 = GetFarthestPoint(b, -d); + return (point1 - point2); + } + + /* + Checks different edges in 3D Triangle to see if the edges enclose the origin + or if not change direction. + */ + private bool Check3DTriangle(ref List simplex, ref Vector3 direction){ + Vector3 a = simplex[3]; + Vector3 ao = -a; + Vector3 b = simplex[2]; + Vector3 c = simplex[1]; + Vector3 d = simplex[0]; + Vector3 ab = b - a; + Vector3 ac = c - a; + Vector3 ad = d - a; + + Vector3 abc = Vector3.Cross(ab, ac); + Vector3 acd = Vector3.Cross(ac, ad); + Vector3 adb = Vector3.Cross(ad, ab); + + if(Vector3.Dot(abc, ao) > 0){ + simplex.RemoveAt(0); + return CheckTriangle(ref simplex, ref direction); + } + if(Vector3.Dot(acd, ao) > 0){ + simplex.RemoveAt(2); + return CheckTriangle(ref simplex, ref direction); + } + if(Vector3.Dot(adb, ao) > 0){ + simplex.RemoveAt(1); + simplex[1] = d; + simplex[0] = b; + return CheckTriangle(ref simplex, ref direction); + } + + return true; + } + + /* + Checks to see if the edges ab and ac enclose the origin. + Depending on direction towards origin changes direction or returns true if + the edges do enclose origin. + */ + private bool CheckTriangle(ref List simplex, ref Vector3 d){ + Vector3 a = simplex[0]; + Vector3 b = simplex[1]; + Vector3 c = simplex[2]; + Vector3 ao = -a; + Vector3 ab = b - a; + Vector3 ac = c - a; + Vector3 abc = Vector3.Cross(ab,ac); + + if (Vector3.Dot(Vector3.Cross(abc,ac), ao) > 0) + { + if(Vector3.Dot(ac, ao) > 0){ + simplex.RemoveAt(1); + d = Vector3.Cross(Vector3.Cross(ac,ao),ac).normalized; + }else{ + simplex.RemoveAt(2); + return CheckLine(ref simplex, ref d); + } + } + else + { + if (Vector3.Dot(Vector3.Cross(ab,abc), ao) > 0) + { + simplex.RemoveAt(2); + return CheckLine(ref simplex, ref d); + } + else + { + /* + //Doesn't work but would be for 3D GJK. + if(Vector3.Dot(abc,ao) > 0){ + d = abc; + }else{ + simplex[1] = c; + simplex[2] = b; + d = -abc; + }*/ + return true; + } + } + return false; + } + + /* + If there are only two points in the simplex, will change the direction + depending on if dot product of edge AB is towards the origin. + If not towards origin will remove point B and go in opposite direction to a. + */ + private bool CheckLine(ref List simplex, ref Vector3 d){ + Vector3 a = simplex[0]; + Vector3 ao = -a; + Vector3 b = simplex[1]; + Vector3 ab = b - a; + if(Vector3.Dot(ab, ao) > 0){ + d = Vector3.Cross(Vector3.Cross(ab, ao), ab).normalized; + }else{ + simplex.RemoveAt(1); + d = ao.normalized; + } + + return false; + } + + /* + The function checks to see if the simplex that was created is over the origin space. + It checks if the Simplex is a Line or Triangle or 3D Triangle by number of points in simplex. + If it is a triangle, it checks if the origin point is within the Triangle. + If not than it removes a point from the Simplex and changes the direction. + If it is a Line, it finds a new direction for d + */ + private bool IsOrigin(ref List simplex, ref Vector3 d) + { + if(simplex.Count >= 4){ + return Check3DTriangle(ref simplex, ref d); + } + if (simplex.Count == 3) + { + return CheckTriangle(ref simplex, ref d); + } + if(simplex.Count == 2) + { + return CheckLine(ref simplex, ref d); + } + if(simplex.Count == 1){ + d = -simplex[0].normalized; + } + return false; + } + + /* + The function finds the closest edge from the origin and returns the depth penetration vector, + distance and position. + */ + private void ClosestEdge(ref float distance, ref Vector3 normal, ref int pos, List Simplex){ + distance = Mathf.Infinity; + int i = 0; + foreach(Vector3 a in Simplex){ + int j = i + 1; + i++; + if(j >= Simplex.Count) + j = 0; + + Vector3 b = Simplex[j]; + Vector3 e = b - a; + Vector3 newNormal = Vector3.Cross(Vector3.Cross(e, a), e).normalized; + float newDistance = Vector3.Dot(newNormal, a); + if(newDistance < distance){ + distance = newDistance; + normal = newNormal; + pos = j; + } + } + } + + /* + Function will run the GJK Algorithm between two Prisms A & B. + If the Prisms are colliding, it will return true else false. + If the Prisms are colliding, it will also run the EPA algorithm, + and store the penetration depth vector. + */ + private bool CheckCollision(PrismCollision collision) + { + var prismA = collision.a; + var prismB = collision.b; + + bool isCollision = false; + List simplex = new List(); + Vector3 direction = new Vector3(1, 0, 0); + Vector3 supportVec = support(prismA, prismB, direction); + simplex.Add(supportVec); + direction = -supportVec; + + while (true) + { + supportVec = support(prismA, prismB, direction); + if (Vector3.Dot(supportVec, direction) <= 0) + { + return false; + } + simplex.Add(supportVec); + if(IsOrigin(ref simplex, ref direction)){ + isCollision = true; + break; + } + } + + float distance = 0; + Vector3 normal = Vector3.zero; + int pos = 0; + while(true){ + ClosestEdge(ref distance, ref normal, ref pos, simplex); + Vector3 p = support(prismA, prismB, normal); + float d = Vector3.Dot(p,normal); + if(d - distance < 0.000001){ + collision.penetrationDepthVectorAB = normal; + break; + }else{ + simplex.Insert(pos, p); + } + } + + return isCollision; + } + + #endregion + + #region Private Functions + + private void ResolveCollision(PrismCollision collision) + { + var prismObjA = collision.a.prismObject; + var prismObjB = collision.b.prismObject; + + var pushA = -collision.penetrationDepthVectorAB / 2; + var pushB = collision.penetrationDepthVectorAB / 2; + + for (int i = 0; i < collision.a.pointCount; i++) + { + collision.a.points[i] += pushA; + } + for (int i = 0; i < collision.b.pointCount; i++) + { + collision.b.points[i] += pushB; + } + //prismObjA.transform.position += pushA; + //prismObjB.transform.position += pushB; + + Debug.DrawLine(prismObjA.transform.position, prismObjA.transform.position + collision.penetrationDepthVectorAB, Color.cyan, UPDATE_RATE); + } + + #endregion + + #region Visualization Functions + + private void DrawPrismRegion() + { + var points = new Vector3[] { new Vector3(1, 0, 1), new Vector3(1, 0, -1), new Vector3(-1, 0, -1), new Vector3(-1, 0, 1) }.Select(p => p * prismRegionRadiusXZ).ToArray(); + + var yMin = -prismRegionRadiusY; + var yMax = prismRegionRadiusY; + + var wireFrameColor = Color.yellow; + + foreach (var point in points) + { + Debug.DrawLine(point + Vector3.up * yMin, point + Vector3.up * yMax, wireFrameColor); + } + + for (int i = 0; i < points.Length; i++) + { + Debug.DrawLine(points[i] + Vector3.up * yMin, points[(i + 1) % points.Length] + Vector3.up * yMin, wireFrameColor); + Debug.DrawLine(points[i] + Vector3.up * yMax, points[(i + 1) % points.Length] + Vector3.up * yMax, wireFrameColor); + } + } + + private void DrawPrismWireFrames() + { + for (int prismIndex = 0; prismIndex < prisms.Count; prismIndex++) + { + var prism = prisms[prismIndex]; + var prismTransform = prismObjects[prismIndex].transform; + + var yMin = prism.midY - prism.height / 2 * prismTransform.localScale.y; + var yMax = prism.midY + prism.height / 2 * prismTransform.localScale.y; + + var wireFrameColor = prismColliding[prisms[prismIndex]] ? Color.red : Color.green; + + foreach (var point in prism.points) + { + Debug.DrawLine(point + Vector3.up * yMin, point + Vector3.up * yMax, wireFrameColor); + } + + for (int i = 0; i < prism.pointCount; i++) + { + Debug.DrawLine(prism.points[i] + Vector3.up * yMin, prism.points[(i + 1) % prism.pointCount] + Vector3.up * yMin, wireFrameColor); + Debug.DrawLine(prism.points[i] + Vector3.up * yMax, prism.points[(i + 1) % prism.pointCount] + Vector3.up * yMax, wireFrameColor); + } + } + } + + #endregion + + #region Utility Classes + + private class PrismCollision + { + public Prism a; + public Prism b; + public Vector3 penetrationDepthVectorAB; + } + + private class Tuple + { + public K Item1; + public V Item2; + + public Tuple(K k, V v) + { + Item1 = k; + Item2 = v; + } + } + + #endregion +} diff --git a/B2 Scripts/PrismManager.cs.meta b/B2 Scripts/PrismManager.cs.meta new file mode 100644 index 0000000..fb478cc --- /dev/null +++ b/B2 Scripts/PrismManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 27f617c0ea9733b44b50c37923d5c7d4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B2 Scripts/Prisms.meta b/B2 Scripts/Prisms.meta new file mode 100644 index 0000000..d219c78 --- /dev/null +++ b/B2 Scripts/Prisms.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 13b0e323d611bdc429ba7736ab7fdf8d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B2 Scripts/Prisms/IrregularPrism.cs b/B2 Scripts/Prisms/IrregularPrism.cs new file mode 100644 index 0000000..df6c9d0 --- /dev/null +++ b/B2 Scripts/Prisms/IrregularPrism.cs @@ -0,0 +1,18 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +public class IrregularPrism : Prism +{ + + void Start() + { + pointCount = Mathf.Max(pointCount, 3); + points = Enumerable.Range(0, pointCount).Select(i => Random.value).OrderBy(i => i).Select(theta => Quaternion.AngleAxis(360f * theta, Vector3.up) * Vector3.forward * 0.5f).ToArray(); + points = points.Select(p => transform.position + Quaternion.AngleAxis(transform.eulerAngles.y, Vector3.up) * new Vector3(p.x * transform.localScale.x, 0, p.z * transform.localScale.z)).ToArray(); + midY = 0; + height = 2; + } + +} diff --git a/B2 Scripts/Prisms/IrregularPrism.cs.meta b/B2 Scripts/Prisms/IrregularPrism.cs.meta new file mode 100644 index 0000000..c37479d --- /dev/null +++ b/B2 Scripts/Prisms/IrregularPrism.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fcad85690c4db29478f29b09ee4018eb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B2 Scripts/Prisms/Prism.cs b/B2 Scripts/Prisms/Prism.cs new file mode 100644 index 0000000..d6fd358 --- /dev/null +++ b/B2 Scripts/Prisms/Prism.cs @@ -0,0 +1,12 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class Prism : MonoBehaviour +{ + public int pointCount = 3; + public Vector3[] points; + public float midY, height; + + public GameObject prismObject; +} diff --git a/B2 Scripts/Prisms/Prism.cs.meta b/B2 Scripts/Prisms/Prism.cs.meta new file mode 100644 index 0000000..b27db57 --- /dev/null +++ b/B2 Scripts/Prisms/Prism.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b194720c872ae014aabc4ffeccce6337 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B2 Scripts/Prisms/RectPrism.cs b/B2 Scripts/Prisms/RectPrism.cs new file mode 100644 index 0000000..612e29e --- /dev/null +++ b/B2 Scripts/Prisms/RectPrism.cs @@ -0,0 +1,26 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +public class RectPrism : Prism +{ + + void Start() + { + points = new Vector3[] { new Vector3(0.5f, 0, 0.5f), new Vector3(0.5f, 0, -0.5f), new Vector3(-0.5f, 0, -0.5f), new Vector3(-0.5f, 0, 0.5f) }; + midY = 0; + height = 1; + } + + void Update() + { + var yMin = midY - height / 2 * transform.localScale.y; + var yMax = midY + height / 2 * transform.localScale.y; + foreach (var pt in points.Select(p => new Vector3(p.x * transform.localScale.x, 0, p.z * transform.localScale.z))) + { + var point = transform.position + Quaternion.AngleAxis(transform.eulerAngles.y, Vector3.up) * pt; + Debug.DrawLine(point + Vector3.up * yMin, point + Vector3.up * yMax, Color.red); + } + } +} diff --git a/B2 Scripts/Prisms/RectPrism.cs.meta b/B2 Scripts/Prisms/RectPrism.cs.meta new file mode 100644 index 0000000..17911b0 --- /dev/null +++ b/B2 Scripts/Prisms/RectPrism.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cfeac4f90ede63d4ab80604f102d3569 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B2 Scripts/Prisms/RegularPrism.cs b/B2 Scripts/Prisms/RegularPrism.cs new file mode 100644 index 0000000..fe49fe9 --- /dev/null +++ b/B2 Scripts/Prisms/RegularPrism.cs @@ -0,0 +1,19 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +public class RegularPrism : Prism +{ + public bool forwardAxisPerpToFace = true; + + void Start() + { + pointCount = Mathf.Max(pointCount, 3); + points = Enumerable.Range(0, pointCount).Select(i => Quaternion.AngleAxis(360f / pointCount * (i + (forwardAxisPerpToFace ? 0.5f : 0)), Vector3.up) * Vector3.forward * 0.5f).ToArray(); + points = points.Select(p => transform.position + Quaternion.AngleAxis(transform.eulerAngles.y, Vector3.up) * new Vector3(p.x * transform.localScale.x, 0, p.z * transform.localScale.z)).ToArray(); + midY = 0; + height = 2; + } + +} diff --git a/B2 Scripts/Prisms/RegularPrism.cs.meta b/B2 Scripts/Prisms/RegularPrism.cs.meta new file mode 100644 index 0000000..b6b1291 --- /dev/null +++ b/B2 Scripts/Prisms/RegularPrism.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a403003c72c8cac49b410f0c7504539a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B2 Scripts/Prisms/TriPrism.cs b/B2 Scripts/Prisms/TriPrism.cs new file mode 100644 index 0000000..626583a --- /dev/null +++ b/B2 Scripts/Prisms/TriPrism.cs @@ -0,0 +1,26 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +public class TriPrism : Prism +{ + + void Start() + { + points = new Vector3[] { new Vector3(9.2f / 2, 0, 0), new Vector3(-9.2f / 2, 0, 0), new Vector3(0, 0, 7.98f) }; + midY = 0; + height = 9.709259f; + } + + void Update() + { + var yMin = midY - height / 2 * transform.localScale.y; + var yMax = midY + height / 2 * transform.localScale.y; + foreach (var pt in points.Select(p => new Vector3(p.x * transform.localScale.x, 0, p.z * transform.localScale.z))) + { + var point = transform.position + Quaternion.AngleAxis(transform.eulerAngles.y, Vector3.up) * pt; + Debug.DrawLine(point + Vector3.up * yMin, point + Vector3.up * yMax, Color.red); + } + } +} diff --git a/B2 Scripts/Prisms/TriPrism.cs.meta b/B2 Scripts/Prisms/TriPrism.cs.meta new file mode 100644 index 0000000..4b8808c --- /dev/null +++ b/B2 Scripts/Prisms/TriPrism.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a87857e4e27860a469641682af89a345 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B3 Scripts/AgentController.cs b/B3 Scripts/AgentController.cs new file mode 100644 index 0000000..d45c0fe --- /dev/null +++ b/B3 Scripts/AgentController.cs @@ -0,0 +1,182 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.AI; + +public class AgentController : MonoBehaviour +{ + Animator anim; + NavMeshAgent agent; + NavMeshObstacle agentObstacle; + public AgentManager manager; + bool IsRotating = false; + bool IsJumping = false; + bool ActivateAgent = false; + Vector3 newTargetPosition = Vector3.zero; + Quaternion lookRotation; + bool sprint; + float rot, speed; + float baseSpeed = 1.550f; + // Start is called before the first frame update + void Start() + { + anim = GetComponent(); + agent = GetComponent(); + agentObstacle = GetComponent(); + agentObstacle.enabled = false; + // Don’t update position automatically + agent.updatePosition = false; + agent.autoTraverseOffMeshLink = false; + speed = baseSpeed; + } + + Vector2 smoothDeltaPosition = Vector2.zero; + Vector2 velocity = Vector2.zero; + + + void Update() + { + if(ActivateAgent){ + NavMeshPath path = new NavMeshPath(); + NavMesh.CalculatePath(transform.position, transform.position, NavMesh.AllAreas, path); + if(path.corners.Length > 0){ + ActivateAgent = false; + agent.enabled = true; + agent.avoidancePriority = 50; + agent.SetDestination(newTargetPosition); + + agent.isStopped = true; + IsRotating = true; + agent.updatePosition = false; + agent.autoTraverseOffMeshLink = false; + } + + }else if(agent.isOnOffMeshLink && !IsJumping){ + IsJumping = true; + anim.SetBool("leap",true); + }else if(IsJumping){ + OffMeshLinkData data = agent.currentOffMeshLinkData; + + Vector3 endPos = data.endPos + Vector3.up * agent.baseOffset; + + transform.position = Vector3.MoveTowards(transform.position, endPos, 2.0f * Time.deltaTime); + + if(transform.position == endPos) + { + agent.CompleteOffMeshLink(); + IsJumping = false; + anim.SetBool("leap", false); + } + }else if(!IsRotating && agent.enabled){ + MoveAnimation(); + }else if(agent.enabled){ + Vector3 direction = (agent.steeringTarget - transform.position).normalized; + lookRotation = Quaternion.LookRotation(direction); + + Quaternion oldRotation = transform.rotation; + transform.rotation = Quaternion.RotateTowards(transform.rotation, lookRotation, Time.deltaTime * 100.0f); + float angle = Quaternion.Angle( oldRotation, transform.rotation); + + anim.SetFloat("sides", angle); + if (Quaternion.Angle(transform.rotation, lookRotation) < 2.5f) + { + IsRotating = false; + agent.isStopped = false; + } + } + } + + void MoveAnimation(){ + Vector3 worldDeltaPosition = agent.destination - transform.position; + + // Map 'worldDeltaPosition' to local space + float dx = Vector3.Dot(transform.right, worldDeltaPosition); + float dy = Vector3.Dot(transform.forward, worldDeltaPosition); + Vector2 deltaPosition = new Vector2(dx, dy); + + // Low-pass filter the deltaMove + float smooth = Mathf.Min(1.0f, Time.deltaTime / 0.15f); + smoothDeltaPosition = Vector2.Lerp(smoothDeltaPosition, deltaPosition, smooth); + + // Update velocity if time advances + if (Time.deltaTime > 1e-5f) + velocity = smoothDeltaPosition / Time.deltaTime; + + if(velocity.magnitude > 0.5f && agent.remainingDistance > agent.radius -2){ + rot = Mathf.Deg2Rad * Vector2.Angle(transform.position,deltaPosition); + if (anim.GetCurrentAnimatorStateInfo(0).IsName("Forwards")) rot = 0.0f; + // Update animation parameters + anim.SetFloat("sides", rot); + anim.SetFloat("speed", speed); + + agent.speed = speed; + }else{ + anim.SetFloat("sides", 0f); + anim.SetFloat("speed", 0f); + } + + if (agent.enabled) + { + if (PathFindingComplete()) + { + anim.CrossFade("idle", 1f); + anim.SetFloat("sides", 0f); + anim.SetFloat("speed", 0f); + agent.avoidancePriority = 30; + agent.enabled = false; + agentObstacle.enabled = true; + } + } + } + + bool PathFindingComplete(){ + double r = 2; + float distance = Vector3.Distance(agent.destination, agent.transform.position); + + if (manager) + { + r += manager.arrivedAtDestination * (manager.arrivedAtDestination) * 30; + } + if (distance <= (agent.stoppingDistance + r)) + { + if (!agent.hasPath || agent.velocity.sqrMagnitude <= 0.05f) + { + if (manager) + { + manager.arrivedAtDestination += 1; + if (manager.arrivedAtDestination == manager.selectedCount) + { + manager.arrivedAtDestination = 0; + agent.stoppingDistance = 0.6f; + } + } + return true; + } + } + return false; + } + + public void OnSelected(){ + gameObject.transform.GetChild(7).gameObject.SetActive(true); + } + public void OnDeselect(){ + gameObject.transform.GetChild(7).gameObject.SetActive(false); + } + + public void SetAgentDestination(Vector3 targetPosition){ + ActivateAgent = true; + newTargetPosition = targetPosition; + agentObstacle.enabled = false; + } + + private void OnAnimatorMove() + { + if(!IsJumping && !IsRotating) + transform.position = agent.nextPosition; + } + + public void OnSpeedSliderChange(float val) + { + speed = (1 + val * 2) * baseSpeed; + } +} diff --git a/B3 Scripts/AgentController.cs.meta b/B3 Scripts/AgentController.cs.meta new file mode 100644 index 0000000..6034bb1 --- /dev/null +++ b/B3 Scripts/AgentController.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 063517f3ca99d4ae78e73436a940b69b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B3 Scripts/AgentManager.cs b/B3 Scripts/AgentManager.cs new file mode 100644 index 0000000..5a127e7 --- /dev/null +++ b/B3 Scripts/AgentManager.cs @@ -0,0 +1,95 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System; +using UnityEngine.AI; +using UnityEngine.UI; + +public class AgentManager : MonoBehaviour +{ + public List agents; + public Slider speedSlider; + public int selectedCount; + public int arrivedAtDestination; + + // Start is called before the first frame update + void Start() + { + arrivedAtDestination = 0; + agents = new List(); + GameObject[] newAgents = GameObject.FindGameObjectsWithTag("Agent"); + foreach(GameObject newAgent in newAgents){ + agents.Add(newAgent); + } + selectedCount = agents.Count; + } + + // Update is called once per frame + void Update() + { + if (Input.GetMouseButtonDown(0)) + { + CheckMousePosition(); + } + if(Input.GetMouseButtonDown(1)){ + CheckValidPosition(); + } + } + + void CheckMousePosition() + { + RaycastHit hit; + Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); + if (Physics.Raycast(ray, out hit)) + { + NavMeshHit navHit; + if (hit.collider.gameObject.CompareTag("Agent")) + { + if (!agents.Contains(hit.collider.gameObject)) + { + Debug.Log("Added Agent"); + agents.Add(hit.collider.gameObject); + selectedCount += 1; + hit.collider.gameObject.GetComponent().OnSelected(); + } + else + { + Debug.Log("Removed Agent"); + selectedCount -= 1; + agents.Remove(hit.collider.gameObject); + hit.collider.gameObject.GetComponent().OnDeselect(); + } + } + + } + } + + void CheckValidPosition(){ + RaycastHit hit; + Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); + if (Physics.Raycast(ray, out hit)) + { + NavMeshHit navHit; + if (NavMesh.SamplePosition(hit.point, out navHit, 1.0f, NavMesh.AllAreas)) + { + foreach (GameObject agent in agents) + { + Debug.Log(navHit.position); + NavMeshAgent nav = agent.GetComponent(); + Vector3 dist = nav.destination - navHit.position; + agent.GetComponent().SetAgentDestination(navHit.position); + } + } + } + } + + public void OnSliderChange(){ + float val = speedSlider.value; + foreach(GameObject agent in agents){ + agent.GetComponent().OnSpeedSliderChange(val); + } + } +} diff --git a/B3 Scripts/AgentManager.cs.meta b/B3 Scripts/AgentManager.cs.meta new file mode 100644 index 0000000..571fb4c --- /dev/null +++ b/B3 Scripts/AgentManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dc4fdba3a902d403bb2b42994fbe93cd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B3 Scripts/CameraController.cs b/B3 Scripts/CameraController.cs new file mode 100644 index 0000000..625e0fb --- /dev/null +++ b/B3 Scripts/CameraController.cs @@ -0,0 +1,32 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class CameraController : MonoBehaviour +{ + public GameObject Player; + private Vector3 offset; + public Transform target; + public float smoothTime = 0.3F; + private Vector3 velocity = Vector3.zero; + public Camera focus; + + // Start is called before the first frame update + void Start() + { + focus = GetComponent(); + } + + // LateUpdate is guaranteed to run when the object moves + + void LateUpdate() + { + // Define a target position above and behind the target transform + Vector3 targetPosition = target.TransformPoint(new Vector3(0, 5, -10)); + + // Smoothly move the camera towards that target position + transform.position = Vector3.SmoothDamp(transform.position, targetPosition, ref velocity, smoothTime); + focus.transform.LookAt(target); + + } +} diff --git a/B3 Scripts/CameraController.cs.meta b/B3 Scripts/CameraController.cs.meta new file mode 100644 index 0000000..b58b668 --- /dev/null +++ b/B3 Scripts/CameraController.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ce8bb8101243ec94d857c6ff215f6555 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B3 Scripts/Curves.meta b/B3 Scripts/Curves.meta new file mode 100644 index 0000000..b9480a6 --- /dev/null +++ b/B3 Scripts/Curves.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 7c6558394b792574481dc53bf753b7dc +folderAsset: yes +timeCreated: 1473982653 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/B3 Scripts/Curves/BezierSpline.cs b/B3 Scripts/Curves/BezierSpline.cs new file mode 100644 index 0000000..0ded675 --- /dev/null +++ b/B3 Scripts/Curves/BezierSpline.cs @@ -0,0 +1,290 @@ +using UnityEngine; +using System.Collections; +using System; + +/* + * + * + * 1. GetControlPoint / SetControlPoint of a curve + * 2. GetPoint <-- polynomial implementation + * 3. GetDirection <-- result of GetVelocity (first derivative of curve) + * 4. Define the ControlPoints modes for connectors and extremes + * 5. GetControlPointMode / SetControlPointMode + * + */ + + +/* Control points which connect curves can be in 3 modes */ +public enum ControlPointMode : int { + MIRRORED = 0, + ALIGNED = 1, + FREE = 2 +} + +public class BezierSpline : MonoBehaviour { + + + + /* We will keep one reference of each control point which connect curves */ + [SerializeField] + private ControlPointMode[] modes; + + /* Make them saveable */ + [SerializeField] + private Vector3[] controlPoints; + + [SerializeField] + private bool loop; + + public bool Loop { + get { + return loop; + } + set { + loop = value; + if(loop) { + /* We need to: match the mode and position of both ends of the spline */ + modes[modes.Length - 1] = modes[0]; + SetControlPoint(0, controlPoints[0]); + } + } + } + + public int curves; + + public Vector3 GetControlPoint(int p) { + return controlPoints[p]; + } + + public void SetControlPoint(int p, Vector3 point) { + /* When a point is moved, if it is the middle one + * adjust the two around it if the mode is not free + */ + if (GetControlPointMode(p) != ControlPointMode.FREE && p % 3 == 0) { + Vector3 deltaAdjust = point - controlPoints[p]; // get the difference between the new and current location + if(loop) { + /* For loops + * On extreme cases, set both ends to be equal + * For all other cases, adjust the surrounding points accoridngly by the delta + * of the newPosition - originalPosition + */ + if(p == 0) { + controlPoints[p + 1] += deltaAdjust; + controlPoints[controlPoints.Length - 2] += deltaAdjust; + controlPoints[controlPoints.Length - 1] = point; + } else if (p == controlPoints.Length - 1) { + controlPoints[0] = point; + controlPoints[1] += deltaAdjust; + controlPoints[p - 1] += deltaAdjust; + } else { + controlPoints[p - 1] += deltaAdjust; + controlPoints[p + 1] += deltaAdjust; + } + } else { + if (p > 0) { + controlPoints[p - 1] += deltaAdjust; + } + if(p + 1 < controlPoints.Length - 1) { + controlPoints[p + 1] += deltaAdjust; + } + } + } + controlPoints[p] = point; + EnforceConstraintMode(p); + } + + public int GetControlPointsCount { + get { + return Curves * 3 + 1; + } + } + + public int Curves { + get { + return curves; + } + } + + public void Reset() { + controlPoints = new Vector3[] { + new Vector3(1f, 0f, 0f), + new Vector3(2f, 1f, 0f), + new Vector3(3f, 2f, 0f), + new Vector3(4f, 3f, 0f) + }; + curves = 1; + + // the first point and the last of the first curve + modes = new ControlPointMode[] { + ControlPointMode.FREE, + ControlPointMode.FREE + }; + } + + public Vector3 GetPoint(float t) { + return transform.TransformPoint(CubicBezierCurve.GetPoint(controlPoints[0], controlPoints[1], controlPoints[2], controlPoints[3], t)); + } + + public Vector3 GetPoint(float t, int curveSection) { + int offset = curveSection * 3; + return transform.TransformPoint(CubicBezierCurve.GetPoint( + controlPoints[0 + offset], + controlPoints[1 + offset], + controlPoints[2 + offset], + controlPoints[3 + offset], t)); + } + + public Vector3 GetDirection(float t, int curveSection) { + return GetCurveVelocity(t, curveSection).normalized; + } + + public Vector3 GetCurveVelocity(float t, int curveSection) { + int offset = curveSection * 3; + return transform.TransformPoint(CubicBezierCurve.GetCurveRateOfChange( + controlPoints[0 + offset], + controlPoints[1 + offset], + controlPoints[2 + offset], + controlPoints[3 + offset], t)) - transform.position; + } + + /* + The Spline will receive 3 more points. Although the curve has 4, the first point + of the new curve will be the last point of the previous one + */ + public void AddCurve() { + // make the first point of the new curve, the last of the original + Vector3 point = controlPoints[controlPoints.Length - 1]; + + // resize the current controlPoints array + Array.Resize(ref controlPoints, controlPoints.Length + 3); + + // set up the new point in the resized array + point.x += 1f; + controlPoints[controlPoints.Length - 3] = point; + point.x += 1f; + controlPoints[controlPoints.Length - 2] = point; + point.x += 1f; + controlPoints[controlPoints.Length - 1] = point; + + curves += 1; + + /* When adding a new curve, make the new control point connector the same as the one before by default */ + Array.Resize(ref modes, modes.Length + 1); + modes[modes.Length - 1] = modes[modes.Length - 2]; + + /* Enforce the mode on the new curve's connector control point */ + EnforceConstraintMode(controlPoints.Length - 4); + + if (loop) { + // match position + controlPoints[controlPoints.Length - 1] = controlPoints[0]; + // match mode + modes[modes.Length - 1] = modes[0]; + // enforce + EnforceConstraintMode(0); + } + } + + public static Vector3 GetPoint(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t) { + t = Mathf.Clamp01(t); + float oneMinus = 1f - t; + return + (float)Math.Pow(oneMinus, 3) * p0 + + 3f * (float)Math.Pow(oneMinus, 2) * t * p1 + + 3f * oneMinus * (float)Math.Pow(t, 2) * p2 + + (float)Math.Pow(t, 3) * p3; + } + + public static Vector3 GetCurveRateOfChange(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t) { + t = Mathf.Clamp01(t); + float oneMinus = 1f - t; + return + 3f * oneMinus * oneMinus * (p1 - p0) + + 6f * oneMinus * t * (p2 - p1) + + 3f * t * t * (p3 - p2); + + } + + /* Get and Set the contorl point for the right curve */ + + public ControlPointMode GetControlPointMode(int i) { + return modes[(i + 1) / 3]; + } + + public void SetControlPointMode(int p, ControlPointMode mode) { + int modeIndex = (p + 1) / 3; + modes[modeIndex] = mode; + + /* If we are in loop mode, ensure both ends share the same constraint if either changed */ + if (loop) { + if(modeIndex == 0) { + modes[modes.Length - 1] = mode; + } else if (modeIndex == modes.Length - 1) { + modes[0] = mode; + } + } + EnforceConstraintMode(p); + } + + /* + * Function which will enforce the constraint between points + * To be called when a a point is Set or a mode is Set + */ + + public void EnforceConstraintMode(int index) { + + // get current mode + int curModeIndex = (index + 1) / 3; + ControlPointMode mode = modes[curModeIndex]; + + /* We can only enforce the constraint on points which belong to the union of 2 curves. + * We disregard the extreme cases IFF NOT LOOPING (first and last control point and the case on which the mode + * is FREE, since there is nothing to adjust + */ + if(mode == ControlPointMode.FREE || !loop && (curModeIndex == 0 || curModeIndex == modes.Length - 1)) { + return; + } + + /* + * Find the middle point + * 1. if the middle is selected: + * a. adjust the one after it + * 2. else, adjust the opposite of the currently selected + * + * 3. LOOPING: + * If we are looping, we have to ROLL over indexes. + */ + int midIndex = curModeIndex * 3, fixIndex, adjustIndex; + if(midIndex <= index) { + fixIndex = midIndex - 1; + if (fixIndex < 0) { // loop case + fixIndex = controlPoints.Length - 2; + } + adjustIndex = midIndex + 1; // this will be the adjusted one + if (adjustIndex >= controlPoints.Length) { // loop case + adjustIndex = 1; + } + } else { + fixIndex = midIndex + 1; + if(fixIndex >= controlPoints.Length) { // loop case + fixIndex = 1; + } + adjustIndex = midIndex - 1; + if(adjustIndex < 0) { // loop case + adjustIndex = controlPoints.Length - 2; + } + } + + + Vector3 middlePoint = controlPoints[midIndex]; + + /* Mirroring */ + Vector3 enforcedTangent = middlePoint - controlPoints[fixIndex]; + + /* Align - respect distance */ + if (mode == ControlPointMode.ALIGNED) { + enforcedTangent = enforcedTangent.normalized * Vector3.Distance(middlePoint, controlPoints[adjustIndex]); + } + controlPoints[adjustIndex] = middlePoint + enforcedTangent; + } +} diff --git a/B3 Scripts/Curves/BezierSpline.cs.meta b/B3 Scripts/Curves/BezierSpline.cs.meta new file mode 100644 index 0000000..fd17544 --- /dev/null +++ b/B3 Scripts/Curves/BezierSpline.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 940a190d51ca13040a18565d29b3bb6c +timeCreated: 1450772809 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B3 Scripts/Curves/CatmullRomSpline.cs b/B3 Scripts/Curves/CatmullRomSpline.cs new file mode 100644 index 0000000..b849e18 --- /dev/null +++ b/B3 Scripts/Curves/CatmullRomSpline.cs @@ -0,0 +1,144 @@ +using UnityEngine; +using System.Collections; +using System; + +public class CatmullRomSpline : MonoBehaviour, ICurve { + + + /* + * Catmull Rom splines have 4 control points + * + */ + [SerializeField] + private Vector3[] controlPoints; + + [SerializeField] + private int curves; + + [SerializeField] + private bool loop; + + public int ControlPointsCount { + get { + return controlPoints.Length; + } + } + + public bool Loop { + get { + return loop; + } + set { + loop = value; + // handle reforming here if needed + } + } + + public int Curves { + get { + return curves; + } + } + + public Vector3 GetPoint(int i) { + return controlPoints[i]; + } + + public void SetPoint(int i, Vector3 point) { + controlPoints[i] = point; + } + + public Vector3 GetCurvePoint(float t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3) { + Vector3 a = 0.5f * (2f * p1); + Vector3 b = 0.5f * (p2 - p0); + Vector3 c = 0.5f * (2f * p0 - 5f * p1 + 4f * p2 - p3); + Vector3 d = 0.5f * (-p0 + 3f * p1 - 3f * p2 + p3); + + Vector3 pos = a + (b * t) + (c * t * t) + (d * t * t * t); + // first derivative: b + 2ct + 3dt^2 + return pos; + } + + public Vector3 GetCurveDirection(float t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3) { + + Vector3 b = 0.5f * (p2 - p0); + Vector3 c = 0.5f * (2f * p0 - 5f * p1 + 4f * p2 - p3); + Vector3 d = 0.5f * (-p0 + 3f * p1 - 3f * p2 + p3); + + // first derivative: b + 2ct + 3dt^2 + Vector3 pos = b + 2f * c * t + 3f * d * (float) Math.Pow(t,2); + return pos; + } + + /* Unity method */ + public void Reset() { + controlPoints = new Vector3[] { + new Vector3(1f, 0f, 0f), + new Vector3(3f, 3f, 0f), + new Vector3(5f, 3f, 0f), + new Vector3(7f, 0f, 0f) + }; + curves += 1; + } + + /* ICurve methods */ + + private int currentControlPoint = 0; + private float currentT = 0; + private bool running = false; + private int velocity = 20; + private int calls = 0; + + public void SetSteps(int v) { + velocity = v; + } + + public int CurrentControlPoint { + get { + return currentControlPoint; + } + } + + public bool Running { + get { + return running; + } + } + + /* Returns the next point in the curve, and advances it forward */ + public Vector3 GetCurvePoint() { + Vector3 point = Vector3.zero; + if ((currentControlPoint != 0 && currentControlPoint < ControlPointsCount - 2) || loop) { + int i = currentControlPoint, + iMinusOne = i == 0 ? controlPoints.Length - 1 : i - 1, + iPlusOne = (i + 1) % ControlPointsCount, + iPlusTwo = (i + 2) % ControlPointsCount; + currentT = calls / (float) velocity; + currentT = Mathf.Clamp01(currentT); + point = GetCurvePoint(currentT, controlPoints[iMinusOne], controlPoints[i], controlPoints[iPlusOne], controlPoints[iPlusTwo]); + if (calls == velocity) { + currentControlPoint = currentControlPoint + 1 > controlPoints.Length - 1 ? 0 : currentControlPoint + 1; + calls = 0; + } else calls++; + } + return this.transform.TransformPoint(point); + } + + /* Returns the direction of the current point in the curve */ + public Vector3 GetCurveDirection() { + Vector3 dir = Vector3.zero; + int i = currentControlPoint, + iMinusOne = i == 0 ? controlPoints.Length - 1 : i - 1, + iPlusOne = (i + 1) % ControlPointsCount, + iPlusTwo = (i + 2) % ControlPointsCount; + currentT = calls / (float)velocity; + currentT = Mathf.Clamp01(currentT); + dir = GetCurveDirection(currentT, controlPoints[iMinusOne], controlPoints[i], controlPoints[iPlusOne], controlPoints[iPlusTwo]); + return this.transform.TransformPoint(dir).normalized; + } + + public void ResetCurve() { + currentControlPoint = loop ? 0 : 1; + currentT = 0; + } +} diff --git a/B3 Scripts/Curves/CatmullRomSpline.cs.meta b/B3 Scripts/Curves/CatmullRomSpline.cs.meta new file mode 100644 index 0000000..bea9d94 --- /dev/null +++ b/B3 Scripts/Curves/CatmullRomSpline.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 18a31ed461c77594cb1e5976b0884b5c +timeCreated: 1451312880 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B3 Scripts/Curves/CubicBezierCurve.cs b/B3 Scripts/Curves/CubicBezierCurve.cs new file mode 100644 index 0000000..9bf9762 --- /dev/null +++ b/B3 Scripts/Curves/CubicBezierCurve.cs @@ -0,0 +1,49 @@ +using UnityEngine; +using System.Collections; +using System; + +public class CubicBezierCurve : MonoBehaviour { + + public Vector3[] controlPoints; + + public void Reset() { + controlPoints = new Vector3[] { + new Vector3(1f, 0f, 0f), + new Vector3(2f, 0f, 0f), + new Vector3(3f, 0f, 0f), + new Vector3(4f, 0f, 0f) + }; + } + + public Vector3 GetPoint(float t) { + return transform.TransformPoint(CubicBezierCurve.GetPoint(controlPoints[0], controlPoints[1], controlPoints[2], controlPoints[3], t)); + } + + public Vector3 GetDirection(float t) { + return GetCurveVelocity(t).normalized; + } + + public Vector3 GetCurveVelocity(float t) { + return transform.TransformPoint(CubicBezierCurve.GetCurveRateOfChange(controlPoints[0], controlPoints[1], controlPoints[2], controlPoints[3], t)) - transform.position; + } + + public static Vector3 GetPoint(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t) { + t = Mathf.Clamp01(t); + float oneMinus = 1f - t; + return + (float) Math.Pow(oneMinus, 3) * p0 + + 3f * (float) Math.Pow(oneMinus,2) * t * p1 + + 3f * oneMinus * (float) Math.Pow(t,2) * p2 + + (float) Math.Pow(t,3) * p3; + } + + public static Vector3 GetCurveRateOfChange(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t) { + t = Mathf.Clamp01(t); + float oneMinus = 1f - t; + return + 3f * oneMinus * oneMinus * (p1 - p0) + + 6f * oneMinus * t * (p2 - p1) + + 3f * t * t * (p3 - p2); + + } +} diff --git a/B3 Scripts/Curves/CubicBezierCurve.cs.meta b/B3 Scripts/Curves/CubicBezierCurve.cs.meta new file mode 100644 index 0000000..66f79ca --- /dev/null +++ b/B3 Scripts/Curves/CubicBezierCurve.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 57dbb68ba5930de4aafd969398df3416 +timeCreated: 1450657325 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B3 Scripts/Curves/CurveAnimator.meta b/B3 Scripts/Curves/CurveAnimator.meta new file mode 100644 index 0000000..be4ffea --- /dev/null +++ b/B3 Scripts/Curves/CurveAnimator.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 706440163ea519c44adfb0bebd8a7bff +folderAsset: yes +timeCreated: 1451505497 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/B3 Scripts/Curves/CurveAnimator/CurveAnimator.cs b/B3 Scripts/Curves/CurveAnimator/CurveAnimator.cs new file mode 100644 index 0000000..0d3cf8f --- /dev/null +++ b/B3 Scripts/Curves/CurveAnimator/CurveAnimator.cs @@ -0,0 +1,37 @@ +using UnityEngine; +using System.Collections; + +public class CurveAnimator : MonoBehaviour { + + Transform t; + Quaternion q; + public int FrameRate = 30; + + float lastFrame = 0; + + public bool followCurve = true; + public CatmullRomSpline curve; + + // Use this for initialization + void Start () { + t = this.transform; + q = t.rotation; + if (curve != null) { + curve.ResetCurve(); + t.position = curve.GetCurvePoint(); + } + } + + // Update is called once per frame + void Update () { + if(followCurve) { + if(lastFrame >= 1 / (float) FrameRate) { + Vector3 p = curve != null ? curve.GetCurvePoint() : Vector3.zero; + t.position = p == Vector3.zero ? t.position : p; + lastFrame = 0f; + } else { + lastFrame += Time.deltaTime; + } + } + } +} diff --git a/B3 Scripts/Curves/CurveAnimator/CurveAnimator.cs.meta b/B3 Scripts/Curves/CurveAnimator/CurveAnimator.cs.meta new file mode 100644 index 0000000..88f08f9 --- /dev/null +++ b/B3 Scripts/Curves/CurveAnimator/CurveAnimator.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: c0d688727e9187341b2e699ed61af2a9 +timeCreated: 1451505508 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B3 Scripts/Curves/ICurve.cs b/B3 Scripts/Curves/ICurve.cs new file mode 100644 index 0000000..8b84a0e --- /dev/null +++ b/B3 Scripts/Curves/ICurve.cs @@ -0,0 +1,14 @@ +using UnityEngine; +using System.Collections; + +public interface ICurve { + + Vector3 GetCurvePoint(); + + Vector3 GetCurveDirection(); + + void ResetCurve(); + + void SetSteps(int v); + +} diff --git a/B3 Scripts/Curves/ICurve.cs.meta b/B3 Scripts/Curves/ICurve.cs.meta new file mode 100644 index 0000000..4a002b2 --- /dev/null +++ b/B3 Scripts/Curves/ICurve.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 734a92553471104459101d28d9410afb +timeCreated: 1451481913 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B3 Scripts/Curves/Line.cs b/B3 Scripts/Curves/Line.cs new file mode 100644 index 0000000..c75cfe4 --- /dev/null +++ b/B3 Scripts/Curves/Line.cs @@ -0,0 +1,17 @@ +using UnityEngine; +using System.Collections; + +/// +/// A Line is defined by two points which, as in any other 3D environment, will be +/// represented by a (X,Y,Z) Vector. +/// + +namespace Curves { + + public class Line : MonoBehaviour { + + public Vector3 p0, p1; + + } + +} diff --git a/B3 Scripts/Curves/Line.cs.meta b/B3 Scripts/Curves/Line.cs.meta new file mode 100644 index 0000000..f04d504 --- /dev/null +++ b/B3 Scripts/Curves/Line.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 88b48310543da704cb9bae1582f62e1f +timeCreated: 1450186306 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B3 Scripts/Curves/QuadBezierCurve.cs b/B3 Scripts/Curves/QuadBezierCurve.cs new file mode 100644 index 0000000..70241be --- /dev/null +++ b/B3 Scripts/Curves/QuadBezierCurve.cs @@ -0,0 +1,87 @@ +using UnityEngine; +using System.Collections; + +namespace Curves { + + public class QuadBezierCurve : MonoBehaviour { + + public Vector3[] controlPoints; + + /* Special Unity method - Creation or Reset */ + public void Reset() { + + controlPoints = new Vector3[] { + new Vector3(1f,0f,0f), + new Vector3(2f,0f,0f), + new Vector3(3f,0f,0f) + }; + + } + + public Vector3 GetPoint(float t) { + return transform.TransformPoint(QuadBezierCurve.GetPoint(controlPoints[0], controlPoints[1], controlPoints[2], t)); + } + + /* Computing First Derivative (curve rate of change) */ + + public Vector3 GetDirection(float t) { + return this.GetCurveVelocity(t).normalized; + } + + public Vector3 GetCurveVelocity(float t) { + // we substract the transform.position such that we only get the slope instead of a Vector3 relative in World's coordinates + return transform.TransformPoint(QuadBezierCurve.GetPointRateOfChange(controlPoints[0], controlPoints[1], controlPoints[2], t)) - transform.position; + } + + /// + /// + /// Given by the quadratic relation between 3 control points + /// We derived each resulting point at time t simply by its + /// linear interpolation relationship: B(t) = (1 - t)^2 P0 + 2 (1 - t) t P1 + t^2 P2 + /// + /// + /// + /// + /// + /// + /// + + public static Vector3 GetPoint(Vector3 p0, Vector3 p1, Vector3 p2, float t) { + t = Mathf.Clamp01(t); + float oneMinus = 1f - t; + return + oneMinus * oneMinus * p0 + + 2f * t * oneMinus * p1 + + t * t * p2; + /* Another way to do this is to Lerp twice for each pair of points + return Vector3.Lerp(Vector3.Lerp(p1,p2,t), Vector3.Lerp(p2, p3, t), t); + */ + } + + /// + /// + /// Because we know the quadratic equation for a Bezier curve, + /// we can calculate each point's, at any given t, direction + /// using its first derivative: B'(t) = 2 (1 - t) (P1 - P0) + 2 t (P2 - P1) + /// + /// This function produces lines tangent to the curve, which can be interpreted + /// as the speed with which we move along the curve. So now we can add a + /// GetVelocity method to Bezier + /// + /// + /// + /// + /// + /// + /// + public static Vector3 GetPointRateOfChange(Vector3 p0, Vector3 p1, Vector3 p2, float t) { + t = Mathf.Clamp01(t); + float oneMinus = 1f - t; + return + 2f * oneMinus * (p1 - p0) + + 2f * t * (p2 - p1); + } + + } + +} diff --git a/B3 Scripts/Curves/QuadBezierCurve.cs.meta b/B3 Scripts/Curves/QuadBezierCurve.cs.meta new file mode 100644 index 0000000..58420fe --- /dev/null +++ b/B3 Scripts/Curves/QuadBezierCurve.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: d5f66efc46499804695339038c4f240b +timeCreated: 1450656132 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B3 Scripts/Curves/nwlwgsh3.dgz~ b/B3 Scripts/Curves/nwlwgsh3.dgz~ new file mode 100644 index 0000000..338ac3c --- /dev/null +++ b/B3 Scripts/Curves/nwlwgsh3.dgz~ @@ -0,0 +1,266 @@ +using UnityEngine; +using System.Collections; +using System; + +/* + * + * + * 1. GetControlPoint / SetControlPoint of a curve + * 2. GetPoint <-- polynomial implementation + * 3. GetDirection <-- result of GetVelocity (first derivative of curve) + * 4. Define the ControlPoints modes for connectors and extremes + * 5. GetControlPointMode / SetControlPointMode + * + */ + + +/* Control points which connect curves can be in 3 modes */ +public enum ControlPointMode : int { + MIRRORED = 0, + ALIGNED = 1, + FREE = 2 +} + +public class BezierSpline : MonoBehaviour { + + + + /* We will keep one reference of each control point which connect curves */ + [SerializeField] + private ControlPointMode[] modes; + + /* Make them saveable */ + [SerializeField] + private Vector3[] controlPoints; + + [SerializeField] + private bool loop; + + public bool Loop { + get { + return loop; + } + set { + loop = value; + if(loop) { + /* We need to: match the mode and position of both ends of the spline */ + modes[modes.Length - 1] = modes[0]; + SetControlPoint(0, controlPoints[0]); + } + } + } + + public int curves; + + public Vector3 GetControlPoint(int p) { + return controlPoints[p]; + } + + public void SetControlPoint(int p, Vector3 point) { + /* When a point is moved, if it is the middle one + * adjust the two around it if the mode is not free + */ + if (GetControlPointMode(p) != ControlPointMode.FREE && p % 3 == 0) { + Vector3 deltaAdjust = point - controlPoints[p]; // get the difference between the new and current location + if(loop) { + /* For loops + * On extreme cases, set both ends to be equal + * For all other cases, adjust the surrounding points accoridngly by the delta + * of the newPosition - originalPosition + */ + if(p == 0) { + controlPoints[p + 1] += deltaAdjust; + controlPoints[controlPoints.Length - 2] += deltaAdjust; + controlPoints[controlPoints.Length - 1] = point; + } else if (p == controlPoints.Length - 1) { + controlPoints[controlPoints.Length - 1] += deltaAdjust; + controlPoints[1] += deltaAdjust; + controlPoints[0] = point; + } else { + controlPoints[p - 1] += deltaAdjust; + controlPoints[p + 1] += deltaAdjust; + } + } else { + if (p > 0) { + controlPoints[p - 1] += deltaAdjust; + } + if(p + 1 < controlPoints.Length - 1) { + controlPoints[p + 1] += deltaAdjust; + } + } + } + controlPoints[p] = point; + EnforceContraintMode(p); + } + + public int GetControlPointsCount { + get { + return Curves * 3 + 1; + } + } + + public int Curves { + get { + return curves; + } + } + + public void Reset() { + controlPoints = new Vector3[] { + new Vector3(1f, 0f, 0f), + new Vector3(2f, 1f, 0f), + new Vector3(3f, 2f, 0f), + new Vector3(4f, 3f, 0f) + }; + curves = 1; + + // the first point and the last of the first curve + modes = new ControlPointMode[] { + ControlPointMode.FREE, + ControlPointMode.FREE + }; + } + + public Vector3 GetPoint(float t) { + return transform.TransformPoint(CubicBezierCurve.GetPoint(controlPoints[0], controlPoints[1], controlPoints[2], controlPoints[3], t)); + } + + public Vector3 GetPoint(float t, int curveSection) { + int offset = curveSection * 3; + return transform.TransformPoint(CubicBezierCurve.GetPoint( + controlPoints[0 + offset], + controlPoints[1 + offset], + controlPoints[2 + offset], + controlPoints[3 + offset], t)); + } + + public Vector3 GetDirection(float t, int curveSection) { + return GetCurveVelocity(t, curveSection).normalized; + } + + public Vector3 GetCurveVelocity(float t, int curveSection) { + int offset = curveSection * 3; + return transform.TransformPoint(CubicBezierCurve.GetCurveRateOfChange( + controlPoints[0 + offset], + controlPoints[1 + offset], + controlPoints[2 + offset], + controlPoints[3 + offset], t)) - transform.position; + } + + /* + The Spline will receive 3 more points. Although the curve has 4, the first point + of the new curve will be the last point of the previous one + */ + public void AddCurve() { + // make the first point of the new curve, the last of the original + Vector3 point = controlPoints[controlPoints.Length - 1]; + + // resize the current controlPoints array + Array.Resize(ref controlPoints, controlPoints.Length + 3); + + // set up the new point in the resized array + point.x += 1f; + controlPoints[controlPoints.Length - 3] = point; + point.x += 1f; + controlPoints[controlPoints.Length - 2] = point; + point.x += 1f; + controlPoints[controlPoints.Length - 1] = point; + + curves += 1; + + /* When adding a new curve, make the new control point connector the same as the one before by default */ + Array.Resize(ref modes, modes.Length + 1); + modes[modes.Length - 1] = modes[modes.Length - 2]; + + /* Enforce the mode on the new curve's connector control point */ + EnforceContraintMode(controlPoints.Length - 4); + } + + public static Vector3 GetPoint(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t) { + t = Mathf.Clamp01(t); + float oneMinus = 1f - t; + return + (float)Math.Pow(oneMinus, 3) * p0 + + 3f * (float)Math.Pow(oneMinus, 2) * t * p1 + + 3f * oneMinus * (float)Math.Pow(t, 2) * p2 + + (float)Math.Pow(t, 3) * p3; + } + + public static Vector3 GetCurveRateOfChange(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t) { + t = Mathf.Clamp01(t); + float oneMinus = 1f - t; + return + 3f * oneMinus * oneMinus * (p1 - p0) + + 6f * oneMinus * t * (p2 - p1) + + 3f * t * t * (p3 - p2); + + } + + /* Get and Set the contorl point for the right curve */ + + public ControlPointMode GetControlPointMode(int i) { + return modes[(i + 1) / 3]; + } + + public void SetControlPointMode(int p, ControlPointMode mode) { + int modeIndex = (p + 1) / 3; + modes[modeIndex] = mode; + + /* If we are in loop mode, ensure both ends share the same constraint if either changed */ + if (loop) { + if(modeIndex == 0) { + modes[modes.Length - 1] = mode; + } else if (modeIndex == modes.Length - 1) { + modes[0] = mode; + } + } + EnforceContraintMode(p); + } + + /* + * Function which will enforce the constraint between points + * To be called when a a point is Set or a mode is Set + */ + + public void EnforceContraintMode(int index) { + + // get current mode + int curModeIndex = (index + 1) / 3; + ControlPointMode mode = modes[curModeIndex]; + + /* We can only enforce the constraint on points which belong to the union of 2 curves. + * We disregard the extreme cases (first and last control point and the case on which the mode + * is FREE, since there is nothing to adjust + */ + if(mode == ControlPointMode.FREE || curModeIndex == 0 || curModeIndex == modes.Length - 1) { + return; + } + + /* + * Find the middle point + * 1. if the middle is selected: + * a. adjust the one after it + * 2. else, adjust the opposite of the currently selected + */ + int midIndex = curModeIndex * 3, fixIndex, adjustIndex; + if(midIndex <= index) { + fixIndex = midIndex - 1; + adjustIndex = midIndex + 1; // this will be the adjusted one + } else { + fixIndex = midIndex + 1; + adjustIndex = midIndex - 1; + } + + + Vector3 middlePoint = controlPoints[midIndex]; + + /* Mirroring */ + Vector3 enforcedTangent = middlePoint - controlPoints[fixIndex]; + + /* Align - respect distance */ + if (mode == ControlPointMode.ALIGNED) { + enforcedTangent = enforcedTangent.normalized * Vector3.Distance(middlePoint, controlPoints[adjustIndex]); + } + controlPoints[adjustIndex] = middlePoint + enforcedTangent; + } +} diff --git a/B3 Scripts/IsoCameraController.cs b/B3 Scripts/IsoCameraController.cs new file mode 100644 index 0000000..1ff55cc --- /dev/null +++ b/B3 Scripts/IsoCameraController.cs @@ -0,0 +1,48 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class IsoCameraController : MonoBehaviour +{ + public float moveSpeed = 10f; + public float sensitivity = 2f; + + // Start is called before the first frame update + void Start() + { + + } + + // Update is called once per frame + void LateUpdate() + { + + transform.position = transform.position + Vector3.right * Input.GetAxis("Horizontal")*moveSpeed*Time.deltaTime; + transform.position = transform.position + Vector3.forward * Input.GetAxis("Vertical")*moveSpeed*Time.deltaTime; + transform.position = transform.position + transform.forward * Input.mouseScrollDelta.y*moveSpeed*Time.deltaTime; + //transform.position = transform.position - transform.up*Input.GetAxis("Shift")*moveSpeed*Time.deltaTime; + /* + float rotX = transform.localEulerAngles.y + Input.GetAxis("Mouse X") * sensitivity; + float rotY = transform.localEulerAngles.x - Input.GetAxis("Mouse Y") * sensitivity; + transform.localEulerAngles = new Vector3(rotY, rotX, 0f); + /*Cursor.lockState = UnityEngine.CursorLockMode.Locked; + + if (Input.GetKey(KeyCode.Escape)) + Cursor.lockState = UnityEngine.CursorLockMode.None; + else + Cursor.lockState = UnityEngine.CursorLockMode.Locked; + */ + /* + if (Input.GetKeyUp(KeyCode.Mouse0)) + { + RaycastHit hit; + Ray ray = this.GetComponent().ScreenPointToRay(Input.mousePosition); + + if (Physics.Raycast(ray, out hit)) + { + //whatever + } + } + */ + } +} diff --git a/B3 Scripts/IsoCameraController.cs.meta b/B3 Scripts/IsoCameraController.cs.meta new file mode 100644 index 0000000..3840056 --- /dev/null +++ b/B3 Scripts/IsoCameraController.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 34ffc899f01af454fa894ae39b76e986 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B4 Scripts/Agent.cs b/B4 Scripts/Agent.cs new file mode 100644 index 0000000..5518c46 --- /dev/null +++ b/B4 Scripts/Agent.cs @@ -0,0 +1,340 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.AI; + +public class Agent : MonoBehaviour +{ + public float radius; + public float mass; + public float perceptionRadius; + public float goalWeight = 5; + public float goalForceSpeed; + public float agentCount = 0; + public bool spiralFlag = false; + public bool flocking = true; + public Vector3 velocmon; + public float crowdFollowParam = 0f; + + private Vector3 ei_0 = Vector3.zero; + + float A = 2.0f; + float B = 0.6f; + float k = 1.2f; + float kappa = 2.4f; + + private List path; + private NavMeshAgent nma; + private Rigidbody rb; + private SphereCollider sc; + private HashSet perceivedNeighbors = new HashSet(); + private HashSet perceivedWalls = new HashSet(); + + void Start() + { + path = new List(); + nma = GetComponent(); + rb = GetComponent(); + sc = GetComponent(); + + gameObject.transform.localScale = new Vector3(2 * radius, 1, 2 * radius); + nma.radius = radius; + rb.mass = mass; + GetComponent().radius = perceptionRadius / 2; + + if (spiralFlag) + { + computeSpiral(); + } + } + + private void computeSpiral() + { + Vector3 apos = this.transform.position; + Vector3 apnext = apos*1.00008f; + transform.RotateAround(apnext, Vector3.up, 60.0f); + velocmon = apnext; + + this.ComputePath(apnext); + + } + + private void Update() + { + if (spiralFlag) + { + if (path.Count == 1 && Vector3.Distance(transform.position, path[0]) < 2f) + { + computeSpiral(); + + } + } + else { + if (path.Count > 1 && Vector3.Distance(transform.position, path[0]) < 1.1f) + { + path.RemoveAt(0); + } else if (path.Count == 1 && Vector3.Distance(transform.position, path[0]) < 2f) + { + path.RemoveAt(0); + + if (path.Count == 0) + { + gameObject.SetActive(false); + AgentManager.RemoveAgent(gameObject); + } + } + } + #region Visualization + + if (false) + { + if (path.Count > 0) + { + Debug.DrawLine(transform.position, path[0], Color.green); + } + for (int i = 0; i < path.Count - 1; i++) + { + Debug.DrawLine(path[i], path[i + 1], Color.yellow); + } + } + + if (false) + { + foreach (var neighbor in perceivedNeighbors) + { + Debug.DrawLine(transform.position, neighbor.transform.position, Color.yellow); + } + } + + #endregion + } + + #region Public Functions + + public void ComputePath(Vector3 destination) + { + nma.enabled = true; + var nmPath = new NavMeshPath(); + nma.CalculatePath(destination, nmPath); + if(spiralFlag){ + path = new List() { destination }; + }else{ + path = nmPath.corners.Skip(1).ToList(); + } + //nma.SetDestination(destination); + goalForceSpeed = nma.speed; + nma.enabled = false; + } + + public Vector3 GetVelocity() + { + return rb.velocity; + } + + #endregion + + #region Incomplete Functions + + private Vector3 ComputeForce() + { + Vector3 flockForce = Vector3.zero; + if (flocking){ + flockForce = CalculateFlockForce(); + } + Vector3 gForce = CalculateGoalForce(); + + CleanNeighbors(); + Vector3 aForce = CalculateAgentForce(); + + Vector3 wForce = CalculateWallForce(); + + Debug.DrawLine(transform.position, transform.position + gForce, Color.red, 0.5f); + Debug.DrawLine(transform.position, transform.position + aForce, Color.green, 0.5f); + Debug.DrawLine(transform.position, transform.position + wForce, Color.blue, 0.5f); + + return gForce + aForce + wForce + flockForce; + /* + var force = Vector3.zero; + + if (force != Vector3.zero) + { + return force.normalized * Mathf.Min(force.magnitude, Parameters.maxSpeed); + } else + { + return Vector3.zero; + } + */ + } + + private Vector3 CalculateFlockForce() + { + Vector3 Alighnment = Vector3.zero; + Vector3 Seperation = Vector3.zero; + Vector3 Cohesion = Vector3.zero; + foreach (GameObject neighbor in perceivedNeighbors) + { + Alighnment += neighbor.GetComponent().GetLastDirection(); + Cohesion += neighbor.transform.position; + Seperation += neighbor.transform.position - rb.position; + } + Cohesion /= perceivedNeighbors.Count(); + Alighnment /= perceivedNeighbors.Count(); + Seperation /= perceivedNeighbors.Count(); + Seperation *= -1; + return Alighnment.normalized + Seperation.normalized + (Cohesion - rb.position).normalized; + } + private Vector3 NeighborGoalForce(){ + Vector3 avg_e = Vector3.zero; + if(perceivedNeighbors.Count == 0) + return avg_e; + foreach(GameObject neighbor in perceivedNeighbors) + { + avg_e = neighbor.GetComponent().GetLastDirection(); + } + avg_e = avg_e * (1 / perceivedNeighbors.Count); + return avg_e; + } + + private Vector3 CalculateGoalForce() + { + // calculate the path to the destination and take the first two corners. + // return the unit vector in that direction and multiplied by a constant scalar. + ei_0 = (new Vector3(path[0].x, transform.position.y, path[0].z) - transform.position).normalized; + if(crowdFollowParam > 0){ + Vector3 neighbor_e = NeighborGoalForce(); + ei_0 = ((1 - crowdFollowParam) * ei_0 + crowdFollowParam * neighbor_e).normalized; + } + //Return the (Vi_0 * ei_0) - Vi + return ((goalForceSpeed * ei_0) - GetVelocity()); + } + + private Vector3 CalculateAgentForce() + { + Vector3 agentForce = Vector3.zero; + foreach (GameObject neighbor in perceivedNeighbors) + { + Vector3 repForce = Vector3.zero; + Vector3 slideForce = Vector3.zero; + Vector3 opos = neighbor.transform.position; + Agent oagent = neighbor.GetComponent(); + + float r_ij = oagent.radius + radius; + float d_ij = Vector3.Distance(opos,transform.position); + float g = (r_ij - d_ij > 0.000001) ? (r_ij- d_ij) : 0; + Vector3 n_ij = (transform.position - opos)/d_ij; + Vector3 t_ij = new Vector3(-n_ij.z, n_ij.x, 0); + float deltaV_ji = Vector3.Dot((oagent.GetVelocity() - GetVelocity()), t_ij); + + repForce = (A * Mathf.Exp((r_ij - d_ij)/B) + kappa*g) * n_ij; + slideForce = k * g * deltaV_ji * t_ij; + + agentForce += (repForce + slideForce); + } + + return agentForce; + } + + private Vector3 CalculateWallForce() + { + + // Forces on the agent will be perpendicular to the walls closest surface + Vector3 WallForce = Vector3.zero; + foreach (GameObject Wall in perceivedWalls) + { + Vector3 repForce = Vector3.zero; + Vector3 slideForce = Vector3.zero; + + float r_i = radius; + float d_iw = Vector3.Distance(Wall.transform.position, transform.position); + float g = (r_i - d_iw > 0.000001) ? (r_i - d_iw) : 0; + Vector3 n_iw = Vector3.zero; + + Vector3 direction = (Wall.transform.position - rb.transform.position).normalized; + float dotEast = Vector3.Dot(Vector3.right, direction); + float dotNorth = Vector3.Dot(Vector3.forward, direction); + if (Mathf.Abs(dotEast) > Mathf.Abs(dotNorth)) + { + // Wall is east/west of Agent + if(dotEast < 0) + n_iw = Wall.transform.right; + else + n_iw = -Wall.transform.right; + } + else + { + // Wall is north/south of Agent + if(dotNorth < 0) + n_iw = Wall.transform.forward; + else + n_iw = -Wall.transform.forward; + // Will be negative if south, positive if North + } + Vector3 t_iw = new Vector3(-n_iw.z, n_iw.x, 0); + + repForce = (A * Mathf.Exp((r_i - d_iw)/B) + kappa*g) * n_iw; + slideForce = k * g * Vector3.Dot(GetVelocity(), t_iw) * t_iw; + WallForce += (repForce - slideForce); + } + return WallForce; + } + + //OnTriggerExit is not called if the GameObject is deactivated or destroyed. + //Function will clean up perceivedNeighbors that were deactivated while still in range of Agent. + private void CleanNeighbors(){ + HashSet excludeNeighbors = new HashSet(); + foreach(GameObject neighbor in perceivedNeighbors){ + if(!neighbor.activeSelf) + excludeNeighbors.Add(neighbor); + } + perceivedNeighbors.ExceptWith(excludeNeighbors); + } + + public void ApplyForce() + { + var force = ComputeForce(); + force.y = 0; + rb.AddForce(force * 10, ForceMode.Force); + } + + public Vector3 GetLastDirection(){ + return ei_0; + } + + public void OnTriggerEnter(Collider other) + { + if(AgentManager.IsAgent(other.gameObject)){ + perceivedNeighbors.Add(other.gameObject); + } else if(WallManager.IsWall(other.gameObject)){ + perceivedWalls.Add(other.gameObject); + } + } + + public void OnTriggerExit(Collider other) + { + if(AgentManager.IsAgent(other.gameObject)){ + perceivedNeighbors.Remove(other.gameObject); + } else if(WallManager.IsWall(other.gameObject)){ + perceivedWalls.Remove(other.gameObject); + } + } + + public void OnCollisionEnter(Collision collision) + { + /* + if(WallManager.IsWall(collision.transform.gameObject)){ + perceivedWalls.Add(collision.transform.gameObject); + }*/ + } + + public void OnCollisionExit(Collision collision) + { + /* + if(WallManager.IsWall(collision.transform.gameObject)){ + perceivedWalls.Remove(collision.transform.gameObject); + } + */ + } + + #endregion +} diff --git a/B4 Scripts/Agent.cs.meta b/B4 Scripts/Agent.cs.meta new file mode 100644 index 0000000..f7907ce --- /dev/null +++ b/B4 Scripts/Agent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 12450463083bdd140a88af41456e0a5f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B4 Scripts/AgentManager.cs b/B4 Scripts/AgentManager.cs new file mode 100644 index 0000000..27b34b6 --- /dev/null +++ b/B4 Scripts/AgentManager.cs @@ -0,0 +1,186 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.AI; + +public class AgentManager : MonoBehaviour +{ + public int agentCount = 10; + public float agentSpawnRadius = 20; + public GameObject agentPrefab; + public static Dictionary agentsObjs = new Dictionary(); + + private static List agents = new List(); + private GameObject agentParent; + private Vector3 destination; + + public const float UPDATE_RATE = 0.0f; + private const int PATHFINDING_FRAME_SKIP = 25; + + public bool spiralFlag = false; + public bool flocking = true; + + public float CrowdFollow_Param = 0f; + + #region Unity Functions + + void Awake() + { + Random.InitState(0); + + agentParent = GameObject.Find("Agents"); + for (int i = 0; i < agentCount; i++) + { + var randPos = new Vector3((Random.value - 0.5f) * agentSpawnRadius, 0, (Random.value - 0.5f) * agentSpawnRadius); + NavMeshHit hit; + NavMesh.SamplePosition(randPos, out hit, 10, NavMesh.AllAreas); + randPos = hit.position + Vector3.up; + + GameObject agent = null; + agent = Instantiate(agentPrefab, randPos, Quaternion.identity); + agent.name = "Agent " + i; + agent.transform.parent = agentParent.transform; + var agentScript = agent.GetComponent(); + agentScript.radius = 0.3f;// Random.Range(0.2f, 0.6f); + agentScript.mass = 1; + agentScript.perceptionRadius = 3; + if(spiralFlag){ + agentScript.spiralFlag = true; + } + if (!flocking) + { + agentScript.flocking = false; + } + agentScript.crowdFollowParam = CrowdFollow_Param; + agents.Add(agentScript); + agentsObjs.Add(agent, agentScript); + } + + StartCoroutine(Run()); + } + + void Update() + { + #region Visualization + + if (Input.GetMouseButtonDown(0)) + { + if (true) + { + var point = Camera.main.ScreenToWorldPoint(Input.mousePosition + Vector3.forward * 10); + var dir = point - Camera.main.transform.position; + RaycastHit rcHit; + if (Physics.Raycast(point, dir, out rcHit)) + { + NavMeshHit navHit; + if (NavMesh.SamplePosition(rcHit.point, out navHit, 1.0f, NavMesh.AllAreas)) + { + destination = navHit.position; + } + } + } else + { + var randPos = new Vector3((Random.value - 0.5f) * agentSpawnRadius, 0, (Random.value - 0.5f) * agentSpawnRadius); + + NavMeshHit hit; + NavMesh.SamplePosition(randPos, out hit, 1.0f, NavMesh.AllAreas); + print(hit.position); + Debug.DrawLine(hit.position, hit.position + Vector3.up * 10, Color.red, 1000000); + foreach (var agent in agents) + { + //agent.ComputePath(hit.position); + } + } + } + +#if UNITY_EDITOR + if (Application.isFocused) + { + //UnityEditor.SceneView.FocusWindowIfItsOpen(typeof(UnityEditor.SceneView)); + } +#endif + + #endregion + } + + IEnumerator Run() + { + yield return null; + + for (int iterations = 0; ; iterations++) + { + if (iterations % PATHFINDING_FRAME_SKIP == 0) + { + if(!spiralFlag){ + SetAgentDestinations(destination); + } + } + + foreach (var agent in agents) + { + agent.ApplyForce(); + } + + if (UPDATE_RATE == 0) + { + yield return null; + } else + { + yield return new WaitForSeconds(UPDATE_RATE); + } + } + } + + #endregion + + #region Public Functions + + public static bool IsAgent(GameObject obj) + { + return agentsObjs.ContainsKey(obj); + } + + public void SetAgentDestinations(Vector3 NewDestination) + { + destination = NewDestination; + NavMeshHit hit; + NavMesh.SamplePosition(NewDestination, out hit, 10, NavMesh.AllAreas); + foreach (var agent in agents) + { + agent.ComputePath(hit.position); + } + } + + public static void RemoveAgent(GameObject obj) + { + var agent = obj.GetComponent(); + + agents.Remove(agent); + agentsObjs.Remove(obj); + } + #endregion + + #region Private Functions + + #endregion + + #region Visualization Functions + + #endregion + + #region Utility Classes + + private class Tuple + { + public K Item1; + public V Item2; + + public Tuple(K k, V v) { + Item1 = k; + Item2 = v; + } + } + + #endregion +} diff --git a/B4 Scripts/AgentManager.cs.meta b/B4 Scripts/AgentManager.cs.meta new file mode 100644 index 0000000..fb478cc --- /dev/null +++ b/B4 Scripts/AgentManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 27f617c0ea9733b44b50c37923d5c7d4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B4 Scripts/CameraController.cs b/B4 Scripts/CameraController.cs new file mode 100644 index 0000000..52899a4 --- /dev/null +++ b/B4 Scripts/CameraController.cs @@ -0,0 +1,31 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class CameraController : MonoBehaviour +{ + public float moveSpeed = 10f; + public float sensitivity = 2f; + public float camSpeed = -0.5f; + public GameObject Manager; + // Start is called before the first frame update + void Start() + { + + } + + // Update is called once per frame + void LateUpdate() + { + Cursor.visible = false; + Cursor.lockState = CursorLockMode.Locked; + transform.position = transform.position + Vector3.ProjectOnPlane(transform.forward, Vector3.up) * Input.GetAxis("Vertical") *moveSpeed*Time.deltaTime; + transform.position = transform.position + Vector3.ProjectOnPlane(transform.right, Vector3.up) * Input.GetAxis("Horizontal") *moveSpeed*Time.deltaTime; + float x = Input.GetAxis("Mouse X"); + float y = Input.GetAxis("Mouse Y"); + Vector3 rotateValue = new Vector3(y, x * -1, 0); + transform.eulerAngles = transform.eulerAngles - rotateValue; + transform.eulerAngles += rotateValue * camSpeed; + transform.position = transform.position + transform.up * Input.mouseScrollDelta.y * moveSpeed * Time.deltaTime; + } +} diff --git a/B4 Scripts/CameraController.cs.meta b/B4 Scripts/CameraController.cs.meta new file mode 100644 index 0000000..c5ac0bf --- /dev/null +++ b/B4 Scripts/CameraController.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d717c133eacc19346983eff03b856720 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B4 Scripts/EvadeAgent.cs b/B4 Scripts/EvadeAgent.cs new file mode 100644 index 0000000..6c29f37 --- /dev/null +++ b/B4 Scripts/EvadeAgent.cs @@ -0,0 +1,254 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.AI; + +public class EvadeAgent : MonoBehaviour +{ + public float radius; + public float mass; + public float perceptionRadius; + public float goalWeight = 5; + public float goalForceSpeed; + + public List pursueAgents = new List(); + + float A = 3.1f; + float B = 0.8f; + float k = 1.2f; + float kappa = 3.1f; + + private Vector3 direction; + private bool returnHome = false; + private Vector3 homeDestination = new Vector3(0,1,0); + private List path; + private NavMeshAgent nma; + private Rigidbody rb; + private SphereCollider sc; + private HashSet perceivedNeighbors = new HashSet(); + private HashSet perceivedWalls = new HashSet(); + + private int FRAME; + private const int PATHFINDING_FRAME_SKIP = 25; + + void Start() + { + //destination = Vector3.zero; + path = new List(); + nma = GetComponent(); + rb = GetComponent(); + sc = GetComponent(); + FRAME = 0; + goalForceSpeed = 3.5f; + radius = 0.3f; + mass = 1; + perceptionRadius = 3; + + gameObject.transform.localScale = new Vector3(2 * radius, 1, 2 * radius); + nma.radius = radius; + rb.mass = mass; + GetComponent().radius = perceptionRadius / 2; + } + + void FixedUpdate() + { + + if (returnHome && Vector3.Distance(transform.position, homeDestination) > 7f) + { + + }else{ + returnHome = false; + if(FRAME % PATHFINDING_FRAME_SKIP == 0){ + float minDistance = Mathf.Infinity; + Vector3 closestPursuer = Vector3.zero; + + foreach(GameObject pursuer in pursueAgents){ + float d = Vector3.Distance(pursuer.transform.position, transform.position); + if(d < minDistance){ + minDistance = d; + closestPursuer = pursuer.transform.position; + } + } + direction = closestPursuer; + /* + Vector3 oppositeDirection = closestPursuer * 5; + NavMeshHit navHit; + Vector3 dest = Vector3.zero; + if (NavMesh.SamplePosition(oppositeDirection + transform.position, out navHit, 7.0f, NavMesh.AllAreas)) + { + dest = navHit.position; + }else{ + dest = new Vector3(0f, 1f, 0f); + } + /* + ComputePath(destination); + */ + //path.Add(dest); + FRAME = 0; + }else{ + FRAME++; + } + } + ApplyForce(); + } + + public void ComputePath(Vector3 destination) + { + nma.enabled = true; + var nmPath = new NavMeshPath(); + nma.CalculatePath(destination, nmPath); + path = nmPath.corners.Skip(1).ToList(); + goalForceSpeed = nma.speed; + nma.enabled = false; + } + + public Vector3 GetVelocity() + { + return rb.velocity; + } + + private Vector3 ComputeForce() + { + Vector3 gForce = CalculateGoalForce(); + Vector3 aForce = CalculateAgentForce(); + Vector3 wForce = CalculateWallForce(); + + if(Vector3.Angle(gForce, aForce) == 0 && wForce != Vector3.zero && aForce != Vector3.zero){ + returnHome = true; + } + + Debug.DrawLine(transform.position, transform.position + gForce, Color.red, 0.5f); + Debug.DrawLine(transform.position, transform.position + aForce, Color.green, 0.5f); + Debug.DrawLine(transform.position, transform.position + wForce, Color.blue, 0.5f); + + return gForce + aForce + wForce; + } + + private Vector3 CalculateGoalForce() + { + // calculate the path to the destination and take the first two corners. + // return the unit vector in that direction and multiplied by a constant scalar. + Vector3 curPath = Vector3.zero; + if(returnHome){ + curPath = (homeDestination - transform.position).normalized; + }else{ + curPath = (transform.position - direction).normalized; + } + //Return the (Vi_0 * ei_0) - Vi + return ((goalForceSpeed * curPath) - GetVelocity()); + } + + private Vector3 CalculateAgentForce() + { + Vector3 agentForce = Vector3.zero; + foreach (GameObject neighbor in perceivedNeighbors) + { + Vector3 repForce = Vector3.zero; + Vector3 slideForce = Vector3.zero; + Vector3 opos = neighbor.transform.position; + Rigidbody oagent = neighbor.GetComponent(); + + float r_ij = 2 * radius; + float d_ij = Vector3.Distance(opos,transform.position); + float g = (r_ij - d_ij > 0.000001) ? (r_ij- d_ij) : 0; + Vector3 n_ij = (transform.position - opos)/d_ij; + Vector3 t_ij = new Vector3(-n_ij.z, n_ij.x, 0); + float deltaV_ji = Vector3.Dot((oagent.velocity - GetVelocity()), t_ij); + + repForce = (A * Mathf.Exp((r_ij - d_ij)/B) + kappa*g) * n_ij; + slideForce = k * g * deltaV_ji * t_ij; + + agentForce += (repForce + slideForce); + } + + return agentForce; + } + + private Vector3 CalculateWallForce() + { + + // Forces on the agent will be perpendicular to the walls closest surface + Vector3 WallForce = Vector3.zero; + foreach (GameObject Wall in perceivedWalls) + { + Vector3 repForce = Vector3.zero; + Vector3 slideForce = Vector3.zero; + + float r_i = radius; + float d_iw = Vector3.Distance(Wall.transform.position, transform.position); + float g = (r_i - d_iw > 0.000001) ? (r_i - d_iw) : 0; + Vector3 n_iw = Vector3.zero; + + Vector3 direction = (Wall.transform.position - rb.transform.position).normalized; + float dotEast = Vector3.Dot(transform.right, direction); + float dotNorth = Vector3.Dot(transform.forward, direction); + if (Mathf.Abs(dotEast) > Mathf.Abs(dotNorth)) + { + // Wall is east/west of Agent + if(dotEast < 0) + n_iw = Wall.transform.right; // Will be negative if west, positive if east //The inverse by dividing by a number means the force gets stronger as the agent is closer to the wall + else + n_iw = -Wall.transform.right; + } + else + { + // Wall is north/south of Agent + if(dotNorth < 0) + n_iw = Wall.transform.forward; + else + n_iw = -Wall.transform.forward; + // Will be negative if south, positive if North + } + Vector3 t_iw = new Vector3(-n_iw.z, n_iw.x, 0); + + repForce = (A * Mathf.Exp((r_i - d_iw)/B) + kappa*g) * n_iw; + slideForce = k * g * Vector3.Dot(GetVelocity(), t_iw) * t_iw; + WallForce += (repForce - slideForce); + } + return WallForce; + } + + public void ApplyForce() + { + var force = ComputeForce(); + force.y = 0; + rb.AddForce(force * 10, ForceMode.Force); + } + + public void OnTriggerEnter(Collider other) + { + if(other.gameObject.CompareTag("Agent")){ + perceivedNeighbors.Add(other.gameObject); + } else if(WallManager.IsWall(other.gameObject)){ + perceivedWalls.Add(other.gameObject); + } + } + + public void OnTriggerExit(Collider other) + { + if(other.gameObject.CompareTag("Agent")){ + perceivedNeighbors.Remove(other.gameObject); + } else if(WallManager.IsWall(other.gameObject)){ + perceivedWalls.Remove(other.gameObject); + } + } + + public void OnCollisionEnter(Collision collision) + { + /* + if(WallManager.IsWall(collision.transform.gameObject)){ + perceivedWalls.Add(collision.transform.gameObject); + } + */ + } + + public void OnCollisionExit(Collision collision) + { + /* + if(WallManager.IsWall(collision.transform.gameObject)){ + perceivedWalls.Remove(collision.transform.gameObject); + } + */ + } +} diff --git a/B4 Scripts/EvadeAgent.cs.meta b/B4 Scripts/EvadeAgent.cs.meta new file mode 100644 index 0000000..ce32321 --- /dev/null +++ b/B4 Scripts/EvadeAgent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8737b03060d9446f19636a5c3754805b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B4 Scripts/FollowerAgent.cs b/B4 Scripts/FollowerAgent.cs new file mode 100644 index 0000000..a63f739 --- /dev/null +++ b/B4 Scripts/FollowerAgent.cs @@ -0,0 +1,216 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.AI; + +public class FollowerAgent : MonoBehaviour +{ + public float radius; + public float mass; + public float perceptionRadius; + public float goalWeight = 5; + public float goalForceSpeed; + + float A = 2.0f; + float B = 0.6f; + float k = 1.2f; + float kappa = 2.4f; + + private List path; + private NavMeshAgent nma; + private Rigidbody rb; + private SphereCollider sc; + private HashSet perceivedNeighbors = new HashSet(); + private HashSet perceivedWalls = new HashSet(); + + void Start() + { + path = new List(); + nma = GetComponent(); + rb = GetComponent(); + sc = GetComponent(); + + gameObject.transform.localScale = new Vector3(2 * radius, 1, 2 * radius); + nma.radius = radius; + rb.mass = mass; + GetComponent().radius = perceptionRadius / 2; + } + + private void Update() + { + if (path.Count > 0 && Vector3.Distance(transform.position, path[0]) < 2.1f) + { + path.RemoveAt(0); + } + } + + #region Public Functions + + public void ComputePath(Vector3 destination) + { + nma.enabled = true; + var nmPath = new NavMeshPath(); + nma.CalculatePath(destination, nmPath); + path = nmPath.corners.Skip(1).ToList(); + goalForceSpeed = nma.speed; + nma.enabled = false; + } + + public Vector3 GetVelocity() + { + return rb.velocity; + } + + #endregion + + #region Incomplete Functions + + private Vector3 ComputeForce() + { + Vector3 gForce = CalculateGoalForce(); + + CleanNeighbors(); + Vector3 aForce = CalculateAgentForce(); + + Vector3 wForce = CalculateWallForce(); + + Debug.DrawLine(transform.position, transform.position + gForce, Color.red, 0.5f); + Debug.DrawLine(transform.position, transform.position + aForce, Color.green, 0.5f); + Debug.DrawLine(transform.position, transform.position + wForce, Color.blue, 0.5f); + + return gForce + aForce + wForce; + } + + private Vector3 CalculateGoalForce() + { + // calculate the path to the destination and take the first two corners. + // return the unit vector in that direction and multiplied by a constant scalar. + if(path.Count == 0) + return Vector3.zero - GetVelocity(); + + Vector3 ei_0 = (new Vector3(path[0].x, transform.position.y, path[0].z) - transform.position).normalized; + //Return the (Vi_0 * ei_0) - Vi + return ((goalForceSpeed * ei_0) - GetVelocity()); + } + + private Vector3 CalculateAgentForce() + { + Vector3 agentForce = Vector3.zero; + foreach (GameObject neighbor in perceivedNeighbors) + { + Vector3 repForce = Vector3.zero; + Vector3 slideForce = Vector3.zero; + Vector3 opos = neighbor.transform.position; + float oRadius = 0f; + Vector3 oVelocity = Vector3.zero; + + if(neighbor.GetComponent()){ + FollowerAgent oagent = neighbor.GetComponent(); + oRadius = oagent.radius; + oVelocity = oagent.GetVelocity(); + } + else if(neighbor.GetComponent()){ + LeaderAgent oLeader = neighbor.GetComponent(); + oRadius = oLeader.radius; + oVelocity = oLeader.GetVelocity(); + } + + float r_ij = oRadius + radius; + float d_ij = Vector3.Distance(opos,transform.position); + float g = (r_ij - d_ij > 0.000001) ? (r_ij- d_ij) : 0; + Vector3 n_ij = (transform.position - opos)/d_ij; + Vector3 t_ij = new Vector3(-n_ij.z, n_ij.x, 0); + float deltaV_ji = Vector3.Dot((oVelocity - GetVelocity()), t_ij); + + repForce = (A * Mathf.Exp((r_ij - d_ij)/B) + kappa*g) * n_ij; + slideForce = k * g * deltaV_ji * t_ij; + + agentForce += (repForce + slideForce); + } + + return agentForce; + } + + private Vector3 CalculateWallForce() + { + + // Forces on the agent will be perpendicular to the walls closest surface + Vector3 WallForce = Vector3.zero; + foreach (GameObject Wall in perceivedWalls) + { + Vector3 repForce = Vector3.zero; + Vector3 slideForce = Vector3.zero; + + float r_i = radius; + float d_iw = Vector3.Distance(Wall.transform.position, transform.position); + float g = (r_i - d_iw > 0.000001) ? (r_i - d_iw) : 0; + Vector3 n_iw = Vector3.zero; + + Vector3 direction = (Wall.transform.position - rb.transform.position).normalized; + float dotEast = Vector3.Dot(Vector3.right, direction); + float dotNorth = Vector3.Dot(Vector3.forward, direction); + if (Mathf.Abs(dotEast) > Mathf.Abs(dotNorth)) + { + // Wall is east/west of Agent + if(dotEast < 0) + n_iw = Wall.transform.right; // Will be negative if west, positive if east //The inverse by dividing by a number means the force gets stronger as the agent is closer to the wall + else + n_iw = -Wall.transform.right; + } + else + { + // Wall is north/south of Agent + if(dotNorth < 0) + n_iw = Wall.transform.forward; + else + n_iw = -Wall.transform.forward; + // Will be negative if south, positive if North + } + Vector3 t_iw = new Vector3(-n_iw.z, n_iw.x, 0); + + repForce = (A * Mathf.Exp((r_i - d_iw)/B) + kappa*g) * n_iw; + slideForce = k * g * Vector3.Dot(GetVelocity(), t_iw) * t_iw; + WallForce += (repForce - slideForce); + } + return WallForce; + } + + //OnTriggerExit is not called if the GameObject is deactivated or destroyed. + //Function will clean up perceivedNeighbors that were deactivated while still in range of Agent. + private void CleanNeighbors(){ + HashSet excludeNeighbors = new HashSet(); + foreach(GameObject neighbor in perceivedNeighbors){ + if(!neighbor.activeSelf) + excludeNeighbors.Add(neighbor); + } + perceivedNeighbors.ExceptWith(excludeNeighbors); + } + + public void ApplyForce() + { + var force = ComputeForce(); + force.y = 0; + rb.AddForce(force * 10, ForceMode.Force); + } + + public void OnTriggerEnter(Collider other) + { + if(other.gameObject.CompareTag("Agent")){ + perceivedNeighbors.Add(other.gameObject); + } else if(WallManager.IsWall(other.gameObject)){ + perceivedWalls.Add(other.gameObject); + } + } + + public void OnTriggerExit(Collider other) + { + if(other.gameObject.CompareTag("Agent")){ + perceivedNeighbors.Remove(other.gameObject); + } else if(WallManager.IsWall(other.gameObject)){ + perceivedWalls.Remove(other.gameObject); + } + } + + #endregion +} diff --git a/B4 Scripts/FollowerAgent.cs.meta b/B4 Scripts/FollowerAgent.cs.meta new file mode 100644 index 0000000..e243447 --- /dev/null +++ b/B4 Scripts/FollowerAgent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0204925323c2245288622f91421600ed +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B4 Scripts/LFAgentManager.cs b/B4 Scripts/LFAgentManager.cs new file mode 100644 index 0000000..11f10f8 --- /dev/null +++ b/B4 Scripts/LFAgentManager.cs @@ -0,0 +1,188 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.AI; + +public class LFAgentManager : MonoBehaviour +{ + public int agentCount = 10; + public float agentSpawnRadius = 20; + public GameObject agentPrefab; + public GameObject leaderPrefab; + public static Dictionary agentsObjs = new Dictionary(); + + private LeaderAgent leader; + private static List agents = new List(); + private GameObject agentParent; + private Vector3 destination; + + public const float UPDATE_RATE = 0.0f; + private const int PATHFINDING_FRAME_SKIP = 25; + + public bool spiralFlag = false; + public float CrowdFollow_Param = 0f; + + #region Unity Functions + + void Awake() + { + Random.InitState(0); + + agentParent = GameObject.Find("Agents"); + for (int i = 0; i < agentCount; i++) + { + var randPos = new Vector3((Random.value - 0.5f) * agentSpawnRadius, 0, (Random.value - 0.5f) * agentSpawnRadius); + NavMeshHit hit; + NavMesh.SamplePosition(randPos, out hit, 10, NavMesh.AllAreas); + randPos = hit.position + Vector3.up; + + GameObject agent = null; + agent = Instantiate(agentPrefab, randPos, Quaternion.identity); + agent.name = "Agent " + i; + agent.transform.parent = agentParent.transform; + var agentScript = agent.GetComponent(); + agentScript.radius = 0.3f;// Random.Range(0.2f, 0.6f); + agentScript.mass = 1; + agentScript.perceptionRadius = 3; + agent.SetActive(true); + agents.Add(agentScript); + agentsObjs.Add(agent, agentScript); + } + + leader = leaderPrefab.GetComponent(); + + StartCoroutine(Run()); + } + + void Update() + { + #region Visualization + + if (Input.GetMouseButtonDown(0)) + { + if (true) + { + var point = Camera.main.ScreenToWorldPoint(Input.mousePosition + Vector3.forward * 10); + var dir = point - Camera.main.transform.position; + RaycastHit rcHit; + if (Physics.Raycast(point, dir, out rcHit)) + { + NavMeshHit navHit; + if (NavMesh.SamplePosition(rcHit.point, out navHit, 1.0f, NavMesh.AllAreas)) + { + SetLeaderDestination(navHit.position); + } + } + } else + { + var randPos = new Vector3((Random.value - 0.5f) * agentSpawnRadius, 0, (Random.value - 0.5f) * agentSpawnRadius); + + NavMeshHit hit; + NavMesh.SamplePosition(randPos, out hit, 1.0f, NavMesh.AllAreas); + print(hit.position); + Debug.DrawLine(hit.position, hit.position + Vector3.up * 10, Color.red, 1000000); + foreach (var agent in agents) + { + //agent.ComputePath(hit.position); + } + } + } + +#if UNITY_EDITOR + if (Application.isFocused) + { + //UnityEditor.SceneView.FocusWindowIfItsOpen(typeof(UnityEditor.SceneView)); + } +#endif + + #endregion + } + + IEnumerator Run() + { + yield return null; + + for (int iterations = 0; ; iterations++) + { + if (iterations % PATHFINDING_FRAME_SKIP == 0) + { + SetAgentDestinations(leaderPrefab.transform.position); + } + + foreach (var agent in agents) + { + agent.ApplyForce(); + } + + leader.ApplyForce(); + + if (UPDATE_RATE == 0) + { + yield return null; + } else + { + yield return new WaitForSeconds(UPDATE_RATE); + } + } + } + + #endregion + + #region Public Functions + + public static bool IsAgent(GameObject obj) + { + return agentsObjs.ContainsKey(obj); + } + + public void SetLeaderDestination(Vector3 NewDestination){ + NavMeshHit hit; + NavMesh.SamplePosition(NewDestination, out hit, 10, NavMesh.AllAreas); + leader.ComputePath(hit.position); + } + + public void SetAgentDestinations(Vector3 NewDestination) + { + destination = NewDestination; + NavMeshHit hit; + NavMesh.SamplePosition(NewDestination, out hit, 10, NavMesh.AllAreas); + foreach (var agent in agents) + { + agent.ComputePath(hit.position); + } + + } + + public static void RemoveAgent(GameObject obj) + { + var agent = obj.GetComponent(); + + agents.Remove(agent); + agentsObjs.Remove(obj); + } + #endregion + + #region Private Functions + + #endregion + + #region Visualization Functions + + #endregion + + #region Utility Classes + + private class Tuple + { + public K Item1; + public V Item2; + + public Tuple(K k, V v) { + Item1 = k; + Item2 = v; + } + } + + #endregion +} diff --git a/B4 Scripts/LFAgentManager.cs.meta b/B4 Scripts/LFAgentManager.cs.meta new file mode 100644 index 0000000..b8c6c03 --- /dev/null +++ b/B4 Scripts/LFAgentManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2aa5ba0f87d084d719bd53f7d7bd4e46 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B4 Scripts/LeaderAgent.cs b/B4 Scripts/LeaderAgent.cs new file mode 100644 index 0000000..a5249b7 --- /dev/null +++ b/B4 Scripts/LeaderAgent.cs @@ -0,0 +1,219 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.AI; + +public class LeaderAgent : MonoBehaviour +{ + public float radius; + public float mass; + public float perceptionRadius; + public float goalWeight = 5; + public float goalForceSpeed; + + float A = 2.0f; + float B = 0.6f; + float k = 1.2f; + float kappa = 2.4f; + + private List path; + private NavMeshAgent nma; + private Rigidbody rb; + private SphereCollider sc; + private HashSet perceivedNeighbors = new HashSet(); + private HashSet perceivedWalls = new HashSet(); + + void Start() + { + path = new List(); + nma = GetComponent(); + rb = GetComponent(); + sc = GetComponent(); + + gameObject.transform.localScale = new Vector3(2 * radius, 1, 2 * radius); + nma.radius = radius; + rb.mass = mass; + GetComponent().radius = perceptionRadius / 2; + } + + private void Update() + { + if (path.Count > 0 && Vector3.Distance(transform.position, path[0]) < 1.1f) + { + path.RemoveAt(0); + } + } + + #region Public Functions + + public void ComputePath(Vector3 destination) + { + nma.enabled = true; + var nmPath = new NavMeshPath(); + nma.CalculatePath(destination, nmPath); + path = nmPath.corners.Skip(1).ToList(); + goalForceSpeed = nma.speed; + nma.enabled = false; + } + + public Vector3 GetVelocity() + { + return rb.velocity; + } + + #endregion + + #region Incomplete Functions + + private Vector3 ComputeForce() + { + Vector3 gForce = CalculateGoalForce(); + + Vector3 wForce = CalculateWallForce(); + + Debug.DrawLine(transform.position, transform.position + gForce, Color.red, 0.5f); + Debug.DrawLine(transform.position, transform.position + wForce, Color.blue, 0.5f); + + return gForce + wForce; + } + + private Vector3 CalculateGoalForce() + { + // calculate the path to the destination and take the first two corners. + // return the unit vector in that direction and multiplied by a constant scalar. + if(path.Count == 0) + return Vector3.zero - GetVelocity(); + Vector3 ei_0 = (new Vector3(path[0].x, transform.position.y, path[0].z) - transform.position).normalized; + //Return the (Vi_0 * ei_0) - Vi + return ((goalForceSpeed * ei_0) - GetVelocity()); + } + /* + private Vector3 CalculateAgentForce() + { + Vector3 agentForce = Vector3.zero; + foreach (GameObject neighbor in perceivedNeighbors) + { + Vector3 repForce = Vector3.zero; + Vector3 slideForce = Vector3.zero; + Vector3 opos = neighbor.transform.position; + Agent oagent = neighbor.GetComponent(); + + float r_ij = oagent.radius + radius; + float d_ij = Vector3.Distance(opos,transform.position); + float g = (r_ij - d_ij > 0.000001) ? (r_ij- d_ij) : 0; + Vector3 n_ij = (transform.position - opos)/d_ij; + Vector3 t_ij = new Vector3(-n_ij.z, n_ij.x, 0); + float deltaV_ji = Vector3.Dot((oagent.GetVelocity() - GetVelocity()), t_ij); + + repForce = (A * Mathf.Exp((r_ij - d_ij)/B) + kappa*g) * n_ij; + slideForce = k * g * deltaV_ji * t_ij; + + agentForce += (repForce + slideForce); + } + + return agentForce; + } + */ + private Vector3 CalculateWallForce() + { + + // Forces on the agent will be perpendicular to the walls closest surface + Vector3 WallForce = Vector3.zero; + foreach (GameObject Wall in perceivedWalls) + { + Vector3 repForce = Vector3.zero; + Vector3 slideForce = Vector3.zero; + + float r_i = radius; + float d_iw = Vector3.Distance(Wall.transform.position, transform.position); + float g = (r_i - d_iw > 0.000001) ? (r_i - d_iw) : 0; + Vector3 n_iw = Vector3.zero; + + Vector3 direction = (Wall.transform.position - rb.transform.position).normalized; + float dotEast = Vector3.Dot(Vector3.right, direction); + float dotNorth = Vector3.Dot(Vector3.forward, direction); + if (Mathf.Abs(dotEast) > Mathf.Abs(dotNorth)) + { + // Wall is east/west of Agent + if(dotEast < 0) + n_iw = Wall.transform.right; // Will be negative if west, positive if east //The inverse by dividing by a number means the force gets stronger as the agent is closer to the wall + else + n_iw = -Wall.transform.right; + } + else + { + // Wall is north/south of Agent + if(dotNorth < 0) + n_iw = Wall.transform.forward; + else + n_iw = -Wall.transform.forward; + // Will be negative if south, positive if North + } + Vector3 t_iw = new Vector3(-n_iw.z, n_iw.x, 0); + + repForce = (A * Mathf.Exp((r_i - d_iw)/B) + kappa*g) * n_iw; + slideForce = k * g * Vector3.Dot(GetVelocity(), t_iw) * t_iw; + WallForce += (repForce - slideForce); + } + return WallForce; + } + + /* + //OnTriggerExit is not called if the GameObject is deactivated or destroyed. + //Function will clean up perceivedNeighbors that were deactivated while still in range of Agent. + private void CleanNeighbors(){ + HashSet excludeNeighbors = new HashSet(); + foreach(GameObject neighbor in perceivedNeighbors){ + if(!neighbor.activeSelf) + excludeNeighbors.Add(neighbor); + } + perceivedNeighbors.ExceptWith(excludeNeighbors); + } + */ + public void ApplyForce() + { + var force = ComputeForce(); + force.y = 0; + rb.AddForce(force * 10, ForceMode.Force); + } + + public void OnTriggerEnter(Collider other) + { /* + if(AgentManager.IsAgent(other.gameObject)){ + perceivedNeighbors.Add(other.gameObject); + } else*/ + if(WallManager.IsWall(other.gameObject)){ + perceivedWalls.Add(other.gameObject); + } + } + + public void OnTriggerExit(Collider other) + { /* + if(AgentManager.IsAgent(other.gameObject)){ + perceivedNeighbors.Remove(other.gameObject); + } else */ + if(WallManager.IsWall(other.gameObject)){ + perceivedWalls.Remove(other.gameObject); + } + } + + public void OnCollisionEnter(Collision collision) + { + /* + if(WallManager.IsWall(collision.transform.gameObject)){ + perceivedWalls.Add(collision.transform.gameObject); + }*/ + } + + public void OnCollisionExit(Collision collision) + { + /* + if(WallManager.IsWall(collision.transform.gameObject)){ + perceivedWalls.Remove(collision.transform.gameObject); + } + */ + } + + #endregion +} diff --git a/B4 Scripts/LeaderAgent.cs.meta b/B4 Scripts/LeaderAgent.cs.meta new file mode 100644 index 0000000..c751394 --- /dev/null +++ b/B4 Scripts/LeaderAgent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ecbc8d03e751e4af09660ff04f57f205 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B4 Scripts/Parameters.cs b/B4 Scripts/Parameters.cs new file mode 100644 index 0000000..beb8bd9 --- /dev/null +++ b/B4 Scripts/Parameters.cs @@ -0,0 +1,19 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public struct Parameters +{ + public const float T = 0.5f; + public const float A = 2000f; + public const float B = 0.08f; + public const float k = 1.2f * 100000f; + public const float Kappa = 2.4f * 100000f; + + public const float WALL_A = 2000f; + public const float WALL_B = 0.08f; + public const float WALL_k = 1.2f * 100000f; + public const float WALL_Kappa = 2.4f * 100000f; + + public const float maxSpeed = 100; +} \ No newline at end of file diff --git a/B4 Scripts/Parameters.cs.meta b/B4 Scripts/Parameters.cs.meta new file mode 100644 index 0000000..08074b5 --- /dev/null +++ b/B4 Scripts/Parameters.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ba3db8e2c60768a46aae8987cdd2ac93 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B4 Scripts/PursueAgent.cs b/B4 Scripts/PursueAgent.cs new file mode 100644 index 0000000..84707d0 --- /dev/null +++ b/B4 Scripts/PursueAgent.cs @@ -0,0 +1,227 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.AI; + +public class PursueAgent : MonoBehaviour +{ + public float radius; + public float mass; + public float perceptionRadius; + public float goalWeight = 5; + public float goalForceSpeed; + + public List evadeAgents = new List(); + + float A = 3.1f; + float B = 0.8f; + float k = 1.2f; + float kappa = 5.4f; + + private Vector3 destination; + private List path; + private NavMeshAgent nma; + private Rigidbody rb; + private SphereCollider sc; + private HashSet perceivedNeighbors = new HashSet(); + private HashSet perceivedWalls = new HashSet(); + + private int FRAME; + private const int PATHFINDING_FRAME_SKIP = 25; + + void Start() + { + destination = Vector3.zero; + path = new List(); + nma = GetComponent(); + rb = GetComponent(); + sc = GetComponent(); + FRAME = 0; + + radius = 0.3f; + mass = 1; + perceptionRadius = 3; + + gameObject.transform.localScale = new Vector3(2 * radius, 1, 2 * radius); + nma.radius = radius; + rb.mass = mass; + GetComponent().radius = perceptionRadius / 2; + } + + void FixedUpdate() + { + if (path.Count > 0 && Vector3.Distance(transform.position, path[0]) < 1.1f) + { + path.RemoveAt(0); + } + + if(path.Count == 0){ + float minDistance = Mathf.Infinity; + Vector3 closestTarget = Vector3.zero; + + foreach(GameObject evader in evadeAgents){ + float d = Vector3.Distance(evader.transform.position, transform.position); + if(d < minDistance){ + minDistance = d; + closestTarget = evader.transform.position; + } + } + closestTarget.y = 0; + destination = closestTarget; + + ComputePath(destination); + }else{ + FRAME++; + } + + ApplyForce(); + } + + public void ComputePath(Vector3 destination) + { + nma.enabled = true; + var nmPath = new NavMeshPath(); + nma.CalculatePath(destination, nmPath); + path = nmPath.corners.Skip(1).ToList(); + goalForceSpeed = nma.speed; + nma.enabled = false; + } + + public Vector3 GetVelocity() + { + return rb.velocity; + } + + private Vector3 ComputeForce() + { + Vector3 gForce = CalculateGoalForce(); + Vector3 aForce = CalculateAgentForce(); + Vector3 wForce = CalculateWallForce(); + + Debug.DrawLine(transform.position, transform.position + gForce, Color.red, 0.5f); + Debug.DrawLine(transform.position, transform.position + aForce, Color.green, 0.5f); + Debug.DrawLine(transform.position, transform.position + wForce, Color.blue, 0.5f); + Debug.DrawLine(transform.position, transform.position + (gForce+aForce+wForce), Color.black, 0.5f); + + return (gForce + aForce + wForce); + } + + private Vector3 CalculateGoalForce() + { + // calculate the path to the destination and take the first two corners. + // return the unit vector in that direction and multiplied by a constant scalar. + Vector3 curPath = (new Vector3(path[0].x, transform.position.y, path[0].z) - transform.position).normalized; + //Return the (Vi_0 * ei_0) - Vi + return ((goalForceSpeed * curPath) - GetVelocity()); + } + + private Vector3 CalculateAgentForce() + { + Vector3 agentForce = Vector3.zero; + foreach (GameObject neighbor in perceivedNeighbors) + { + Vector3 repForce = Vector3.zero; + Vector3 slideForce = Vector3.zero; + Vector3 opos = neighbor.transform.position; + Rigidbody oagent = neighbor.GetComponent(); + + float r_ij = 2 * radius; + float d_ij = Vector3.Distance(opos,transform.position); + float g = (r_ij - d_ij > 0.000001) ? (r_ij- d_ij) : 0; + Vector3 n_ij = (transform.position - opos)/d_ij; + Vector3 t_ij = new Vector3(-n_ij.z, n_ij.x, 0); + float deltaV_ji = Vector3.Dot((oagent.velocity - GetVelocity()), t_ij); + + repForce = (A * Mathf.Exp((r_ij - d_ij)/B) + kappa*g) * n_ij; + slideForce = k * g * deltaV_ji * t_ij; + + agentForce += (repForce + slideForce); + } + + return agentForce; + } + + private Vector3 CalculateWallForce() + { + + // Forces on the agent will be perpendicular to the walls closest surface + Vector3 WallForce = Vector3.zero; + foreach (GameObject Wall in perceivedWalls) + { + Vector3 repForce = Vector3.zero; + Vector3 slideForce = Vector3.zero; + + float r_i = radius; + float d_iw = Vector3.Distance(Wall.transform.position, transform.position); + float g = (r_i - d_iw > 0.000001) ? (r_i - d_iw) : 0; + Vector3 n_iw = Vector3.zero; + + Vector3 direction = (Wall.transform.position - rb.transform.position).normalized; + float dotEast = Vector3.Dot(transform.right, direction); + float dotNorth = Vector3.Dot(transform.forward, direction); + if (Mathf.Abs(dotEast) > Mathf.Abs(dotNorth)) + { + // Wall is east/west of Agent + if(dotEast < 0) + n_iw = Wall.transform.right; // Will be negative if west, positive if east //The inverse by dividing by a number means the force gets stronger as the agent is closer to the wall + else + n_iw = -Wall.transform.right; + } + else + { + // Wall is north/south of Agent + if(dotNorth < 0) + n_iw = Wall.transform.forward; + else + n_iw = -Wall.transform.forward; + // Will be negative if south, positive if North + } + Vector3 t_iw = new Vector3(-n_iw.z, n_iw.x, 0); + + repForce = (A * Mathf.Exp((r_i - d_iw)/B) + kappa*g) * n_iw; + slideForce = k * g * Vector3.Dot(GetVelocity(), t_iw) * t_iw; + WallForce += (repForce - slideForce); + } + return WallForce; + } + + public void ApplyForce() + { + var force = ComputeForce(); + force.y = 0; + rb.AddForce(force * 10, ForceMode.Force); + } + + public void OnTriggerEnter(Collider other) + { + if(other.gameObject.CompareTag("Agent")){ + perceivedNeighbors.Add(other.gameObject); + } else if(WallManager.IsWall(other.gameObject)){ + perceivedWalls.Add(other.gameObject); + } + } + + public void OnTriggerExit(Collider other) + { + if(other.gameObject.CompareTag("Agent")){ + perceivedNeighbors.Remove(other.gameObject); + } else if(WallManager.IsWall(other.gameObject)){ + perceivedWalls.Remove(other.gameObject); + } + } + + public void OnCollisionEnter(Collision collision) + { + /*if(WallManager.IsWall(collision.transform.gameObject)){ + perceivedWalls.Add(collision.transform.gameObject); + }*/ + } + + public void OnCollisionExit(Collision collision) + {/* + if(WallManager.IsWall(collision.transform.gameObject)){ + perceivedWalls.Remove(collision.transform.gameObject); + }*/ + } +} diff --git a/B4 Scripts/PursueAgent.cs.meta b/B4 Scripts/PursueAgent.cs.meta new file mode 100644 index 0000000..29c4dc4 --- /dev/null +++ b/B4 Scripts/PursueAgent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4f6b9e84decc44b96b3c2579e52bdd90 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B4 Scripts/SlidingFrictionForceVisualizer.cs b/B4 Scripts/SlidingFrictionForceVisualizer.cs new file mode 100644 index 0000000..e4e4c0f --- /dev/null +++ b/B4 Scripts/SlidingFrictionForceVisualizer.cs @@ -0,0 +1,41 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class SlidingFrictionForceVisualizer : MonoBehaviour +{ + public GameObject agent1; + public GameObject agent2; + + [Range(0, 10)] + public float speed1; + [Range(0, 10)] + public float speed2; + + void Update() + { + Debug.DrawLine(agent1.transform.position, agent1.transform.position + Vector3.up * 3, Color.green); + Debug.DrawLine(agent1.transform.position, agent1.transform.position + agent1.transform.forward * speed1, Color.cyan); + Debug.DrawLine(agent2.transform.position, agent2.transform.position + agent2.transform.forward * speed2, Color.cyan); + + var n = (agent2.transform.position - agent1.transform.position).normalized; + var tangent = Vector3.Cross(Vector3.up, n); + Debug.DrawLine(agent1.transform.position, agent1.transform.position + tangent * 2, Color.yellow); + + var magnitude = Vector3.Dot(agent1.transform.forward * speed1 - agent2.transform.forward * speed2, tangent); + Debug.DrawLine(agent1.transform.position, agent1.transform.position + tangent * magnitude * 2, Color.red); + + Debug.DrawLine(agent1.transform.position + agent1.transform.forward * speed1, agent2.transform.position + agent2.transform.forward * speed2, Color.magenta); + + #region Visualization + +#if UNITY_EDITOR + if (Application.isFocused) + { + UnityEditor.SceneView.FocusWindowIfItsOpen(typeof(UnityEditor.SceneView)); + } +#endif + + #endregion + } +} diff --git a/B4 Scripts/SlidingFrictionForceVisualizer.cs.meta b/B4 Scripts/SlidingFrictionForceVisualizer.cs.meta new file mode 100644 index 0000000..2dfb3e3 --- /dev/null +++ b/B4 Scripts/SlidingFrictionForceVisualizer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ce8f0edba51a5f5409bf63fef1fab43b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B4 Scripts/WallManager.cs b/B4 Scripts/WallManager.cs new file mode 100644 index 0000000..ea5437d --- /dev/null +++ b/B4 Scripts/WallManager.cs @@ -0,0 +1,78 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class WallManager : MonoBehaviour +{ + public float wallProb = 0.3f; + public float wallSpawnRadius = 20; + public GameObject wallPrefab; + + private List walls = new List(); + private GameObject wallParent; + private static HashSet wallObjs = new HashSet(); + + void Start() + { + Random.InitState(0); + + wallParent = GameObject.Find("Walls"); + for (int i = -Mathf.RoundToInt(wallSpawnRadius / 2); i < wallSpawnRadius / 2; i++) + for (int j = -Mathf.RoundToInt(wallSpawnRadius / 2); j < wallSpawnRadius / 2; j++) + { + if (Random.value < wallProb) + { + GameObject wall = null; + wall = Instantiate(wallPrefab, new Vector3(i + 0.5f, 0.5f, j + 0.5f), Quaternion.identity); + wall.name = "Wall " + i; + wall.transform.parent = wallParent.transform; + var wallScript = wall.GetComponent(); + + walls.Add(wallScript); + wallObjs.Add(wall); + } + } + for (int i = -Mathf.RoundToInt(wallSpawnRadius / 2); i < wallSpawnRadius / 2; i++) + { + foreach (var j in new[] { -Mathf.RoundToInt(wallSpawnRadius / 2) - 1, wallSpawnRadius / 2 }) + { + GameObject wall = null; + wall = Instantiate(wallPrefab, new Vector3(i + 0.5f, 0.5f, j + 0.5f), Quaternion.identity); + wall.name = "Wall " + i; + wall.transform.parent = wallParent.transform; + var wallScript = wall.GetComponent(); + + walls.Add(wallScript); + wallObjs.Add(wall); + } + } + for (int j = -Mathf.RoundToInt(wallSpawnRadius / 2); j < wallSpawnRadius / 2; j++) + { + foreach (var i in new[] { -Mathf.RoundToInt(wallSpawnRadius / 2) - 1, wallSpawnRadius / 2 }) + { + GameObject wall = null; + wall = Instantiate(wallPrefab, new Vector3(i + 0.5f, 0.5f, j + 0.5f), Quaternion.identity); + wall.name = "Wall " + i; + wall.transform.parent = wallParent.transform; + var wallScript = wall.GetComponent(); + + walls.Add(wallScript); + wallObjs.Add(wall); + } + } + } + + void Update() + { + + } + + #region Public Functions + + public static bool IsWall(GameObject obj) + { + return wallObjs.Contains(obj); + } + + #endregion +} diff --git a/B4 Scripts/WallManager.cs.meta b/B4 Scripts/WallManager.cs.meta new file mode 100644 index 0000000..c3ac5b9 --- /dev/null +++ b/B4 Scripts/WallManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 739b90a3b1e98314a8e6b5c1901afc81 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B5 Scripts/CameraController.cs b/B5 Scripts/CameraController.cs new file mode 100644 index 0000000..a91ca60 --- /dev/null +++ b/B5 Scripts/CameraController.cs @@ -0,0 +1,31 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class CameraController : MonoBehaviour +{ + public float moveSpeed = 10f; + public float sensitivity = 2f; + public float camSpeed = -0.5f; + // Start is called before the first frame update + void Start() + { + + } + + // Update is called once per frame + void LateUpdate() + { + Cursor.visible = false; + Cursor.lockState = CursorLockMode.Locked; + transform.position = transform.position + Vector3.ProjectOnPlane(transform.forward, Vector3.up) * Input.GetAxis("Vertical") *moveSpeed*Time.deltaTime; + transform.position = transform.position + Vector3.ProjectOnPlane(transform.right, Vector3.up) * Input.GetAxis("Horizontal") *moveSpeed*Time.deltaTime; + float x = Input.GetAxis("Mouse X"); + float y = Input.GetAxis("Mouse Y"); + Vector3 rotateValue = new Vector3(y, x * -1, 0); + transform.eulerAngles = transform.eulerAngles - rotateValue; + transform.eulerAngles += rotateValue * camSpeed; + int vertical = Input.GetKey("left shift") ? 1 : (Input.GetKey("left ctrl") ? -1 : 0); + transform.position = transform.position + transform.up * vertical * moveSpeed * Time.deltaTime; + } +} diff --git a/B5 Scripts/CameraController.cs.meta b/B5 Scripts/CameraController.cs.meta new file mode 100644 index 0000000..3d6d92c --- /dev/null +++ b/B5 Scripts/CameraController.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 16d182def365549ae86b4303a629a084 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B5 Scripts/PlayerController.cs b/B5 Scripts/PlayerController.cs new file mode 100644 index 0000000..29c54ec --- /dev/null +++ b/B5 Scripts/PlayerController.cs @@ -0,0 +1,25 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class PlayerController : MonoBehaviour +{ + private bool foundExit = false; + // Start is called before the first frame update + void Start() + { + + } + + void OnTriggerEnter(Collider other){ + Debug.Log(other.tag); + if(other.tag == "Exit"){ + Debug.Log("Exit found!"); + foundExit = true; + } + } + + public bool IsExitFound(){ + return foundExit; + } +} diff --git a/B5 Scripts/PlayerController.cs.meta b/B5 Scripts/PlayerController.cs.meta new file mode 100644 index 0000000..ea6b960 --- /dev/null +++ b/B5 Scripts/PlayerController.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bb5248eeecc434a50b5f6e0b33621c3b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B5 Scripts/SequenceEvery.cs b/B5 Scripts/SequenceEvery.cs new file mode 100644 index 0000000..4660f49 --- /dev/null +++ b/B5 Scripts/SequenceEvery.cs @@ -0,0 +1,78 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System; +using UnityEngine; + +namespace TreeSharpPlus +{ + /// + /// The base sequence class. This will execute each branch of logic, in order. + /// If all branches succeed, this composite will return a successful run status. + /// If any branch fails, this composite will return a failed run status. + /// + public class SequenceEvery : NodeGroup + { + protected Stopwatch stopwatch; + protected long waitMax; + + public SequenceEvery(Val waitMax, params Node[] children) + : base(children) + { + this.waitMax = waitMax.Value; + this.stopwatch = new Stopwatch(); + } + + public override void Start() + { + base.Start(); + } + + public override void Stop() + { + base.Stop(); + this.stopwatch.Stop(); + } + + public override IEnumerable Execute() + { + if(!this.stopwatch.IsRunning) + this.stopwatch.Reset(); + this.stopwatch.Start(); + while(true){ + if (this.stopwatch.ElapsedMilliseconds >= this.waitMax) + { + this.stopwatch.Stop(); + foreach (Node node in this.Children) + { + // Move to the next node + this.Selection = node; + node.Start(); + + // If the current node is still running, report that. Don't 'break' the enumerator + RunStatus result; + while ((result = this.TickNode(node)) == RunStatus.Running) + yield return RunStatus.Running; + + // Call Stop to allow the node to clean anything up. + node.Stop(); + + // Clear the selection + this.Selection.ClearLastStatus(); + this.Selection = null; + + if (result == RunStatus.Failure) + { + yield return RunStatus.Failure; + yield break; + } + + yield return RunStatus.Running; + } + yield return RunStatus.Success; + yield break; + } + yield return RunStatus.Running; + } + } + } +} \ No newline at end of file diff --git a/B5 Scripts/SequenceEvery.cs.meta b/B5 Scripts/SequenceEvery.cs.meta new file mode 100644 index 0000000..e77b540 --- /dev/null +++ b/B5 Scripts/SequenceEvery.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 60122ca60af264c0f95b1a99c9fd5b79 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B6 Scripts/Agent.cs b/B6 Scripts/Agent.cs new file mode 100644 index 0000000..bed4261 --- /dev/null +++ b/B6 Scripts/Agent.cs @@ -0,0 +1,419 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.AI; + +public class Agent : MonoBehaviour +{ + public float radius; + public float mass; + public float perceptionRadius; + public float goalWeight = 5; + public float goalForceSpeed; + public float agentCount = 0; + public bool spiralFlag = false; + public bool flocking = true; + public Vector3 velocmon; + public float crowdFollowParam = 0f; + public string alignment = "Red"; + + + private Vector3 ei_0 = Vector3.zero; + + float A = 2.0f; + float B = 0.6f; + float k = 1.2f; + float kappa = 2.4f; + + private Vector3 destination; + public List path; + private NavMeshAgent nma; + private Rigidbody rb; + private SphereCollider sc; + private HashSet perceivedNeighbors = new HashSet(); + private HashSet perceivedWalls = new HashSet(); + private HashSet perceivedEnemy = new HashSet(); + + + void Start() + { + path = new List(); + nma = GetComponent(); + rb = GetComponent(); + sc = GetComponent(); + + gameObject.transform.localScale = new Vector3(2 * radius, 1, 2 * radius); + nma.radius = radius; + rb.mass = mass; + GetComponent().radius = perceptionRadius / 2; + + if (spiralFlag) + { + computeSpiral(); + } + } + + private void computeSpiral() + { + Vector3 apos = this.transform.position; + Vector3 apnext = apos*1.00008f; + transform.RotateAround(apnext, Vector3.up, 60.0f); + velocmon = apnext; + + this.SetDestination(apnext); + this.ComputePath(); + + } + + private void Update() + { + if (spiralFlag) + { + if (path.Count == 1 && Vector3.Distance(transform.position, path[0]) < 2f) + { + computeSpiral(); + + } + } + else { + if (path.Count >= 1 && Vector3.Distance(transform.position, path[0]) < 1.1f) + { + path.RemoveAt(0); + } + } + #region Visualization + + if (false) + { + if (path.Count > 0) + { + Debug.DrawLine(transform.position, path[0], Color.green); + } + for (int i = 0; i < path.Count - 1; i++) + { + Debug.DrawLine(path[i], path[i + 1], Color.yellow); + } + } + + if (false) + { + foreach (var neighbor in perceivedNeighbors) + { + Debug.DrawLine(transform.position, neighbor.transform.position, Color.yellow); + } + } + + #endregion + } + + private void FixedUpdate(){ + CheckEnemyNeighbors(); + } + + #region Public Functions + public void SetDestination(Vector3 d){ + if(d != destination){ + destination = d; + } + } + + public void ComputePath() + { + nma.enabled = true; + var nmPath = new NavMeshPath(); + nma.CalculatePath(destination, nmPath); + if(spiralFlag){ + path = new List() { destination }; + }else{ + path = nmPath.corners.Skip(1).ToList(); + } + goalForceSpeed = nma.speed; + nma.enabled = false; + } + + public Vector3 GetVelocity() + { + return rb.velocity; + } + + #endregion + + #region Incomplete Functions + + private Vector3 ComputeForce() + { + + Vector3 flockForce = Vector3.zero; + if (flocking){ + flockForce = CalculateFlockForce(); + } + Vector3 gForce = CalculateGoalForce(); + + Vector3 tForce = CalculateAttractionForce(); + + Vector3 aForce = CalculateAgentForce(); + + Vector3 wForce = CalculateWallForce(); + + Debug.DrawLine(transform.position, transform.position + gForce, Color.red, 0.5f); + Debug.DrawLine(transform.position, transform.position + aForce, Color.green, 0.5f); + Debug.DrawLine(transform.position, transform.position + wForce, Color.blue, 0.5f); + Debug.DrawLine(transform.position, transform.position + tForce, Color.black, 0.5f); + return gForce + aForce + wForce + flockForce; + } + + private Vector3 CalculateFlockForce() + { + Vector3 Alighnment = Vector3.zero; + Vector3 Seperation = Vector3.zero; + Vector3 Cohesion = Vector3.zero; + foreach (GameObject neighbor in perceivedNeighbors) + { + Alighnment += neighbor.GetComponent().GetLastDirection(); + Cohesion += neighbor.transform.position; + Seperation += neighbor.transform.position - rb.position; + } + Cohesion /= perceivedNeighbors.Count(); + Alighnment /= perceivedNeighbors.Count(); + Seperation /= perceivedNeighbors.Count(); + Seperation *= -1; + return Alighnment.normalized + Seperation.normalized + (Cohesion - rb.position).normalized; + } + + private Vector3 NeighborGoalForce(){ + Vector3 avg_e = Vector3.zero; + if(perceivedNeighbors.Count == 0) + return avg_e; + foreach(GameObject neighbor in perceivedNeighbors) + { + avg_e = neighbor.GetComponent().GetLastDirection(); + } + avg_e = avg_e * (1 / perceivedNeighbors.Count); + return avg_e; + } + + private Vector3 CalculateGoalForce() + { + // calculate the path to the destination and take the first two corners. + // return the unit vector in that direction and multiplied by a constant scalar. + if(path.Count == 0) + return Vector3.zero; + + ei_0 = (new Vector3(path[0].x, transform.position.y, path[0].z) - transform.position).normalized; + if(crowdFollowParam > 0){ + Vector3 neighbor_e = NeighborGoalForce(); + ei_0 = ((1 - crowdFollowParam) * ei_0 + crowdFollowParam * neighbor_e).normalized; + } + //Return the (Vi_0 * ei_0) - Vi + return ((goalForceSpeed * ei_0) - GetVelocity()); + } + + private Vector3 CalculateAttractionForce(){ + Vector3 attractionForce = Vector3.zero; + foreach(GameObject enemy in perceivedEnemy){ + Vector3 repForce = Vector3.zero; + Vector3 slideForce = Vector3.zero; + Vector3 opos = enemy.transform.position; + Agent oagent = enemy.GetComponent(); + + float r_ij = oagent.radius + radius; + float d_ij = Vector3.Distance(opos,transform.position); + float g = (r_ij - d_ij > 0.000001) ? (r_ij- d_ij) : 0; + Vector3 n_ij = (transform.position - opos)/d_ij; + Vector3 t_ij = new Vector3(-n_ij.z, n_ij.x, 0); + float deltaV_ji = Vector3.Dot((oagent.GetVelocity() - GetVelocity()), t_ij); + + repForce = (A * Mathf.Exp((r_ij - d_ij)/B) + kappa*g) * n_ij; + slideForce = k * g * deltaV_ji * t_ij; + + attractionForce += (repForce + slideForce); + } + + return -attractionForce; + } + + private Vector3 CalculateAgentForce() + { + Vector3 agentForce = Vector3.zero; + foreach (GameObject neighbor in perceivedNeighbors) + { + Vector3 repForce = Vector3.zero; + Vector3 slideForce = Vector3.zero; + Vector3 opos = neighbor.transform.position; + Agent oagent = neighbor.GetComponent(); + + float r_ij = oagent.radius + radius; + float d_ij = Vector3.Distance(opos,transform.position); + float g = (r_ij - d_ij > 0.000001) ? (r_ij- d_ij) : 0; + Vector3 n_ij = (transform.position - opos)/d_ij; + Vector3 t_ij = new Vector3(-n_ij.z, n_ij.x, 0); + float deltaV_ji = Vector3.Dot((oagent.GetVelocity() - GetVelocity()), t_ij); + + repForce = (A * Mathf.Exp((r_ij - d_ij)/B) + kappa*g) * n_ij; + slideForce = k * g * deltaV_ji * t_ij; + + agentForce += (repForce + slideForce); + } + + return agentForce; + } + + private Vector3 CalculateWallForce() + { + + // Forces on the agent will be perpendicular to the walls closest surface + Vector3 WallForce = Vector3.zero; + foreach (GameObject Wall in perceivedWalls) + { + Vector3 repForce = Vector3.zero; + Vector3 slideForce = Vector3.zero; + + float r_i = radius; + float d_iw = Vector3.Distance(Wall.transform.position, transform.position); + float g = (r_i - d_iw > 0.000001) ? (r_i - d_iw) : 0; + Vector3 n_iw = Vector3.zero; + + Vector3 direction = (Wall.transform.position - rb.transform.position).normalized; + float dotEast = Vector3.Dot(Vector3.right, direction); + float dotNorth = Vector3.Dot(Vector3.forward, direction); + if (Mathf.Abs(dotEast) > Mathf.Abs(dotNorth)) + { + // Wall is east/west of Agent + if(dotEast < 0) + n_iw = Wall.transform.right; + else + n_iw = -Wall.transform.right; + } + else + { + // Wall is north/south of Agent + if(dotNorth < 0) + n_iw = Wall.transform.forward; + else + n_iw = -Wall.transform.forward; + // Will be negative if south, positive if North + } + Vector3 t_iw = new Vector3(-n_iw.z, n_iw.x, 0); + + repForce = (A * Mathf.Exp((r_i - d_iw)/B) + kappa*g) * n_iw; + slideForce = k * g * Vector3.Dot(GetVelocity(), t_iw) * t_iw; + WallForce += (repForce - slideForce); + } + return WallForce; + } + + //OnTriggerExit is not called if the GameObject is deactivated or destroyed. + //Function will clean up perceivedNeighbors that were deactivated while still in range of Agent. + private void CleanNeighbors(){ + HashSet excludeNeighbors = new HashSet(); + foreach(GameObject neighbor in perceivedNeighbors){ + if(neighbor == null || !neighbor.activeSelf) + excludeNeighbors.Add(neighbor); + } + perceivedNeighbors.ExceptWith(excludeNeighbors); + } + private void CleanEnemies(){ + HashSet excludeNeighbors = new HashSet(); + foreach(GameObject enemy in perceivedEnemy){ + if(enemy == null || !enemy.activeSelf) + excludeNeighbors.Add(enemy); + } + perceivedEnemy.ExceptWith(excludeNeighbors); + } + + private void CheckEnemyNeighbors(){ + CleanEnemies(); + foreach(GameObject enemy in perceivedEnemy){ + if(Vector3.Distance(transform.position, enemy.transform.position) < 1f){ + GameObject managers = GameObject.Find("Managers"); + if (this.alignment.Equals("Red")) { + RTSAgentManager manr = managers.GetComponent(); + manr.RemoveAgent(this.gameObject); + } else{ + RTSEAgentManager mane = managers.GetComponent(); + mane.RemoveAgent(this.gameObject); + } + Destroy(this.gameObject); + } + } + } + + public void ApplyForce() + { + CleanNeighbors(); + CleanEnemies(); + + var force = ComputeForce(); + force.y = 0; + rb.AddForce(force * 10, ForceMode.Force); + } + + public Vector3 GetLastDirection(){ + return ei_0; + } + + public void OnTriggerEnter(Collider other) + { + + if(gameObject.tag == other.gameObject.tag){ + perceivedNeighbors.Add(other.gameObject); + } else if(WallManager.IsWall(other.gameObject)){ + perceivedWalls.Add(other.gameObject); + } else if (PointsManager.isCpoint(other.gameObject)) + { + cPointBehavior cBehav = other.gameObject.GetComponent(); + if (cBehav.owner.Equals(this.alignment)) return; + cBehav.health--; + if (cBehav.health <= 0) + { + cBehav.owner = this.alignment; + cBehav.health = cBehav.max_health; + } + GameObject managers = GameObject.Find("Managers"); + if (this.alignment.Equals("Red")) { + RTSAgentManager manr = managers.GetComponent(); + manr.RemoveAgent(this.gameObject); + } else + { + RTSEAgentManager mane = managers.GetComponent(); + mane.RemoveAgent(this.gameObject); + } + Destroy(this.gameObject); + }else if((gameObject.tag == "Agent" && other.gameObject.tag == "EnemyAgent") || + (gameObject.tag == "EnemyAgent" && other.gameObject.tag == "Agent")){ + perceivedEnemy.Add(other.gameObject); + } + } + + public void OnTriggerExit(Collider other) + { + if(gameObject.tag == other.gameObject.tag){ + perceivedNeighbors.Remove(other.gameObject); + } else if(WallManager.IsWall(other.gameObject)){ + perceivedWalls.Remove(other.gameObject); + }else if((gameObject.tag =="Agent" && other.gameObject.tag == "EnemyAgent") || + (gameObject.tag == "EnemyAgent" && other.gameObject.tag == "Agent")){ + perceivedEnemy.Remove(other.gameObject); + } + } + + public void OnCollisionEnter(Collision collision) + { + /* + if(WallManager.IsWall(collision.transform.gameObject)){ + perceivedWalls.Add(collision.transform.gameObject); + }*/ + } + + public void OnCollisionExit(Collision collision) + { + /* + if(WallManager.IsWall(collision.transform.gameObject)){ + perceivedWalls.Remove(collision.transform.gameObject); + } + */ + } + + #endregion +} diff --git a/B6 Scripts/Agent.cs.meta b/B6 Scripts/Agent.cs.meta new file mode 100644 index 0000000..f7907ce --- /dev/null +++ b/B6 Scripts/Agent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 12450463083bdd140a88af41456e0a5f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B6 Scripts/AgentManager.cs b/B6 Scripts/AgentManager.cs new file mode 100644 index 0000000..ed69f53 --- /dev/null +++ b/B6 Scripts/AgentManager.cs @@ -0,0 +1,249 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.AI; + +public class AgentManager : MonoBehaviour +{ + public int agentCount = 10; + public float agentSpawnRadius = 20; + public GameObject agentPrefab; + public static Dictionary agentsObjs = new Dictionary(); + + private static List agents = new List(); + private static List selected = new List(); + private GameObject agentParent; + private Vector3 destination; + + public const float UPDATE_RATE = 0.0f; + private const int PATHFINDING_FRAME_SKIP = 25; + + public bool spiralFlag = false; + public bool flocking = true; + + public float CrowdFollow_Param = 0f; + + public Material selectedMaterial; + public Material unselectedMaterial; + + private Vector3 mouseDownPosition = Vector3.zero; + + #region Unity Functions + + void Awake() + { + Random.InitState(0); + + agentParent = GameObject.Find("Agents"); + for (int i = 0; i < agentCount; i++) + { + var randPos = new Vector3((Random.value - 0.5f) * agentSpawnRadius, 0, (Random.value - 0.5f) * agentSpawnRadius); + NavMeshHit hit; + NavMesh.SamplePosition(randPos, out hit, 10, NavMesh.AllAreas); + randPos = hit.position + Vector3.up; + + GameObject agent = null; + agent = Instantiate(agentPrefab, randPos, Quaternion.identity); + agent.name = "Agent " + i; + agent.transform.parent = agentParent.transform; + var agentScript = agent.GetComponent(); + agentScript.radius = 0.3f;// Random.Range(0.2f, 0.6f); + agentScript.mass = 1; + agentScript.perceptionRadius = 3; + if(spiralFlag){ + agentScript.spiralFlag = true; + } + if (!flocking) + { + agentScript.flocking = false; + } + agentScript.crowdFollowParam = CrowdFollow_Param; + agents.Add(agentScript); + agentsObjs.Add(agent, agentScript); + selected.Add(agentScript); + } + + StartCoroutine(Run()); + } + + void Update() + { + #region Visualization + if(Input.GetMouseButtonDown(0)){ + mouseDownPosition = Input.mousePosition; + } + if (Input.GetMouseButtonUp(0)) + { + if(Input.mousePosition == mouseDownPosition){ + var point = Camera.main.ScreenToWorldPoint(Input.mousePosition + Vector3.forward * 10); + var dir = point - Camera.main.transform.position; + + RaycastHit rcHit; + if (Physics.Raycast(point, dir, out rcHit)) + { + if(rcHit.transform.tag == "Agent"){ + ChangeSelectAgent(rcHit.transform.gameObject); + }else{ + NavMeshHit navHit; + if (NavMesh.SamplePosition(rcHit.point, out navHit, 1.0f, NavMesh.AllAreas)) + { + destination = navHit.position; + SetAgentDestinations(destination); + } + } + } + } + } + +#if UNITY_EDITOR + if (Application.isFocused) + { + //UnityEditor.SceneView.FocusWindowIfItsOpen(typeof(UnityEditor.SceneView)); + } +#endif + + #endregion + } + + IEnumerator Run() + { + yield return null; + + for (int iterations = 0; ; iterations++) + { + if (iterations % PATHFINDING_FRAME_SKIP == 0) + { + if(!spiralFlag){ + ComputeAgentPath(); + } + } + + foreach (var agent in agents) + { + agent.ApplyForce(); + } + + if (UPDATE_RATE == 0) + { + yield return null; + } else + { + yield return new WaitForSeconds(UPDATE_RATE); + } + } + } + + #endregion + + #region Public Functions + + public static bool IsAgent(GameObject obj) + { + return agentsObjs.ContainsKey(obj); + } + + public void SetAgentDestinations(Vector3 NewDestination) + { + destination = NewDestination; + NavMeshHit hit; + NavMesh.SamplePosition(NewDestination, out hit, 10, NavMesh.AllAreas); + foreach (var agent in selected) + { + agent.SetDestination(hit.position); + } + } + + public void ComputeAgentPath(){ + foreach(var agent in agents){ + agent.ComputePath(); + } + } + + public static void RemoveAgent(GameObject obj) + { + var agent = obj.GetComponent(); + + agents.Remove(agent); + agentsObjs.Remove(obj); + } + + public void SelectedAgents(Vector3 selection){ + //Debug.Log(selection); + Vector3 worldDownPosition = Camera.main.ScreenToWorldPoint(mouseDownPosition + Vector3.forward * 10); + Vector3 worldUpPosition = Camera.main.ScreenToWorldPoint(selection + Vector3.forward * 10); + Vector3 dir = worldDownPosition - Camera.main.transform.position; + RaycastHit rcDownHit; + RaycastHit rcUpHit; + if (Physics.Raycast(worldDownPosition, dir, out rcDownHit)) + { + dir = worldUpPosition - Camera.main.transform.position; + if(Physics.Raycast(worldUpPosition, dir, out rcUpHit)){ + worldDownPosition = rcDownHit.point; + worldUpPosition = rcUpHit.point; + //Debug.Log("World Down Position:" + worldDownPosition); + //Debug.Log("World Up Position:" + worldUpPosition); + if(!Input.GetKey(KeyCode.LeftShift)){ + //Debug.Log("Here"); + foreach(Agent selectedAgent in agents){ + if(selected.Contains(selectedAgent)){ + selected.Remove(selectedAgent); + selectedAgent.transform.gameObject.GetComponent().material = unselectedMaterial; + } + } + } + + float xRight = worldUpPosition.x > worldDownPosition.x ? worldDownPosition.x : worldUpPosition.x; + float xLeft = worldUpPosition.x > worldDownPosition.x ? worldUpPosition.x : worldDownPosition.x; + + float yUp = worldUpPosition.z > worldDownPosition.z ? worldDownPosition.z : worldUpPosition.z; + float yDown = worldUpPosition.z > worldDownPosition.z ? worldUpPosition.z : worldDownPosition.z; + //Debug.Log("xRight:" + xRight + " xLeft:" + xLeft + " yUp:" + yUp + " yDown:" + yDown); + foreach(Agent agent in agents){ + Vector3 agentPosition = agent.transform.position; + //Debug.Log("Agent Position:" + agentPosition); + if(!selected.Contains(agent) && agentPosition.x >= xRight && agentPosition.x <= xLeft && agentPosition.z >= yUp && agentPosition.z <= yDown){ + selected.Add(agent); + agent.transform.gameObject.GetComponent().material = selectedMaterial; + } + } + } + + } + } + #endregion + + #region Private Functions + + void ChangeSelectAgent(GameObject agent){ + Agent agScript = agent.GetComponent(); + if(selected.Contains(agScript)){ + selected.Remove(agScript); + agent.GetComponent().material = unselectedMaterial; + }else{ + selected.Add(agScript); + agent.GetComponent().material = selectedMaterial; + } + } + + #endregion + + #region Visualization Functions + + #endregion + + #region Utility Classes + + private class Tuple + { + public K Item1; + public V Item2; + + public Tuple(K k, V v) { + Item1 = k; + Item2 = v; + } + } + + #endregion +} diff --git a/B6 Scripts/AgentManager.cs.meta b/B6 Scripts/AgentManager.cs.meta new file mode 100644 index 0000000..fb478cc --- /dev/null +++ b/B6 Scripts/AgentManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 27f617c0ea9733b44b50c37923d5c7d4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B6 Scripts/AudioManager.cs b/B6 Scripts/AudioManager.cs new file mode 100644 index 0000000..21e5808 --- /dev/null +++ b/B6 Scripts/AudioManager.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Audio; + +public class AudioManager : MonoBehaviour +{ + public Sound[] sounds; + + void Awake() + { + foreach(Sound s in sounds){ + s.source = gameObject.AddComponent(); + s.source.clip = s.clip; + s.source.volume = s.volume; + s.source.pitch = s.pitch; + s.source.loop = s.loop; + } + } + + public void ChangeVolume(string name, float newVolume){ + Sound s = Array.Find(sounds, sound => sound.name == name); + if(newVolume >= 0 && newVolume <=1f) + s.source.volume = newVolume; + } + + public float CurrentVolume(string name){ + Sound s = Array.Find(sounds, sound => sound.name == name); + return s.source.volume; + } + + public void Play(string name){ + Sound s = Array.Find(sounds, sound => sound.name == name); + s.source.Play(); + } + + public void Stop(string name){ + Sound s = Array.Find(sounds, sound => sound.name == name); + s.source.Stop(); + } + + public bool IsPlaying(string name){ + Sound s = Array.Find(sounds, sound => sound.name == name); + return s.source.isPlaying; + } +} diff --git a/B6 Scripts/AudioManager.cs.meta b/B6 Scripts/AudioManager.cs.meta new file mode 100644 index 0000000..d3fc6f5 --- /dev/null +++ b/B6 Scripts/AudioManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ed8b91cc096404b7bb0cd71c0aa698bf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B6 Scripts/CameraController.cs b/B6 Scripts/CameraController.cs new file mode 100644 index 0000000..571fdb7 --- /dev/null +++ b/B6 Scripts/CameraController.cs @@ -0,0 +1,63 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class CameraController : MonoBehaviour +{ + public float moveSpeed = 10f; + public float sensitivity = 2f; + public float camSpeed = -0.5f; + public RTSAgentManager agentManager; + public Texture2D selectionHighLight = null; + + static Rect selection = new Rect(0, 0, 0, 0); + Vector3 startSelected = Vector3.zero; + // Update is called once per frame + void LateUpdate() + { + //Cursor.visible = false; + //Cursor.lockState = CursorLockMode.Locked; + transform.position = transform.position + Vector3.ProjectOnPlane(transform.forward, Vector3.up) * Input.GetAxis("Vertical") *moveSpeed*Time.deltaTime; + transform.position = transform.position + Vector3.ProjectOnPlane(transform.right, Vector3.up) * Input.GetAxis("Horizontal") *moveSpeed*Time.deltaTime; + //float x = Input.GetAxis("Mouse X"); + //float y = Input.GetAxis("Mouse Y"); + //Vector3 rotateValue = new Vector3(y, x * -1, 0); + //transform.eulerAngles = transform.eulerAngles - rotateValue; + //transform.eulerAngles += rotateValue * camSpeed; + transform.position = transform.position + transform.up * Input.mouseScrollDelta.y * moveSpeed * Time.deltaTime; + StartSelection(); + } + + void StartSelection(){ + if(Input.GetMouseButtonDown(0)){ + startSelected = Input.mousePosition; + }else if(Input.GetMouseButtonUp(0)){ + if(startSelected != Input.mousePosition){ + agentManager.SelectedAgents(Input.mousePosition); + } + startSelected = Vector3.zero; + } + HandleSelection(); + } + + void HandleSelection(){ + if(Input.GetMouseButton(0)){ + selection = new Rect(startSelected.x, Screen.height - startSelected.y, + Input.mousePosition.x - startSelected.x, (Screen.height - Input.mousePosition.y) - (Screen.height - startSelected.y)); + if(selection.width < 0){ + selection.x += selection.width; + selection.width = -selection.width; + } + if(selection.height < 0){ + selection.y += selection.height; + selection.height = -selection.height; + } + } + } + + void OnGUI(){ + if(startSelected != Vector3.zero){ + GUI.DrawTexture(selection, selectionHighLight); + } + } +} diff --git a/B6 Scripts/CameraController.cs.meta b/B6 Scripts/CameraController.cs.meta new file mode 100644 index 0000000..c5ac0bf --- /dev/null +++ b/B6 Scripts/CameraController.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d717c133eacc19346983eff03b856720 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B6 Scripts/EvadeAgent.cs b/B6 Scripts/EvadeAgent.cs new file mode 100644 index 0000000..6c29f37 --- /dev/null +++ b/B6 Scripts/EvadeAgent.cs @@ -0,0 +1,254 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.AI; + +public class EvadeAgent : MonoBehaviour +{ + public float radius; + public float mass; + public float perceptionRadius; + public float goalWeight = 5; + public float goalForceSpeed; + + public List pursueAgents = new List(); + + float A = 3.1f; + float B = 0.8f; + float k = 1.2f; + float kappa = 3.1f; + + private Vector3 direction; + private bool returnHome = false; + private Vector3 homeDestination = new Vector3(0,1,0); + private List path; + private NavMeshAgent nma; + private Rigidbody rb; + private SphereCollider sc; + private HashSet perceivedNeighbors = new HashSet(); + private HashSet perceivedWalls = new HashSet(); + + private int FRAME; + private const int PATHFINDING_FRAME_SKIP = 25; + + void Start() + { + //destination = Vector3.zero; + path = new List(); + nma = GetComponent(); + rb = GetComponent(); + sc = GetComponent(); + FRAME = 0; + goalForceSpeed = 3.5f; + radius = 0.3f; + mass = 1; + perceptionRadius = 3; + + gameObject.transform.localScale = new Vector3(2 * radius, 1, 2 * radius); + nma.radius = radius; + rb.mass = mass; + GetComponent().radius = perceptionRadius / 2; + } + + void FixedUpdate() + { + + if (returnHome && Vector3.Distance(transform.position, homeDestination) > 7f) + { + + }else{ + returnHome = false; + if(FRAME % PATHFINDING_FRAME_SKIP == 0){ + float minDistance = Mathf.Infinity; + Vector3 closestPursuer = Vector3.zero; + + foreach(GameObject pursuer in pursueAgents){ + float d = Vector3.Distance(pursuer.transform.position, transform.position); + if(d < minDistance){ + minDistance = d; + closestPursuer = pursuer.transform.position; + } + } + direction = closestPursuer; + /* + Vector3 oppositeDirection = closestPursuer * 5; + NavMeshHit navHit; + Vector3 dest = Vector3.zero; + if (NavMesh.SamplePosition(oppositeDirection + transform.position, out navHit, 7.0f, NavMesh.AllAreas)) + { + dest = navHit.position; + }else{ + dest = new Vector3(0f, 1f, 0f); + } + /* + ComputePath(destination); + */ + //path.Add(dest); + FRAME = 0; + }else{ + FRAME++; + } + } + ApplyForce(); + } + + public void ComputePath(Vector3 destination) + { + nma.enabled = true; + var nmPath = new NavMeshPath(); + nma.CalculatePath(destination, nmPath); + path = nmPath.corners.Skip(1).ToList(); + goalForceSpeed = nma.speed; + nma.enabled = false; + } + + public Vector3 GetVelocity() + { + return rb.velocity; + } + + private Vector3 ComputeForce() + { + Vector3 gForce = CalculateGoalForce(); + Vector3 aForce = CalculateAgentForce(); + Vector3 wForce = CalculateWallForce(); + + if(Vector3.Angle(gForce, aForce) == 0 && wForce != Vector3.zero && aForce != Vector3.zero){ + returnHome = true; + } + + Debug.DrawLine(transform.position, transform.position + gForce, Color.red, 0.5f); + Debug.DrawLine(transform.position, transform.position + aForce, Color.green, 0.5f); + Debug.DrawLine(transform.position, transform.position + wForce, Color.blue, 0.5f); + + return gForce + aForce + wForce; + } + + private Vector3 CalculateGoalForce() + { + // calculate the path to the destination and take the first two corners. + // return the unit vector in that direction and multiplied by a constant scalar. + Vector3 curPath = Vector3.zero; + if(returnHome){ + curPath = (homeDestination - transform.position).normalized; + }else{ + curPath = (transform.position - direction).normalized; + } + //Return the (Vi_0 * ei_0) - Vi + return ((goalForceSpeed * curPath) - GetVelocity()); + } + + private Vector3 CalculateAgentForce() + { + Vector3 agentForce = Vector3.zero; + foreach (GameObject neighbor in perceivedNeighbors) + { + Vector3 repForce = Vector3.zero; + Vector3 slideForce = Vector3.zero; + Vector3 opos = neighbor.transform.position; + Rigidbody oagent = neighbor.GetComponent(); + + float r_ij = 2 * radius; + float d_ij = Vector3.Distance(opos,transform.position); + float g = (r_ij - d_ij > 0.000001) ? (r_ij- d_ij) : 0; + Vector3 n_ij = (transform.position - opos)/d_ij; + Vector3 t_ij = new Vector3(-n_ij.z, n_ij.x, 0); + float deltaV_ji = Vector3.Dot((oagent.velocity - GetVelocity()), t_ij); + + repForce = (A * Mathf.Exp((r_ij - d_ij)/B) + kappa*g) * n_ij; + slideForce = k * g * deltaV_ji * t_ij; + + agentForce += (repForce + slideForce); + } + + return agentForce; + } + + private Vector3 CalculateWallForce() + { + + // Forces on the agent will be perpendicular to the walls closest surface + Vector3 WallForce = Vector3.zero; + foreach (GameObject Wall in perceivedWalls) + { + Vector3 repForce = Vector3.zero; + Vector3 slideForce = Vector3.zero; + + float r_i = radius; + float d_iw = Vector3.Distance(Wall.transform.position, transform.position); + float g = (r_i - d_iw > 0.000001) ? (r_i - d_iw) : 0; + Vector3 n_iw = Vector3.zero; + + Vector3 direction = (Wall.transform.position - rb.transform.position).normalized; + float dotEast = Vector3.Dot(transform.right, direction); + float dotNorth = Vector3.Dot(transform.forward, direction); + if (Mathf.Abs(dotEast) > Mathf.Abs(dotNorth)) + { + // Wall is east/west of Agent + if(dotEast < 0) + n_iw = Wall.transform.right; // Will be negative if west, positive if east //The inverse by dividing by a number means the force gets stronger as the agent is closer to the wall + else + n_iw = -Wall.transform.right; + } + else + { + // Wall is north/south of Agent + if(dotNorth < 0) + n_iw = Wall.transform.forward; + else + n_iw = -Wall.transform.forward; + // Will be negative if south, positive if North + } + Vector3 t_iw = new Vector3(-n_iw.z, n_iw.x, 0); + + repForce = (A * Mathf.Exp((r_i - d_iw)/B) + kappa*g) * n_iw; + slideForce = k * g * Vector3.Dot(GetVelocity(), t_iw) * t_iw; + WallForce += (repForce - slideForce); + } + return WallForce; + } + + public void ApplyForce() + { + var force = ComputeForce(); + force.y = 0; + rb.AddForce(force * 10, ForceMode.Force); + } + + public void OnTriggerEnter(Collider other) + { + if(other.gameObject.CompareTag("Agent")){ + perceivedNeighbors.Add(other.gameObject); + } else if(WallManager.IsWall(other.gameObject)){ + perceivedWalls.Add(other.gameObject); + } + } + + public void OnTriggerExit(Collider other) + { + if(other.gameObject.CompareTag("Agent")){ + perceivedNeighbors.Remove(other.gameObject); + } else if(WallManager.IsWall(other.gameObject)){ + perceivedWalls.Remove(other.gameObject); + } + } + + public void OnCollisionEnter(Collision collision) + { + /* + if(WallManager.IsWall(collision.transform.gameObject)){ + perceivedWalls.Add(collision.transform.gameObject); + } + */ + } + + public void OnCollisionExit(Collision collision) + { + /* + if(WallManager.IsWall(collision.transform.gameObject)){ + perceivedWalls.Remove(collision.transform.gameObject); + } + */ + } +} diff --git a/B6 Scripts/EvadeAgent.cs.meta b/B6 Scripts/EvadeAgent.cs.meta new file mode 100644 index 0000000..ce32321 --- /dev/null +++ b/B6 Scripts/EvadeAgent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8737b03060d9446f19636a5c3754805b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B6 Scripts/FollowerAgent.cs b/B6 Scripts/FollowerAgent.cs new file mode 100644 index 0000000..a63f739 --- /dev/null +++ b/B6 Scripts/FollowerAgent.cs @@ -0,0 +1,216 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.AI; + +public class FollowerAgent : MonoBehaviour +{ + public float radius; + public float mass; + public float perceptionRadius; + public float goalWeight = 5; + public float goalForceSpeed; + + float A = 2.0f; + float B = 0.6f; + float k = 1.2f; + float kappa = 2.4f; + + private List path; + private NavMeshAgent nma; + private Rigidbody rb; + private SphereCollider sc; + private HashSet perceivedNeighbors = new HashSet(); + private HashSet perceivedWalls = new HashSet(); + + void Start() + { + path = new List(); + nma = GetComponent(); + rb = GetComponent(); + sc = GetComponent(); + + gameObject.transform.localScale = new Vector3(2 * radius, 1, 2 * radius); + nma.radius = radius; + rb.mass = mass; + GetComponent().radius = perceptionRadius / 2; + } + + private void Update() + { + if (path.Count > 0 && Vector3.Distance(transform.position, path[0]) < 2.1f) + { + path.RemoveAt(0); + } + } + + #region Public Functions + + public void ComputePath(Vector3 destination) + { + nma.enabled = true; + var nmPath = new NavMeshPath(); + nma.CalculatePath(destination, nmPath); + path = nmPath.corners.Skip(1).ToList(); + goalForceSpeed = nma.speed; + nma.enabled = false; + } + + public Vector3 GetVelocity() + { + return rb.velocity; + } + + #endregion + + #region Incomplete Functions + + private Vector3 ComputeForce() + { + Vector3 gForce = CalculateGoalForce(); + + CleanNeighbors(); + Vector3 aForce = CalculateAgentForce(); + + Vector3 wForce = CalculateWallForce(); + + Debug.DrawLine(transform.position, transform.position + gForce, Color.red, 0.5f); + Debug.DrawLine(transform.position, transform.position + aForce, Color.green, 0.5f); + Debug.DrawLine(transform.position, transform.position + wForce, Color.blue, 0.5f); + + return gForce + aForce + wForce; + } + + private Vector3 CalculateGoalForce() + { + // calculate the path to the destination and take the first two corners. + // return the unit vector in that direction and multiplied by a constant scalar. + if(path.Count == 0) + return Vector3.zero - GetVelocity(); + + Vector3 ei_0 = (new Vector3(path[0].x, transform.position.y, path[0].z) - transform.position).normalized; + //Return the (Vi_0 * ei_0) - Vi + return ((goalForceSpeed * ei_0) - GetVelocity()); + } + + private Vector3 CalculateAgentForce() + { + Vector3 agentForce = Vector3.zero; + foreach (GameObject neighbor in perceivedNeighbors) + { + Vector3 repForce = Vector3.zero; + Vector3 slideForce = Vector3.zero; + Vector3 opos = neighbor.transform.position; + float oRadius = 0f; + Vector3 oVelocity = Vector3.zero; + + if(neighbor.GetComponent()){ + FollowerAgent oagent = neighbor.GetComponent(); + oRadius = oagent.radius; + oVelocity = oagent.GetVelocity(); + } + else if(neighbor.GetComponent()){ + LeaderAgent oLeader = neighbor.GetComponent(); + oRadius = oLeader.radius; + oVelocity = oLeader.GetVelocity(); + } + + float r_ij = oRadius + radius; + float d_ij = Vector3.Distance(opos,transform.position); + float g = (r_ij - d_ij > 0.000001) ? (r_ij- d_ij) : 0; + Vector3 n_ij = (transform.position - opos)/d_ij; + Vector3 t_ij = new Vector3(-n_ij.z, n_ij.x, 0); + float deltaV_ji = Vector3.Dot((oVelocity - GetVelocity()), t_ij); + + repForce = (A * Mathf.Exp((r_ij - d_ij)/B) + kappa*g) * n_ij; + slideForce = k * g * deltaV_ji * t_ij; + + agentForce += (repForce + slideForce); + } + + return agentForce; + } + + private Vector3 CalculateWallForce() + { + + // Forces on the agent will be perpendicular to the walls closest surface + Vector3 WallForce = Vector3.zero; + foreach (GameObject Wall in perceivedWalls) + { + Vector3 repForce = Vector3.zero; + Vector3 slideForce = Vector3.zero; + + float r_i = radius; + float d_iw = Vector3.Distance(Wall.transform.position, transform.position); + float g = (r_i - d_iw > 0.000001) ? (r_i - d_iw) : 0; + Vector3 n_iw = Vector3.zero; + + Vector3 direction = (Wall.transform.position - rb.transform.position).normalized; + float dotEast = Vector3.Dot(Vector3.right, direction); + float dotNorth = Vector3.Dot(Vector3.forward, direction); + if (Mathf.Abs(dotEast) > Mathf.Abs(dotNorth)) + { + // Wall is east/west of Agent + if(dotEast < 0) + n_iw = Wall.transform.right; // Will be negative if west, positive if east //The inverse by dividing by a number means the force gets stronger as the agent is closer to the wall + else + n_iw = -Wall.transform.right; + } + else + { + // Wall is north/south of Agent + if(dotNorth < 0) + n_iw = Wall.transform.forward; + else + n_iw = -Wall.transform.forward; + // Will be negative if south, positive if North + } + Vector3 t_iw = new Vector3(-n_iw.z, n_iw.x, 0); + + repForce = (A * Mathf.Exp((r_i - d_iw)/B) + kappa*g) * n_iw; + slideForce = k * g * Vector3.Dot(GetVelocity(), t_iw) * t_iw; + WallForce += (repForce - slideForce); + } + return WallForce; + } + + //OnTriggerExit is not called if the GameObject is deactivated or destroyed. + //Function will clean up perceivedNeighbors that were deactivated while still in range of Agent. + private void CleanNeighbors(){ + HashSet excludeNeighbors = new HashSet(); + foreach(GameObject neighbor in perceivedNeighbors){ + if(!neighbor.activeSelf) + excludeNeighbors.Add(neighbor); + } + perceivedNeighbors.ExceptWith(excludeNeighbors); + } + + public void ApplyForce() + { + var force = ComputeForce(); + force.y = 0; + rb.AddForce(force * 10, ForceMode.Force); + } + + public void OnTriggerEnter(Collider other) + { + if(other.gameObject.CompareTag("Agent")){ + perceivedNeighbors.Add(other.gameObject); + } else if(WallManager.IsWall(other.gameObject)){ + perceivedWalls.Add(other.gameObject); + } + } + + public void OnTriggerExit(Collider other) + { + if(other.gameObject.CompareTag("Agent")){ + perceivedNeighbors.Remove(other.gameObject); + } else if(WallManager.IsWall(other.gameObject)){ + perceivedWalls.Remove(other.gameObject); + } + } + + #endregion +} diff --git a/B6 Scripts/FollowerAgent.cs.meta b/B6 Scripts/FollowerAgent.cs.meta new file mode 100644 index 0000000..e243447 --- /dev/null +++ b/B6 Scripts/FollowerAgent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0204925323c2245288622f91421600ed +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B6 Scripts/LFAgentManager.cs b/B6 Scripts/LFAgentManager.cs new file mode 100644 index 0000000..11f10f8 --- /dev/null +++ b/B6 Scripts/LFAgentManager.cs @@ -0,0 +1,188 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.AI; + +public class LFAgentManager : MonoBehaviour +{ + public int agentCount = 10; + public float agentSpawnRadius = 20; + public GameObject agentPrefab; + public GameObject leaderPrefab; + public static Dictionary agentsObjs = new Dictionary(); + + private LeaderAgent leader; + private static List agents = new List(); + private GameObject agentParent; + private Vector3 destination; + + public const float UPDATE_RATE = 0.0f; + private const int PATHFINDING_FRAME_SKIP = 25; + + public bool spiralFlag = false; + public float CrowdFollow_Param = 0f; + + #region Unity Functions + + void Awake() + { + Random.InitState(0); + + agentParent = GameObject.Find("Agents"); + for (int i = 0; i < agentCount; i++) + { + var randPos = new Vector3((Random.value - 0.5f) * agentSpawnRadius, 0, (Random.value - 0.5f) * agentSpawnRadius); + NavMeshHit hit; + NavMesh.SamplePosition(randPos, out hit, 10, NavMesh.AllAreas); + randPos = hit.position + Vector3.up; + + GameObject agent = null; + agent = Instantiate(agentPrefab, randPos, Quaternion.identity); + agent.name = "Agent " + i; + agent.transform.parent = agentParent.transform; + var agentScript = agent.GetComponent(); + agentScript.radius = 0.3f;// Random.Range(0.2f, 0.6f); + agentScript.mass = 1; + agentScript.perceptionRadius = 3; + agent.SetActive(true); + agents.Add(agentScript); + agentsObjs.Add(agent, agentScript); + } + + leader = leaderPrefab.GetComponent(); + + StartCoroutine(Run()); + } + + void Update() + { + #region Visualization + + if (Input.GetMouseButtonDown(0)) + { + if (true) + { + var point = Camera.main.ScreenToWorldPoint(Input.mousePosition + Vector3.forward * 10); + var dir = point - Camera.main.transform.position; + RaycastHit rcHit; + if (Physics.Raycast(point, dir, out rcHit)) + { + NavMeshHit navHit; + if (NavMesh.SamplePosition(rcHit.point, out navHit, 1.0f, NavMesh.AllAreas)) + { + SetLeaderDestination(navHit.position); + } + } + } else + { + var randPos = new Vector3((Random.value - 0.5f) * agentSpawnRadius, 0, (Random.value - 0.5f) * agentSpawnRadius); + + NavMeshHit hit; + NavMesh.SamplePosition(randPos, out hit, 1.0f, NavMesh.AllAreas); + print(hit.position); + Debug.DrawLine(hit.position, hit.position + Vector3.up * 10, Color.red, 1000000); + foreach (var agent in agents) + { + //agent.ComputePath(hit.position); + } + } + } + +#if UNITY_EDITOR + if (Application.isFocused) + { + //UnityEditor.SceneView.FocusWindowIfItsOpen(typeof(UnityEditor.SceneView)); + } +#endif + + #endregion + } + + IEnumerator Run() + { + yield return null; + + for (int iterations = 0; ; iterations++) + { + if (iterations % PATHFINDING_FRAME_SKIP == 0) + { + SetAgentDestinations(leaderPrefab.transform.position); + } + + foreach (var agent in agents) + { + agent.ApplyForce(); + } + + leader.ApplyForce(); + + if (UPDATE_RATE == 0) + { + yield return null; + } else + { + yield return new WaitForSeconds(UPDATE_RATE); + } + } + } + + #endregion + + #region Public Functions + + public static bool IsAgent(GameObject obj) + { + return agentsObjs.ContainsKey(obj); + } + + public void SetLeaderDestination(Vector3 NewDestination){ + NavMeshHit hit; + NavMesh.SamplePosition(NewDestination, out hit, 10, NavMesh.AllAreas); + leader.ComputePath(hit.position); + } + + public void SetAgentDestinations(Vector3 NewDestination) + { + destination = NewDestination; + NavMeshHit hit; + NavMesh.SamplePosition(NewDestination, out hit, 10, NavMesh.AllAreas); + foreach (var agent in agents) + { + agent.ComputePath(hit.position); + } + + } + + public static void RemoveAgent(GameObject obj) + { + var agent = obj.GetComponent(); + + agents.Remove(agent); + agentsObjs.Remove(obj); + } + #endregion + + #region Private Functions + + #endregion + + #region Visualization Functions + + #endregion + + #region Utility Classes + + private class Tuple + { + public K Item1; + public V Item2; + + public Tuple(K k, V v) { + Item1 = k; + Item2 = v; + } + } + + #endregion +} diff --git a/B6 Scripts/LFAgentManager.cs.meta b/B6 Scripts/LFAgentManager.cs.meta new file mode 100644 index 0000000..b8c6c03 --- /dev/null +++ b/B6 Scripts/LFAgentManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2aa5ba0f87d084d719bd53f7d7bd4e46 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B6 Scripts/LeaderAgent.cs b/B6 Scripts/LeaderAgent.cs new file mode 100644 index 0000000..a5249b7 --- /dev/null +++ b/B6 Scripts/LeaderAgent.cs @@ -0,0 +1,219 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.AI; + +public class LeaderAgent : MonoBehaviour +{ + public float radius; + public float mass; + public float perceptionRadius; + public float goalWeight = 5; + public float goalForceSpeed; + + float A = 2.0f; + float B = 0.6f; + float k = 1.2f; + float kappa = 2.4f; + + private List path; + private NavMeshAgent nma; + private Rigidbody rb; + private SphereCollider sc; + private HashSet perceivedNeighbors = new HashSet(); + private HashSet perceivedWalls = new HashSet(); + + void Start() + { + path = new List(); + nma = GetComponent(); + rb = GetComponent(); + sc = GetComponent(); + + gameObject.transform.localScale = new Vector3(2 * radius, 1, 2 * radius); + nma.radius = radius; + rb.mass = mass; + GetComponent().radius = perceptionRadius / 2; + } + + private void Update() + { + if (path.Count > 0 && Vector3.Distance(transform.position, path[0]) < 1.1f) + { + path.RemoveAt(0); + } + } + + #region Public Functions + + public void ComputePath(Vector3 destination) + { + nma.enabled = true; + var nmPath = new NavMeshPath(); + nma.CalculatePath(destination, nmPath); + path = nmPath.corners.Skip(1).ToList(); + goalForceSpeed = nma.speed; + nma.enabled = false; + } + + public Vector3 GetVelocity() + { + return rb.velocity; + } + + #endregion + + #region Incomplete Functions + + private Vector3 ComputeForce() + { + Vector3 gForce = CalculateGoalForce(); + + Vector3 wForce = CalculateWallForce(); + + Debug.DrawLine(transform.position, transform.position + gForce, Color.red, 0.5f); + Debug.DrawLine(transform.position, transform.position + wForce, Color.blue, 0.5f); + + return gForce + wForce; + } + + private Vector3 CalculateGoalForce() + { + // calculate the path to the destination and take the first two corners. + // return the unit vector in that direction and multiplied by a constant scalar. + if(path.Count == 0) + return Vector3.zero - GetVelocity(); + Vector3 ei_0 = (new Vector3(path[0].x, transform.position.y, path[0].z) - transform.position).normalized; + //Return the (Vi_0 * ei_0) - Vi + return ((goalForceSpeed * ei_0) - GetVelocity()); + } + /* + private Vector3 CalculateAgentForce() + { + Vector3 agentForce = Vector3.zero; + foreach (GameObject neighbor in perceivedNeighbors) + { + Vector3 repForce = Vector3.zero; + Vector3 slideForce = Vector3.zero; + Vector3 opos = neighbor.transform.position; + Agent oagent = neighbor.GetComponent(); + + float r_ij = oagent.radius + radius; + float d_ij = Vector3.Distance(opos,transform.position); + float g = (r_ij - d_ij > 0.000001) ? (r_ij- d_ij) : 0; + Vector3 n_ij = (transform.position - opos)/d_ij; + Vector3 t_ij = new Vector3(-n_ij.z, n_ij.x, 0); + float deltaV_ji = Vector3.Dot((oagent.GetVelocity() - GetVelocity()), t_ij); + + repForce = (A * Mathf.Exp((r_ij - d_ij)/B) + kappa*g) * n_ij; + slideForce = k * g * deltaV_ji * t_ij; + + agentForce += (repForce + slideForce); + } + + return agentForce; + } + */ + private Vector3 CalculateWallForce() + { + + // Forces on the agent will be perpendicular to the walls closest surface + Vector3 WallForce = Vector3.zero; + foreach (GameObject Wall in perceivedWalls) + { + Vector3 repForce = Vector3.zero; + Vector3 slideForce = Vector3.zero; + + float r_i = radius; + float d_iw = Vector3.Distance(Wall.transform.position, transform.position); + float g = (r_i - d_iw > 0.000001) ? (r_i - d_iw) : 0; + Vector3 n_iw = Vector3.zero; + + Vector3 direction = (Wall.transform.position - rb.transform.position).normalized; + float dotEast = Vector3.Dot(Vector3.right, direction); + float dotNorth = Vector3.Dot(Vector3.forward, direction); + if (Mathf.Abs(dotEast) > Mathf.Abs(dotNorth)) + { + // Wall is east/west of Agent + if(dotEast < 0) + n_iw = Wall.transform.right; // Will be negative if west, positive if east //The inverse by dividing by a number means the force gets stronger as the agent is closer to the wall + else + n_iw = -Wall.transform.right; + } + else + { + // Wall is north/south of Agent + if(dotNorth < 0) + n_iw = Wall.transform.forward; + else + n_iw = -Wall.transform.forward; + // Will be negative if south, positive if North + } + Vector3 t_iw = new Vector3(-n_iw.z, n_iw.x, 0); + + repForce = (A * Mathf.Exp((r_i - d_iw)/B) + kappa*g) * n_iw; + slideForce = k * g * Vector3.Dot(GetVelocity(), t_iw) * t_iw; + WallForce += (repForce - slideForce); + } + return WallForce; + } + + /* + //OnTriggerExit is not called if the GameObject is deactivated or destroyed. + //Function will clean up perceivedNeighbors that were deactivated while still in range of Agent. + private void CleanNeighbors(){ + HashSet excludeNeighbors = new HashSet(); + foreach(GameObject neighbor in perceivedNeighbors){ + if(!neighbor.activeSelf) + excludeNeighbors.Add(neighbor); + } + perceivedNeighbors.ExceptWith(excludeNeighbors); + } + */ + public void ApplyForce() + { + var force = ComputeForce(); + force.y = 0; + rb.AddForce(force * 10, ForceMode.Force); + } + + public void OnTriggerEnter(Collider other) + { /* + if(AgentManager.IsAgent(other.gameObject)){ + perceivedNeighbors.Add(other.gameObject); + } else*/ + if(WallManager.IsWall(other.gameObject)){ + perceivedWalls.Add(other.gameObject); + } + } + + public void OnTriggerExit(Collider other) + { /* + if(AgentManager.IsAgent(other.gameObject)){ + perceivedNeighbors.Remove(other.gameObject); + } else */ + if(WallManager.IsWall(other.gameObject)){ + perceivedWalls.Remove(other.gameObject); + } + } + + public void OnCollisionEnter(Collision collision) + { + /* + if(WallManager.IsWall(collision.transform.gameObject)){ + perceivedWalls.Add(collision.transform.gameObject); + }*/ + } + + public void OnCollisionExit(Collision collision) + { + /* + if(WallManager.IsWall(collision.transform.gameObject)){ + perceivedWalls.Remove(collision.transform.gameObject); + } + */ + } + + #endregion +} diff --git a/B6 Scripts/LeaderAgent.cs.meta b/B6 Scripts/LeaderAgent.cs.meta new file mode 100644 index 0000000..c751394 --- /dev/null +++ b/B6 Scripts/LeaderAgent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ecbc8d03e751e4af09660ff04f57f205 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B6 Scripts/Parameters.cs b/B6 Scripts/Parameters.cs new file mode 100644 index 0000000..beb8bd9 --- /dev/null +++ b/B6 Scripts/Parameters.cs @@ -0,0 +1,19 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public struct Parameters +{ + public const float T = 0.5f; + public const float A = 2000f; + public const float B = 0.08f; + public const float k = 1.2f * 100000f; + public const float Kappa = 2.4f * 100000f; + + public const float WALL_A = 2000f; + public const float WALL_B = 0.08f; + public const float WALL_k = 1.2f * 100000f; + public const float WALL_Kappa = 2.4f * 100000f; + + public const float maxSpeed = 100; +} \ No newline at end of file diff --git a/B6 Scripts/Parameters.cs.meta b/B6 Scripts/Parameters.cs.meta new file mode 100644 index 0000000..08074b5 --- /dev/null +++ b/B6 Scripts/Parameters.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ba3db8e2c60768a46aae8987cdd2ac93 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B6 Scripts/PointsManager.cs b/B6 Scripts/PointsManager.cs new file mode 100644 index 0000000..787f8ac --- /dev/null +++ b/B6 Scripts/PointsManager.cs @@ -0,0 +1,55 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; +public class PointsManager : MonoBehaviour +{ + public GameObject endScreen; + private List cPoints; + private RTSAgentManager management; + private cPointBehavior redBase; + private cPointBehavior greenBase; + public AudioManager audioManager; + + // Start is called before the first frame update + void Start() + { + Time.timeScale = 0; + + cPoints = new List(); + GameObject[] points = GameObject.FindGameObjectsWithTag("CapturePoints"); + foreach (GameObject p in points) + { + cPoints.Add(p.GetComponent()); + } + + management = this.transform.GetComponent(); + redBase = GameObject.Find("RedBase").GetComponent(); + greenBase = GameObject.Find("GreenBase").GetComponent(); + if(audioManager != null) + audioManager.Play("BattleMusic"); + } + + public static bool isCpoint(GameObject obj) + { + return obj.GetComponent(); + } + // Update is called once per frame + void Update() + { + if (!redBase.owner.Equals("Red") || !greenBase.owner.Equals("Green")) + { + Time.timeScale = 0; + endScreen.SetActive(true); + } + int multR = 0; + foreach (cPointBehavior c in cPoints) + { + if (c.owner.Equals("Red")) + { + multR++; + } + } + management.resourceModifier = 1 +multR*0.5f; + } +} diff --git a/B6 Scripts/PointsManager.cs.meta b/B6 Scripts/PointsManager.cs.meta new file mode 100644 index 0000000..2beab3a --- /dev/null +++ b/B6 Scripts/PointsManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f0b38c7008839654a88ebbf01131c10c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 400 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B6 Scripts/PursueAgent.cs b/B6 Scripts/PursueAgent.cs new file mode 100644 index 0000000..84707d0 --- /dev/null +++ b/B6 Scripts/PursueAgent.cs @@ -0,0 +1,227 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.AI; + +public class PursueAgent : MonoBehaviour +{ + public float radius; + public float mass; + public float perceptionRadius; + public float goalWeight = 5; + public float goalForceSpeed; + + public List evadeAgents = new List(); + + float A = 3.1f; + float B = 0.8f; + float k = 1.2f; + float kappa = 5.4f; + + private Vector3 destination; + private List path; + private NavMeshAgent nma; + private Rigidbody rb; + private SphereCollider sc; + private HashSet perceivedNeighbors = new HashSet(); + private HashSet perceivedWalls = new HashSet(); + + private int FRAME; + private const int PATHFINDING_FRAME_SKIP = 25; + + void Start() + { + destination = Vector3.zero; + path = new List(); + nma = GetComponent(); + rb = GetComponent(); + sc = GetComponent(); + FRAME = 0; + + radius = 0.3f; + mass = 1; + perceptionRadius = 3; + + gameObject.transform.localScale = new Vector3(2 * radius, 1, 2 * radius); + nma.radius = radius; + rb.mass = mass; + GetComponent().radius = perceptionRadius / 2; + } + + void FixedUpdate() + { + if (path.Count > 0 && Vector3.Distance(transform.position, path[0]) < 1.1f) + { + path.RemoveAt(0); + } + + if(path.Count == 0){ + float minDistance = Mathf.Infinity; + Vector3 closestTarget = Vector3.zero; + + foreach(GameObject evader in evadeAgents){ + float d = Vector3.Distance(evader.transform.position, transform.position); + if(d < minDistance){ + minDistance = d; + closestTarget = evader.transform.position; + } + } + closestTarget.y = 0; + destination = closestTarget; + + ComputePath(destination); + }else{ + FRAME++; + } + + ApplyForce(); + } + + public void ComputePath(Vector3 destination) + { + nma.enabled = true; + var nmPath = new NavMeshPath(); + nma.CalculatePath(destination, nmPath); + path = nmPath.corners.Skip(1).ToList(); + goalForceSpeed = nma.speed; + nma.enabled = false; + } + + public Vector3 GetVelocity() + { + return rb.velocity; + } + + private Vector3 ComputeForce() + { + Vector3 gForce = CalculateGoalForce(); + Vector3 aForce = CalculateAgentForce(); + Vector3 wForce = CalculateWallForce(); + + Debug.DrawLine(transform.position, transform.position + gForce, Color.red, 0.5f); + Debug.DrawLine(transform.position, transform.position + aForce, Color.green, 0.5f); + Debug.DrawLine(transform.position, transform.position + wForce, Color.blue, 0.5f); + Debug.DrawLine(transform.position, transform.position + (gForce+aForce+wForce), Color.black, 0.5f); + + return (gForce + aForce + wForce); + } + + private Vector3 CalculateGoalForce() + { + // calculate the path to the destination and take the first two corners. + // return the unit vector in that direction and multiplied by a constant scalar. + Vector3 curPath = (new Vector3(path[0].x, transform.position.y, path[0].z) - transform.position).normalized; + //Return the (Vi_0 * ei_0) - Vi + return ((goalForceSpeed * curPath) - GetVelocity()); + } + + private Vector3 CalculateAgentForce() + { + Vector3 agentForce = Vector3.zero; + foreach (GameObject neighbor in perceivedNeighbors) + { + Vector3 repForce = Vector3.zero; + Vector3 slideForce = Vector3.zero; + Vector3 opos = neighbor.transform.position; + Rigidbody oagent = neighbor.GetComponent(); + + float r_ij = 2 * radius; + float d_ij = Vector3.Distance(opos,transform.position); + float g = (r_ij - d_ij > 0.000001) ? (r_ij- d_ij) : 0; + Vector3 n_ij = (transform.position - opos)/d_ij; + Vector3 t_ij = new Vector3(-n_ij.z, n_ij.x, 0); + float deltaV_ji = Vector3.Dot((oagent.velocity - GetVelocity()), t_ij); + + repForce = (A * Mathf.Exp((r_ij - d_ij)/B) + kappa*g) * n_ij; + slideForce = k * g * deltaV_ji * t_ij; + + agentForce += (repForce + slideForce); + } + + return agentForce; + } + + private Vector3 CalculateWallForce() + { + + // Forces on the agent will be perpendicular to the walls closest surface + Vector3 WallForce = Vector3.zero; + foreach (GameObject Wall in perceivedWalls) + { + Vector3 repForce = Vector3.zero; + Vector3 slideForce = Vector3.zero; + + float r_i = radius; + float d_iw = Vector3.Distance(Wall.transform.position, transform.position); + float g = (r_i - d_iw > 0.000001) ? (r_i - d_iw) : 0; + Vector3 n_iw = Vector3.zero; + + Vector3 direction = (Wall.transform.position - rb.transform.position).normalized; + float dotEast = Vector3.Dot(transform.right, direction); + float dotNorth = Vector3.Dot(transform.forward, direction); + if (Mathf.Abs(dotEast) > Mathf.Abs(dotNorth)) + { + // Wall is east/west of Agent + if(dotEast < 0) + n_iw = Wall.transform.right; // Will be negative if west, positive if east //The inverse by dividing by a number means the force gets stronger as the agent is closer to the wall + else + n_iw = -Wall.transform.right; + } + else + { + // Wall is north/south of Agent + if(dotNorth < 0) + n_iw = Wall.transform.forward; + else + n_iw = -Wall.transform.forward; + // Will be negative if south, positive if North + } + Vector3 t_iw = new Vector3(-n_iw.z, n_iw.x, 0); + + repForce = (A * Mathf.Exp((r_i - d_iw)/B) + kappa*g) * n_iw; + slideForce = k * g * Vector3.Dot(GetVelocity(), t_iw) * t_iw; + WallForce += (repForce - slideForce); + } + return WallForce; + } + + public void ApplyForce() + { + var force = ComputeForce(); + force.y = 0; + rb.AddForce(force * 10, ForceMode.Force); + } + + public void OnTriggerEnter(Collider other) + { + if(other.gameObject.CompareTag("Agent")){ + perceivedNeighbors.Add(other.gameObject); + } else if(WallManager.IsWall(other.gameObject)){ + perceivedWalls.Add(other.gameObject); + } + } + + public void OnTriggerExit(Collider other) + { + if(other.gameObject.CompareTag("Agent")){ + perceivedNeighbors.Remove(other.gameObject); + } else if(WallManager.IsWall(other.gameObject)){ + perceivedWalls.Remove(other.gameObject); + } + } + + public void OnCollisionEnter(Collision collision) + { + /*if(WallManager.IsWall(collision.transform.gameObject)){ + perceivedWalls.Add(collision.transform.gameObject); + }*/ + } + + public void OnCollisionExit(Collision collision) + {/* + if(WallManager.IsWall(collision.transform.gameObject)){ + perceivedWalls.Remove(collision.transform.gameObject); + }*/ + } +} diff --git a/B6 Scripts/PursueAgent.cs.meta b/B6 Scripts/PursueAgent.cs.meta new file mode 100644 index 0000000..29c4dc4 --- /dev/null +++ b/B6 Scripts/PursueAgent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4f6b9e84decc44b96b3c2579e52bdd90 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B6 Scripts/RTSAgentManager.cs b/B6 Scripts/RTSAgentManager.cs new file mode 100644 index 0000000..cc28a84 --- /dev/null +++ b/B6 Scripts/RTSAgentManager.cs @@ -0,0 +1,304 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.AI; +using UnityEngine.UI; + +public class RTSAgentManager : MonoBehaviour +{ + public float resourceModifier = 1; + public float agentSpawnRadius = 20; + public GameObject agentPrefab; + public static Dictionary agentsObjs = new Dictionary(); + public float buildResources; + + private static List agents = new List(); + private static List selected = new List(); + private GameObject agentParent; + private Vector3 destination; + public float time = 0; + + public const float UPDATE_RATE = 0.0f; + private const int PATHFINDING_FRAME_SKIP = 25; + + public bool spiralFlag = false; + public bool flocking = true; + + public float CrowdFollow_Param = 0f; + + public Material selectedMaterial; + public Material unselectedMaterial; + public Text buildCount; + + private Vector3 mouseDownPosition = Vector3.zero; + + #region Unity Functions + + void Awake() + { + Random.InitState(0); + buildResources = 10; + + agentParent = GameObject.Find("Agents"); + + StartCoroutine(Run()); + } + + void Update() + { + #region Visualization + if (Input.GetMouseButtonDown(0)) + { + mouseDownPosition = Input.mousePosition; + } + if (Input.GetMouseButtonUp(0)) + { + if (Input.mousePosition == mouseDownPosition) + { + var point = Camera.main.ScreenToWorldPoint(Input.mousePosition + Vector3.forward * 10); + var dir = point - Camera.main.transform.position; + Debug.DrawRay(point,dir,Color.red,2f); + RaycastHit rcHit; + if (Physics.Raycast(point, dir, out rcHit)) + { + if (rcHit.transform.tag == "Agent") + { + ChangeSelectAgent(rcHit.transform.gameObject); + } + else + { + NavMeshHit navHit; + if (NavMesh.SamplePosition(rcHit.point, out navHit, 5.0f, NavMesh.AllAreas)) + { + destination = navHit.position; + SetAgentDestinations(destination); + } + } + } + } + } + if (Input.GetButtonUp("Fire1")) + { + if (buildResources > 0) + { + buildResources--; + spawnAgent(); + //Debug.Log("Fire1 pressed"); + } + else + { + //do something to alert the player + } + } + time += Time.deltaTime; + if (time >= 1) + { + time = 0; + buildResources += resourceModifier; + buildCount.text = "Resources: " + buildResources.ToString(); + } + + +#if UNITY_EDITOR + if (Application.isFocused) + { + //UnityEditor.SceneView.FocusWindowIfItsOpen(typeof(UnityEditor.SceneView)); + } +#endif + + #endregion + } + void spawnAgent() + { + + var spawnPos = agentParent.transform.position + Random.onUnitSphere; + spawnPos.y = 0; + NavMeshHit hit; + NavMesh.SamplePosition(spawnPos, out hit, 10, NavMesh.AllAreas); + spawnPos = hit.position + Vector3.up; + + GameObject agent = null; + agent = Instantiate(agentPrefab, spawnPos, Quaternion.identity); + agent.name = "Agent " + agents.Count; + agent.transform.parent = agentParent.transform; + agent.tag = "Agent"; + var agentScript = agent.GetComponent(); + agentScript.radius = 0.3f;// Random.Range(0.2f, 0.6f); + agentScript.mass = 1; + agentScript.perceptionRadius = 3; + if (spiralFlag) + { + agentScript.spiralFlag = true; + } + if (!flocking) + { + agentScript.flocking = false; + } + agentScript.crowdFollowParam = CrowdFollow_Param; + agents.Add(agentScript); + agentsObjs.Add(agent, agentScript); + selected.Add(agentScript); + } + IEnumerator Run() + { + yield return null; + + for (int iterations = 0; ; iterations++) + { + if (iterations % PATHFINDING_FRAME_SKIP == 0) + { + if (!spiralFlag) + { + ComputeAgentPath(); + } + } + + foreach (var agent in agents) + { + if (agent != null){ + agent.ApplyForce(); + } + } + + if (UPDATE_RATE == 0) + { + yield return null; + } + else + { + yield return new WaitForSeconds(UPDATE_RATE); + } + } + } + + #endregion + + #region Public Functions + + public bool IsAgent(GameObject obj) + { + return agentsObjs.ContainsKey(obj); + } + + public void SetAgentDestinations(Vector3 NewDestination) + { + destination = NewDestination; + NavMeshHit hit; + NavMesh.SamplePosition(NewDestination, out hit, 10, NavMesh.AllAreas); + foreach (var agent in selected) + { + agent.SetDestination(hit.position); + } + } + + public void ComputeAgentPath() + { + foreach (var agent in agents) + { + agent.ComputePath(); + } + } + + public void RemoveAgent(GameObject obj) + { + var agent = obj.GetComponent(); + + agents.Remove(agent); + agentsObjs.Remove(obj); + } + + public void SelectedAgents(Vector3 selection) + { + //Debug.Log(selection); + Vector3 worldDownPosition = Camera.main.ScreenToWorldPoint(mouseDownPosition + Vector3.forward * 10); + Vector3 worldUpPosition = Camera.main.ScreenToWorldPoint(selection + Vector3.forward * 10); + Vector3 dir = worldDownPosition - Camera.main.transform.position; + RaycastHit rcDownHit; + RaycastHit rcUpHit; + if (Physics.Raycast(worldDownPosition, dir, out rcDownHit)) + { + dir = worldUpPosition - Camera.main.transform.position; + if (Physics.Raycast(worldUpPosition, dir, out rcUpHit)) + { + worldDownPosition = rcDownHit.point; + worldUpPosition = rcUpHit.point; + //Debug.Log("World Down Position:" + worldDownPosition); + //Debug.Log("World Up Position:" + worldUpPosition); + if (!Input.GetKey(KeyCode.LeftShift)) + { + //Debug.Log("Here"); + foreach (Agent selectedAgent in agents) + { + if (selected.Contains(selectedAgent)) + { + selected.Remove(selectedAgent); + selectedAgent.transform.GetChild(1).gameObject.GetComponent().material = unselectedMaterial; + } + } + } + + float xRight = worldUpPosition.x > worldDownPosition.x ? worldDownPosition.x : worldUpPosition.x; + float xLeft = worldUpPosition.x > worldDownPosition.x ? worldUpPosition.x : worldDownPosition.x; + + float yUp = worldUpPosition.z > worldDownPosition.z ? worldDownPosition.z : worldUpPosition.z; + float yDown = worldUpPosition.z > worldDownPosition.z ? worldUpPosition.z : worldDownPosition.z; + //Debug.Log("xRight:" + xRight + " xLeft:" + xLeft + " yUp:" + yUp + " yDown:" + yDown); + foreach (Agent agent in agents) + { + Vector3 agentPosition = agent.transform.position; + //Debug.Log("Agent Position:" + agentPosition); + if (!selected.Contains(agent) && agentPosition.x >= xRight && agentPosition.x <= xLeft && agentPosition.z >= yUp && agentPosition.z <= yDown) + { + selected.Add(agent); + agent.transform.GetChild(1).gameObject.GetComponent().material = selectedMaterial; + //agent.GetComponent().material.shader = Shader.Find("UltimateOutline"); + } + } + } + + } + } + #endregion + + #region Private Functions + + void ChangeSelectAgent(GameObject agent) + { + Agent agScript = agent.GetComponent(); + if (selected.Contains(agScript)) + { + selected.Remove(agScript); + agent.GetComponent().material = unselectedMaterial; + + } + else + { + selected.Add(agScript); + + agent.GetComponent().material = selectedMaterial; + } + } + + #endregion + + #region Visualization Functions + + #endregion + + #region Utility Classes + + private class Tuple + { + public K Item1; + public V Item2; + + public Tuple(K k, V v) + { + Item1 = k; + Item2 = v; + } + } + + #endregion +} diff --git a/B6 Scripts/RTSAgentManager.cs.meta b/B6 Scripts/RTSAgentManager.cs.meta new file mode 100644 index 0000000..d0dcd8c --- /dev/null +++ b/B6 Scripts/RTSAgentManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5f2fa8bba9d9e3e4f88d091446ff4983 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 200 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B6 Scripts/RTSEAgentManager.cs b/B6 Scripts/RTSEAgentManager.cs new file mode 100644 index 0000000..52e9944 --- /dev/null +++ b/B6 Scripts/RTSEAgentManager.cs @@ -0,0 +1,323 @@ + +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.AI; + +public class RTSEAgentManager : MonoBehaviour //Responsible for Enemy Agent Behavior +{ + public float resourceModifier = 1; + public GameObject agentPrefab; + public static Dictionary agentsObjs = new Dictionary(); + public float buildResources; + + private static List agents = new List(); + private GameObject agentParent; + private Vector3 destination; + private float time = 0; + private List nearestCPoints; + private GameObject redBase; + + public const float UPDATE_RATE = 0.0f; + private const int PATHFINDING_FRAME_SKIP = 25; + + public bool spiralFlag = false; + public bool flocking = true; + + public float CrowdFollow_Param = 0f; + + public Material eMaterial; + + + #region Unity Functions + + void Awake() + { + Random.InitState(0); + buildResources = 10; + + agentParent = GameObject.Find("EAgents"); + redBase = GameObject.Find("RedBase"); + nearestCPoints = new List(GameObject.FindGameObjectsWithTag("CapturePoints")); + nearestCPoints = nearestCPoints.OrderBy(x => Vector2.Distance(this.transform.position, x.transform.position)).ToList(); + + StartCoroutine(Run()); + } + + void Update() + { + #region Visualization + /*if (Input.GetMouseButtonDown(0)) + { + mouseDownPosition = Input.mousePosition; + } + if (Input.GetMouseButtonUp(0)) + { + if (Input.mousePosition == mouseDownPosition) + { + var point = Camera.main.ScreenToWorldPoint(Input.mousePosition + Vector3.forward * 10); + var dir = point - Camera.main.transform.position; + + RaycastHit rcHit; + if (Physics.Raycast(point, dir, out rcHit)) + { + if (rcHit.transform.tag == "Agent") + { + ChangeSelectAgent(rcHit.transform.gameObject); + } + else + { + NavMeshHit navHit; + if (NavMesh.SamplePosition(rcHit.point, out navHit, 1.0f, NavMesh.AllAreas)) + { + destination = navHit.position; + SetAgentDestinations(destination); + } + } + } + } + } + if (Input.GetButtonUp("Fire1")) + { + if (buildResources > 0) + { + buildResources--; + spawnAgent(); + Debug.Log("Fire1 pressed"); + } + else + { + //do something to alert the player + } + }*/ + if (Time.deltaTime != 0) + { + time += Time.deltaTime; + if (time > 2) + { + time = 0; + buildResources += resourceModifier; + } + if (buildResources > 0) + { + buildResources--; + spawnAgent(); + } + foreach (GameObject cPoint in nearestCPoints) + { + if (!cPoint.GetComponent().owner.Equals("Green")) + { + SetAgentDestinations(cPoint.transform.position); + return; + } + } + if (!redBase.GetComponent().owner.Equals("Green")) + { + SetAgentDestinations(redBase.transform.position); + } + } + + + + +#if UNITY_EDITOR + if (Application.isFocused) + { + //UnityEditor.SceneView.FocusWindowIfItsOpen(typeof(UnityEditor.SceneView)); + } +#endif + + #endregion + } + void spawnAgent() + { + + var spawnPos = agentParent.transform.position + Random.onUnitSphere; + spawnPos.y = 0; + NavMeshHit hit; + NavMesh.SamplePosition(spawnPos, out hit, 10, NavMesh.AllAreas); + spawnPos = hit.position + Vector3.up; + + GameObject agent = null; + agent = Instantiate(agentPrefab, spawnPos, Quaternion.identity); + agent.name = "Agent " + agents.Count; + agent.GetComponent().material = this.eMaterial; + agent.transform.parent = agentParent.transform; + agent.tag = "EnemyAgent"; + var agentScript = agent.GetComponent(); + agentScript.radius = 0.3f;// Random.Range(0.2f, 0.6f); + agentScript.mass = 1; + agentScript.perceptionRadius = 3; + agentScript.alignment = "Green"; + if (spiralFlag) + { + agentScript.spiralFlag = true; + } + if (!flocking) + { + agentScript.flocking = false; + } + agentScript.crowdFollowParam = CrowdFollow_Param; + agents.Add(agentScript); + agentsObjs.Add(agent, agentScript); + } + IEnumerator Run() + { + yield return null; + + for (int iterations = 0; ; iterations++) + { + if (iterations % PATHFINDING_FRAME_SKIP == 0) + { + if (!spiralFlag) + { + ComputeAgentPath(); + } + } + + foreach (var agent in agents) + { + if (agent != null){ + agent.ApplyForce(); + } + } + + if (UPDATE_RATE == 0) + { + yield return null; + } + else + { + yield return new WaitForSeconds(UPDATE_RATE); + } + } + } + + #endregion + + #region Public Functions + + public bool IsAgent(GameObject obj) + { + return agentsObjs.ContainsKey(obj); + } + + public void SetAgentDestinations(Vector3 NewDestination) + { + destination = NewDestination; + NavMeshHit hit; + NavMesh.SamplePosition(NewDestination, out hit, 10, NavMesh.AllAreas); + foreach (var agent in agents) + { + agent.SetDestination(hit.position); + } + } + + public void ComputeAgentPath() + { + foreach (var agent in agents) + { + agent.ComputePath(); + } + } + + public void RemoveAgent(GameObject obj) + { + var agent = obj.GetComponent(); + + agents.Remove(agent); + agentsObjs.Remove(obj); + } + + /*public void SelectedAgents(Vector3 selection) + { + //Debug.Log(selection); + Vector3 worldDownPosition = Camera.main.ScreenToWorldPoint(mouseDownPosition + Vector3.forward * 10); + Vector3 worldUpPosition = Camera.main.ScreenToWorldPoint(selection + Vector3.forward * 10); + Vector3 dir = worldDownPosition - Camera.main.transform.position; + RaycastHit rcDownHit; + RaycastHit rcUpHit; + if (Physics.Raycast(worldDownPosition, dir, out rcDownHit)) + { + dir = worldUpPosition - Camera.main.transform.position; + if (Physics.Raycast(worldUpPosition, dir, out rcUpHit)) + { + worldDownPosition = rcDownHit.point; + worldUpPosition = rcUpHit.point; + //Debug.Log("World Down Position:" + worldDownPosition); + //Debug.Log("World Up Position:" + worldUpPosition); + if (!Input.GetKey(KeyCode.LeftShift)) + { + //Debug.Log("Here"); + foreach (Agent selectedAgent in agents) + { + if (selected.Contains(selectedAgent)) + { + selected.Remove(selectedAgent); + selectedAgent.transform.gameObject.GetComponent().material = unselectedMaterial; + } + } + } + + float xRight = worldUpPosition.x > worldDownPosition.x ? worldDownPosition.x : worldUpPosition.x; + float xLeft = worldUpPosition.x > worldDownPosition.x ? worldUpPosition.x : worldDownPosition.x; + + float yUp = worldUpPosition.z > worldDownPosition.z ? worldDownPosition.z : worldUpPosition.z; + float yDown = worldUpPosition.z > worldDownPosition.z ? worldUpPosition.z : worldDownPosition.z; + //Debug.Log("xRight:" + xRight + " xLeft:" + xLeft + " yUp:" + yUp + " yDown:" + yDown); + foreach (Agent agent in agents) + { + Vector3 agentPosition = agent.transform.position; + //Debug.Log("Agent Position:" + agentPosition); + if (!selected.Contains(agent) && agentPosition.x >= xRight && agentPosition.x <= xLeft && agentPosition.z >= yUp && agentPosition.z <= yDown) + { + selected.Add(agent); + agent.transform.gameObject.GetComponent().material = selectedMaterial; + } + } + } + + } + }*/ + #endregion + + #region Private Functions + + /*void ChangeSelectAgent(GameObject agent) + { + Agent agScript = agent.GetComponent(); + if (selected.Contains(agScript)) + { + selected.Remove(agScript); + agent.GetComponent().material = unselectedMaterial; + } + else + { + selected.Add(agScript); + agent.GetComponent().material = selectedMaterial; + } + }*/ + + #endregion + + #region Visualization Functions + + #endregion + + #region Utility Classes + + private class Tuple + { + public K Item1; + public V Item2; + + public Tuple(K k, V v) + { + Item1 = k; + Item2 = v; + } + } + + #endregion +} \ No newline at end of file diff --git a/B6 Scripts/RTSEAgentManager.cs.meta b/B6 Scripts/RTSEAgentManager.cs.meta new file mode 100644 index 0000000..64ec7e3 --- /dev/null +++ b/B6 Scripts/RTSEAgentManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2cf2bfd31af350c40b1c03f2e0670948 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B6 Scripts/SlidingFrictionForceVisualizer.cs b/B6 Scripts/SlidingFrictionForceVisualizer.cs new file mode 100644 index 0000000..e4e4c0f --- /dev/null +++ b/B6 Scripts/SlidingFrictionForceVisualizer.cs @@ -0,0 +1,41 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class SlidingFrictionForceVisualizer : MonoBehaviour +{ + public GameObject agent1; + public GameObject agent2; + + [Range(0, 10)] + public float speed1; + [Range(0, 10)] + public float speed2; + + void Update() + { + Debug.DrawLine(agent1.transform.position, agent1.transform.position + Vector3.up * 3, Color.green); + Debug.DrawLine(agent1.transform.position, agent1.transform.position + agent1.transform.forward * speed1, Color.cyan); + Debug.DrawLine(agent2.transform.position, agent2.transform.position + agent2.transform.forward * speed2, Color.cyan); + + var n = (agent2.transform.position - agent1.transform.position).normalized; + var tangent = Vector3.Cross(Vector3.up, n); + Debug.DrawLine(agent1.transform.position, agent1.transform.position + tangent * 2, Color.yellow); + + var magnitude = Vector3.Dot(agent1.transform.forward * speed1 - agent2.transform.forward * speed2, tangent); + Debug.DrawLine(agent1.transform.position, agent1.transform.position + tangent * magnitude * 2, Color.red); + + Debug.DrawLine(agent1.transform.position + agent1.transform.forward * speed1, agent2.transform.position + agent2.transform.forward * speed2, Color.magenta); + + #region Visualization + +#if UNITY_EDITOR + if (Application.isFocused) + { + UnityEditor.SceneView.FocusWindowIfItsOpen(typeof(UnityEditor.SceneView)); + } +#endif + + #endregion + } +} diff --git a/B6 Scripts/SlidingFrictionForceVisualizer.cs.meta b/B6 Scripts/SlidingFrictionForceVisualizer.cs.meta new file mode 100644 index 0000000..2dfb3e3 --- /dev/null +++ b/B6 Scripts/SlidingFrictionForceVisualizer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ce8f0edba51a5f5409bf63fef1fab43b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B6 Scripts/Sound.cs b/B6 Scripts/Sound.cs new file mode 100644 index 0000000..340fb6f --- /dev/null +++ b/B6 Scripts/Sound.cs @@ -0,0 +1,19 @@ +using UnityEngine; +using UnityEngine.Audio; + +[System.Serializable] +public class Sound +{ + public string name; + + public AudioClip clip; + + [Range(0f, 1f)] + public float volume; + [Range(.1f, 3f)] + public float pitch; + public bool loop; + + [HideInInspector] + public AudioSource source; +} diff --git a/B6 Scripts/Sound.cs.meta b/B6 Scripts/Sound.cs.meta new file mode 100644 index 0000000..5569813 --- /dev/null +++ b/B6 Scripts/Sound.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5b7e5c689b0174b4195b935f9f56037d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B6 Scripts/WallManager.cs b/B6 Scripts/WallManager.cs new file mode 100644 index 0000000..f29c300 --- /dev/null +++ b/B6 Scripts/WallManager.cs @@ -0,0 +1,26 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class WallManager : MonoBehaviour +{ + private static HashSet wallObjs = new HashSet(); + + void Start() + { + GameObject[] walls; + walls = GameObject.FindGameObjectsWithTag("Wall"); + foreach(GameObject wall in walls){ + wallObjs.Add(wall); + } + } + + #region Public Functions + + public static bool IsWall(GameObject obj) + { + return wallObjs.Contains(obj); + } + + #endregion +} diff --git a/B6 Scripts/WallManager.cs.meta b/B6 Scripts/WallManager.cs.meta new file mode 100644 index 0000000..c3ac5b9 --- /dev/null +++ b/B6 Scripts/WallManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 739b90a3b1e98314a8e6b5c1901afc81 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/B6 Scripts/cPointBehavior.cs b/B6 Scripts/cPointBehavior.cs new file mode 100644 index 0000000..88c6156 --- /dev/null +++ b/B6 Scripts/cPointBehavior.cs @@ -0,0 +1,78 @@ +using UnityEngine; +using UnityEngine.UI; + +public class cPointBehavior : MonoBehaviour +{ + // Start is called before the first frame update + + public int max_health = 40; + public int health = 40; + public string owner = "Red"; + public TextMesh text; + public Material redSide; + public Material greenSide; + public Slider slider; + + private RTSAgentManager agentManager; + private RTSEAgentManager EagentManager; + private float time = 0; + void Start() + { + agentManager = GameObject.Find("Managers").GetComponent(); + EagentManager = GameObject.Find("Managers").GetComponent(); + text = this.transform.Find("textMesh").GetComponent(); + text.text = max_health.ToString() + "/" + max_health.ToString(); + } + + public void onTriggerEnter(Collider Other) + { + GameObject ot = Other.gameObject; + if (agentManager.IsAgent(Other.gameObject) || EagentManager.IsAgent(Other.gameObject)) + { + Agent ag = Other.gameObject.GetComponent(); + if (!ag.alignment.Equals(owner)) + { + health -= 1; + } + if (health <= 0) + { + this.owner = ag.alignment; + health = max_health; + } + } + } + + // Update is called once per frame + void Update() + { + if (slider) + { + slider.value = health; + } + if (health < max_health) + { + time += Time.deltaTime; + if (time >= 5.0f) + { + health++; + time = 0; + } + + } + text.text = health.ToString() + "/" + max_health.ToString(); + if (this.owner.Equals("Red")) + { + gameObject.GetComponent().material = redSide; + if(transform.childCount > 1){ + transform.GetChild(1).gameObject.GetComponent().startColor = new Color(1, 0, 0, 1); + } + } + if (this.owner.Equals("Green")) + { + gameObject.GetComponent().material = greenSide; + if(transform.childCount > 1){ + transform.GetChild(1).gameObject.GetComponent().startColor = new Color(0, 1, 0, 1); + } + } + } +} diff --git a/B6 Scripts/cPointBehavior.cs.meta b/B6 Scripts/cPointBehavior.cs.meta new file mode 100644 index 0000000..6891df0 --- /dev/null +++ b/B6 Scripts/cPointBehavior.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c5eb167fbae05434b8323d0f28b2b1ed +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 300 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: