/// ---------------- Respawn Point Collision Avoidance Section ---------------- /// /// <summary> /// <para>The dot product of vector A and second vector B results in /// the length of vector A projected in the direction of vector B. /// Here the dot product is the projection of an asteroid's velocity onto the relative position /// of the potential respawn point. Divide the dot product by the relative speed squared /// (magnitude of the relative velocity) to calculate /// the time when the objects will be closest to each other during their trajectories. /// minSeparation becomes the distance between the two objects at their closest approach. /// If this distance is small enough they will collide.</para> /// <para>I have set it to check for a minSeparation value of < .3 and if the asteroid is /// within 3 seconds of approaching this point.</para> /// </summary> /// <returns>A safe respawn point.</returns> static public IEnumerator PlotRespawnPointCoroutine(CallbackDelegateV3 callback) { Vector3 respawnPoint = Vector3.zero; bool isSafePoint; // Attempt to find a safe point 20 times max to avoid possible infinite loop // if there are no possible safe spots int attempts = 20; do { // Choose a random location within 80% of the play area; respawnPoint = ScreenBounds.RANDOM_RESPAWN_LOC; isSafePoint = true; attempts--; foreach (Asteroid a in ASTEROIDS) { // Only predict collisions with parent asteroids if (a.transform.parent == null) { Vector3 relativePos = a.transform.position - respawnPoint; Vector3 relativeVel = a.GetComponent <Rigidbody> ().velocity; float relativeSpeed = relativeVel.magnitude; // Calculate the time of the closest approach of this asteroid to the respawn point float timeToClosestApproach = Vector3.Dot(relativePos, relativeVel); timeToClosestApproach /= relativeSpeed * relativeSpeed * -1; float distance = relativePos.magnitude; // The minimal Separation is the distance between the two objects at the time of the closest approach. float minSeparation = distance - relativeSpeed * timeToClosestApproach; // If a collision is likely within 3 seconds flag this point as false // Increase minSeparation if you want to give the player a bit more room to move after respawn. if (minSeparation < .3 && timeToClosestApproach <= 3) { #if DEBUG_Asteroid_PlotCollision Debug.DrawLine(respawnPoint, a.transform.position, Color.white, 3f); #endif isSafePoint = false; } } } } while (attempts > 0 && isSafePoint == false); yield return(new WaitForSeconds(PlayerShip.respawnTimer)); callback(respawnPoint); }
/// <summary> /// <para>Given the point of the PlayerShip when it hit an Asteroid, this method /// chooses a respawn point. The RESPAWN_POINT_GRID_DIVISIONS above determines /// how many points the game will check. If that number is 8, then the game /// will check 49 (7x7) points within the play area (dividing each dimension /// into 8ths and avoiding the edges of the play area).</para> /// <para>This method will not find and avoid the location closest to the /// PlayerShip's previous location and then will iterate through all points /// and all Asteroids.</para> /// <para>This process is not very performant (though given the /// small numbers of objects, it's still really fast), so we'll have it use /// a coroutine to demonstrate their use.</para> /// </summary> /// <returns>The respawn point for the PlayerShip.</returns> /// <param name="prevPos">Previous position of the PlayerShip.</param> /// <param name="callback">Method to be called when this method is finished.</param> static public IEnumerator FindRespawnPointCoroutine(Vector3 prevPos, CallbackDelegateV3 callback, ParticleManager _particleSystem) { # if DEBUG_AsteraX_RespawnNotifications
static public IEnumerator FindRespawnPointCoroutine(Vector3 prevPos, CallbackDelegateV3 callback) { if (RESPAWN_POINTS == null) { RESPAWN_POINTS = new Vector3[RESPAWN_DIVISIONS + 1, RESPAWN_DIVISIONS + 1]; Bounds playAreaBounds = ScreenBounds.BOUNDS; float dX = playAreaBounds.size.x / RESPAWN_DIVISIONS; float dY = playAreaBounds.size.y / RESPAWN_DIVISIONS; for (int i = 0; i <= RESPAWN_DIVISIONS; i++) { for (int j = 0; j <= RESPAWN_DIVISIONS; j++) { RESPAWN_POINTS[i, j] = new Vector3( playAreaBounds.min.x + i * dX, playAreaBounds.min.y + j * dY, 0); } } } yield return(new WaitForSeconds(PlayerShip.RESPAWN_DELAY * 0.8f)); float distSqr, closestDistSqr = float.MaxValue; int prevI = 0, prevJ = 0; for (int i = RESPAWN_AVOID_EDGES; i <= RESPAWN_DIVISIONS - RESPAWN_AVOID_EDGES; i++) { for (int j = RESPAWN_AVOID_EDGES; j <= RESPAWN_DIVISIONS - RESPAWN_AVOID_EDGES; j++) { distSqr = (RESPAWN_POINTS[i, j] - prevPos).sqrMagnitude; if (distSqr < closestDistSqr) { closestDistSqr = distSqr; prevI = i; prevJ = j; } } } float furthestDistSqr = 0; Vector3 nextPos = prevPos; for (int i = RESPAWN_AVOID_EDGES; i <= RESPAWN_DIVISIONS - RESPAWN_AVOID_EDGES; i++) { for (int j = RESPAWN_AVOID_EDGES; j <= RESPAWN_DIVISIONS - RESPAWN_AVOID_EDGES; j++) { if (i == prevI && j == prevJ) { continue; } closestDistSqr = float.MaxValue; for (int k = 0; k < ASTEROIDS.Count; k++) { distSqr = (ASTEROIDS[k].transform.position - RESPAWN_POINTS[i, j]).sqrMagnitude; if (distSqr < closestDistSqr) { closestDistSqr = distSqr; } } if (closestDistSqr > furthestDistSqr) { furthestDistSqr = closestDistSqr; nextPos = RESPAWN_POINTS[i, j]; } } } yield return(new WaitForSeconds(PlayerShip.RESPAWN_DELAY * 0.2f)); callback(nextPos); }