// Takes in a vector to where the entity wants to moves, gives back a vector which also factors in local avoidance private Vector3 CalcLocalAvoidance(Vector3 regularDirection, Vector3 previousDirection) { if (nearEntities.Count == 0) { return(regularDirection); } float combinedObstruction = 0f; List <EntityCluster> obstacles = new List <EntityCluster>(); List <float> obstructionValues = new List <float>(); try { foreach (Transform t in nearEntities) { if (t.GetComponent <Entity>() == null || t.GetComponent <Entity>().cluster == null) // means it is dead { nearEntities.Remove(t); if (clusteredEntities.Contains(t)) { clusteredEntities.Remove(t); } continue; } Vector2 diff = t.position - transform.position; diff = Quaternion.Euler(0, 0, Vector2.SignedAngle(previousDirection + regularDirection, Vector2.up)) * diff; /* Distort Vector, to * - prioritize objects in front of the tank, rather than the back * - prioritize objects directly in the way, instead of left or right to the way */ diff.y -= localAvoidanceOffset; diff.y /= 2; float obstruction = 1f - (diff.magnitude / (localAvoidanceDist * t.GetComponent <Entity>().cluster.GetObstacleSizeWithout(transform))); if (obstruction < 0) { obstruction = 0; } if (obstruction > 0.03f) { // t is an obstacle // Check if it is very near of other obstacles (so the vehicle can't pass between them) EntityCluster cluster = t.GetComponent <Entity>().cluster; if (cluster.Contains(transform)) { // It's in the same cluster, so the cluster must be split up // Check if already indexed bool indexed = false; EntityCluster container = null; foreach (EntityCluster obstacle in obstacles) { if (obstacle.Contains(t) && !obstacle.Contains(transform)) { indexed = true; container = obstacle; } } if (indexed) { obstructionValues[obstacles.IndexOf(container)] += obstruction; } else { // create new (temporary) cluster EntityCluster tempCluster = new EntityCluster(new List <Transform>() { t }, true); for (int i = 0; i < tempCluster.Count; i++) { Transform companion = tempCluster[i]; if (companion.GetComponent <Moving>() == null) { continue; } foreach (Transform clustered in companion.GetComponent <Moving>().clusteredEntities) { if (!tempCluster.Contains(clustered) && clustered != transform) { tempCluster.AddTemporary(clustered); //if (ShowDebugInfo) // Debug.DrawLine(tempCluster[i].position, clustered.position, new Color(1f, .6f, .9f), 1.1f / brainUpdateRate); } } //if (tempCluster.Count == 1 && ShowDebugInfo) // DrawSquare(companion.position - new Vector3(.1f, .1f, 0), new Color(1f, .6f, .9f), 1.1f / brainUpdateRate); } obstacles.Add(tempCluster); obstructionValues.Add(obstruction); } } else if (obstacles.Contains(cluster)) { obstructionValues[obstacles.IndexOf(cluster)] += obstruction; } else { obstacles.Add(cluster); obstructionValues.Add(obstruction); } combinedObstruction += obstruction; } } } catch (InvalidOperationException ex) {} catch (NullReferenceException ex) { Debug.LogError(ex.Message); } // factor in cluster size if (combinedObstruction < 0.05f) { return(regularDirection); } if (combinedObstruction > 1f) { combinedObstruction = 1f; } // Combine vectors float directionCorrection = 0; // In degrees; for (var i = 0; i < obstacles.Count; i++) { // Check whether the obstacle is on the left or right int side = 0; // -1 = left, +1 = right Vector2 diff = obstacles[i].GetAvgPosWithout(transform) - (Vector2)transform.position; diff = Quaternion.Euler(0, 0, Vector2.SignedAngle(previousDirection, Vector2.up)) * diff; if (clusterSides.ContainsKey(obstacles[i])) { // It has encountered the cluster recently and the side was saved // The previously chosen side is more probable to be picked again to reduce direction switching int oldSide = clusterSides[obstacles[i]]; int newSide = diff.x < 0 ? -1 : 1; if (newSide == oldSide) { // new and old side agree side = clusterSides[obstacles[i]]; } else { // If the overall obstruction is high, the vehicle will likely keep it's side // If the target direction is very different from the former side, it will likely change sides float angle = Vector2.SignedAngle(transform.up, diff); angle = angle > 0 ? angle : -angle; // absolute value if (angle > combinedObstruction * 45) { // Change side side = newSide; clusterSides[obstacles[i]] = newSide; } else { // keep side side = oldSide; } side = clusterSides[obstacles[i]]; } } else { // First time, check is needed if (diff.x < 0) { // left side = -1; clusterSides.Add(obstacles[i], side); } else { // right side = 1; clusterSides.Add(obstacles[i], side); } } //// Debug //if (ShowDebugInfo) //{ // // show if the cluster is avoided by going left (red) or right (blue) by line color // // number of lines shows the obstruction value (1 - 10) // int number = Mathf.RoundToInt(obstructionValues[i] * obstructionValues[i] * 9f); // Color color = (side == -1) ? Color.blue : Color.red; // Vector3 offset = new Vector3(0, .1f, 0); // foreach (Transform entity in obstacles[i]) { // Vector3 origDirection = (entity.position - transform.position).normalized * 0.01f; // Vector3 perpDirection = Quaternion.Euler(0, 0, 90) * origDirection; // for (int n = 0; n <= number; n++) // { // Vector3 offset2 = perpDirection * (n - number / 2f); // Debug.DrawLine(transform.position + offset2, entity.position + offset2, color, (1 / brainUpdateRate)); // } // } //} directionCorrection += obstructionValues[i] * 105 * side; } // Clamp directionCorrection = Mathf.Clamp(directionCorrection, -120, 120); // Adjust vector regularDirection = Quaternion.Euler(0, 0, directionCorrection) * regularDirection; //CheckObjectAvoidance(previousDirection); return(regularDirection); }
public void UpdateNearEntities() { float minPassDist = GameMaster.minPassDist; float minClusterDist = minPassDist + GetComponent <Entity>().obstacleSize; EntityCluster thisCluster = GetComponent <Entity>().cluster; foreach (GameObject g in GameMaster.entities) { if (g == null) { GameMaster.entities.Remove(g); break; } if (g.GetComponent <Entity>() == null) { GameMaster.entities.Remove(g); break; } Transform t = g.transform; if (t == transform) { continue; } float dist = Vector2.Distance(t.transform.position, transform.position); if (nearEntities.Contains(t.transform)) { if (dist > 7) { nearEntities.Remove(t.transform); // Check if their cluster data should also be deleted // Check if it was the last entity from it's cluster EntityCluster cluster = t.GetComponent <Entity>().cluster; if (cluster == null) { Debug.LogError("cluster fragment"); GameMaster.ReconsiderEntityCluster(transform); cluster = t.GetComponent <Entity>().cluster; } bool last = true; foreach (Transform nearEntity in nearEntities) { if (cluster.Contains(nearEntity)) { last = false; break; } } if (last) { clusterSides.Remove(cluster); } // Remove from cluster (this code should never be executed under normal circumstances) if (clusteredEntities.Contains(t)) { // Remove clustered entities from former cluster foreach (Transform clean in clusteredEntities) { try { thisCluster.RemoveEntity(clean); } catch (Exception ex) { Debug.LogError(ex); } } thisCluster.RemoveEntity(transform); // Reconsider cluster for nearby entities foreach (Transform companion in clusteredEntities) { GameMaster.ReconsiderEntityCluster(companion); } // Remove from clustered entities clusteredEntities.Remove(t); if (t.GetComponent <Moving>() != null) { t.GetComponent <Moving>().clusteredEntities.Remove(transform); } // Reconsider own cluster GameMaster.ReconsiderEntityCluster(transform); } } // check if should be clustered else if (!clusteredEntities.Contains(t) && dist < (minClusterDist + t.GetComponent <Entity>().obstacleSize - 0.15f)) { clusteredEntities.Add(t); if (t.GetComponent <Moving>() != null) { t.GetComponent <Moving>().clusteredEntities.Add(transform); } GameMaster.ReconsiderEntityCluster(transform); GameMaster.ReconsiderEntityCluster(t); } } else if (dist < 7) { nearEntities.Add(t.transform); // Check if should be moved to same cluster if (dist < (minClusterDist + t.GetComponent <Entity>().obstacleSize - 0.15f)) { GameMaster.ReconsiderEntityCluster(transform); } } } // Sort list nearEntities.Sort(new PositionComparer(transform)); // Check if clustered entities are still in cluster range List <Transform> toBeRemoved = new List <Transform>(); for (var i = 0; i < clusteredEntities.Count; i++) { Transform entity = clusteredEntities[i]; if (entity.GetComponent <Entity>() == null) // means it is dead { clusteredEntities.Remove(entity); continue; } float dist = (entity.position - transform.position).magnitude; if (dist > (minClusterDist + entity.GetComponent <Entity>().obstacleSize + 0.15f)) { // should be removed toBeRemoved.Add(entity); } } if (toBeRemoved.Count > 0) { // Remove clustered from former cluster Transform[] companions = thisCluster.Where(transform => nearEntities.Contains(transform)).ToArray(); foreach (Transform clean in companions) { try { thisCluster.RemoveEntity(clean); } catch (Exception ex) { Debug.LogError(ex); } } thisCluster.RemoveEntity(transform); // Reconsider cluster for nearby entities foreach (Transform companion in companions) { GameMaster.ReconsiderEntityCluster(companion); } // Remove too distant entities foreach (var t in toBeRemoved) { clusteredEntities.Remove(t); float distance = (t.position - transform.position).magnitude; if (t.GetComponent <Moving>() != null) { t.GetComponent <Moving>().clusteredEntities.Remove(transform); } } // Reconsider own cluster GameMaster.ReconsiderEntityCluster(transform); } if (toBeRemoved.Count != 0) { GameMaster.ReconsiderEntityCluster(transform); } // Debug if (ShowDebugInfo) { // Mode Vector3 pos = transform.position; pos += new Vector3(.5f, -.5f, 0); // Destination if (mode == 1) { DrawSquare(targetPlace, Color.green, (1 / updateRate)); } else if (mode == 2) { DrawSquare(targetPlace, new Color(1, 0, 1), (1 / updateRate)); } } }