// Orbit a position while always facing straight ahead public void Orbit(Vector3 orbitCenter, float orbitAltitude, float orbitRadius = 1, float timeout = float.PositiveInfinity) { UpdateVectorCallback GetOrbitCenter = (float deltaTime) => { return(orbitCenter); }; UpdateScalarCallback GetOrbitAltitude = (float deltaTime) => { return(orbitAltitude); }; LaunchMovementCoroutine(OrbitPositionCoroutine(GetOrbitCenter, GetOrbitAltitude, orbitRadius, timeout, null)); LaunchDirectionCoroutine(LookFlightDirectionCoroutine()); }
// Orbit a target while always facing it public void OrbitAndLookAt(Transform orbitCenter, float relativeOrbitAltitude, float orbitRadius = 1, float timeout = float.PositiveInfinity, System.Action OnComplete = null) { UpdateVectorCallback GetOrbitCenter = (float deltaTime) => { return(orbitCenter.position); }; UpdateScalarCallback GetOrbitAltitude = (float deltaTime) => { return(orbitCenter.position.y + relativeOrbitAltitude); }; LaunchMovementCoroutine(OrbitPositionCoroutine(GetOrbitCenter, GetOrbitAltitude, orbitRadius, timeout, OnComplete)); LaunchDirectionCoroutine(LookAtCoroutine(orbitCenter)); }
private IEnumerator OrbitPositionCoroutine(UpdateVectorCallback GetOrbitCenter, UpdateScalarCallback GetOrbitAltitude, float orbitRadius, float timeout, System.Action OnComplete) { float step = 20; float direction = MathHelpers.RandomSign(); Vector3 toHelicopter = MathHelpers.Azimuthal(transform.position - GetOrbitCenter(Time.deltaTime)); float startRadius = toHelicopter.magnitude; float currentRadius = startRadius; float startAngle = currentRadius == 0 ? 0 : Mathf.Rad2Deg * Mathf.Acos(toHelicopter.x / currentRadius); if (startAngle < 0) { startAngle += 180; } float currentAngle = startAngle; float degreesElapsed = 0; float startAltitude = transform.position.y; float currentAltitude = startAltitude; int maxObstructionRetries = (int)(360 / step); int numObstructionRetries = maxObstructionRetries; float nextObstructionCheckTime = 0; float timeoutTime = Time.time + timeout; while (true) { // Compute the next position along the circle float nextAngle = currentAngle + direction * step; float nextRadians = Mathf.Deg2Rad * nextAngle; // Move to that position bool flying = true; do { float now = Time.time; if (now >= timeoutTime) { if (OnComplete != null) { yield return(null); // always ensure one frame elapsed before callback OnComplete(); } Halt(); yield break; } // In case orbit center is a moving target, continually update the next // position Vector3 nextPosition = GetOrbitCenter(Time.deltaTime) + currentRadius * new Vector3(Mathf.Cos(nextRadians), 0, Mathf.Sin(nextRadians)); nextPosition.y = currentAltitude; // This doesn't work very well but the idea is to check for // obstructions and move on to subsequent waypoints. If no point seems // reachable, we simply abort. For each attempt, we allow some time to // pass in case we were stuck. if (now > nextObstructionCheckTime && PathObstructed(nextPosition)) { if (numObstructionRetries > 0) { nextObstructionCheckTime = now + obstructionRetryTime; numObstructionRetries--; break; } // Need to abort if (OnComplete != null) { yield return(null); // always ensure one frame elapsed before callback OnComplete(); } Halt(); yield break; } flying = GoTo(nextPosition); UpdateControls(); yield return(null); } while (flying); // Restore number of avoidance attempts if we finally reached a waypoint if (flying == false) { numObstructionRetries = maxObstructionRetries; } // Advance the angle degreesElapsed += step; currentAngle = nextAngle; float revolutionCompleted = degreesElapsed / 360; // Adjust the radius so that it converges to the desired radius within // one revolution currentRadius = Mathf.Lerp(startRadius, orbitRadius, revolutionCompleted); // Altitude convergence currentAltitude = Mathf.Lerp(startAltitude, GetOrbitAltitude(Time.deltaTime), revolutionCompleted); } }