/// <summary> /// Spawn an asteroid field. /// </summary> /// <param name="_numberOfAsteroids">The number of asteroids in the field.</param> /// <param name="_altitude">The maximum altitude AGL of the field, minimum altitude AGL is 50m.</param> /// <param name="_radius">The radius of the field from the spawn point.</param> /// <param name="_geoCoords">The spawn point (centre) of the field.</param> public void SpawnField(int numberOfAsteroids, float altitude, float radius, Vector2d geoCoords) { altitude *= 100f; // Convert to m. radius *= 1000f; // Convert to m. Debug.Log($"[BDArmory.Asteroids]: Spawning asteroid field with {numberOfAsteroids} asteroids with height {altitude}m and radius {radius / 1000f}km at coordinate ({geoCoords.x:F4}, {geoCoords.y:F4})."); BDACompetitionMode.Instance.competitionStatus.Add("Spawning Asteroid Field, please be patient."); var spawnPoint = FlightGlobals.currentMainBody.GetWorldSurfacePosition(geoCoords.x, geoCoords.y, altitude); var upDirection = (spawnPoint - FlightGlobals.currentMainBody.transform.position).normalized; var refDirection = Math.Abs(Vector3.Dot(Vector3.up, upDirection)) < 0.71f ? Vector3.up : Vector3.forward; // Avoid that the reference direction is colinear with the local surface normal. asteroids = new Vessel[numberOfAsteroids]; for (int i = 0; i < asteroids.Length; ++i) { var direction = Vector3.ProjectOnPlane(Quaternion.AngleAxis((float)RNG.NextDouble() * 360f, upDirection) * refDirection, upDirection).normalized; var x = (float)RNG.NextDouble(); var distance = Mathf.Sqrt(1f - x) * radius; var height = RNG.NextDouble() * (altitude - 50f) + 50f; var position = spawnPoint + direction * distance; position += (height - Misc.Misc.GetRadarAltitudeAtPos(position, false)) * upDirection; var asteroid = AsteroidUtils.SpawnAsteroid(position); if (asteroid != null) { asteroids[i] = asteroid; } } UpdateAsteroidNames(); floatingCoroutine = StartCoroutine(Float()); StartCoroutine(CleanOutAsteroids()); }
/// <summary> /// Replace an asteroid at position i in the pool. /// </summary> /// <param name="i"></param> void ReplacePooledAsteroid(int i) { if (BDArmorySettings.DRAW_DEBUG_LABELS) { Debug.Log($"[BDArmory.Asteroids]: Replacing asteroid at position {i}."); } var asteroid = AsteroidUtils.SpawnAsteroid(FlightGlobals.currentMainBody.GetWorldSurfacePosition(geoCoords.x, geoCoords.y, altitude + 10000)); if (asteroid != null) { StartCoroutine(CleanAsteroid(asteroid)); asteroidPool[i] = asteroid; } }
/// <summary> /// Strip out various modules from the asteroids as they make excessive amounts of GC allocations. /// Then set up the initial asteroid rotations. /// </summary> IEnumerator CleanOutAsteroids() { var wait = new WaitForFixedUpdate(); while (asteroids.Any(a => a != null && (a.packed || !a.loaded))) { yield return(wait); } for (int i = 0; i < asteroids.Length; ++i) { if (asteroids[i] == null) { continue; } AsteroidUtils.CleanOutAsteroid(asteroids[i]); } yield return(InitialRotation()); }
/// <summary> /// Add a number of asteroids to the pool. /// </summary> /// <param name="count"></param> void AddAsteroidsToPool(int count) { Debug.Log($"[BDArmory.Asteroids]: Increasing asteroid pool size to {asteroidPool.Count + count}."); spawnPoint = FlightGlobals.currentMainBody.GetWorldSurfacePosition(geoCoords.x, geoCoords.y, altitude); upDirection = (spawnPoint - FlightGlobals.currentMainBody.transform.position).normalized; var refDirection = Math.Abs(Vector3d.Dot(Vector3.up, upDirection)) < 0.71f ? Vector3d.up : Vector3d.forward; // Avoid that the reference direction is colinear with the local surface normal. for (int i = 0; i < count; ++i) { var direction = Vector3.ProjectOnPlane(Quaternion.AngleAxis(i / 60f * 360f, upDirection) * refDirection, upDirection).normalized; // 60 asteroids per layer of the spiral (approx. 100m apart). var position = spawnPoint + (1e4f + 1e2f * i / 60) * upDirection + 1e3f * direction; // 100m altitude difference per layer of the spiral. var asteroid = AsteroidUtils.SpawnAsteroid(position); if (asteroid != null) { StartCoroutine(CleanAsteroid(asteroid)); asteroidPool.Add(asteroid); } } UpdatePooledAsteroidNames(); }
/// <summary> /// Wait until the collider bounds have been generated, then remove various modules from the asteroid for performance reasons. /// </summary> /// <param name="asteroid">The asteroid to clean.</param> IEnumerator CleanAsteroid(Vessel asteroid) { ++cleaningInProgress; var wait = new WaitForFixedUpdate(); asteroid.gameObject.SetActive(true); var startTime = Time.time; while (asteroid != null && Time.time - startTime < 10 && (asteroid.packed || !asteroid.loaded || asteroid.rootPart.GetColliderBounds().Length < 2)) { yield return(wait); } if (asteroid != null) { if (Time.time - startTime >= 10) { Debug.LogWarning($"[BDArmory.Asteroids]: Timed out waiting for colliders on {asteroid.vesselName} to be generated."); } AsteroidUtils.CleanOutAsteroid(asteroid); asteroid.gameObject.SetActive(false); } --cleaningInProgress; }