float3 FutureLocalPosition(Orbiter o, float t) { // TODO: Mass stubbed out here... should come from the body? const float MASS = 1; float period = Orbiter.Period(o, MASS, G); float radians = (o.Radians + (float)o.Direction * TWO_PI / period * t) % (TWO_PI); float3 futurePosition = o.Radius * UnitCircle(radians); return(futurePosition); }
public void FixedUpdate() { float dt = Time.fixedDeltaTime; foreach (var emitter in Emitters) { emitter.TimeTillEmission -= dt; if (emitter.TimeTillEmission <= 0) { GameObject emittee = Instantiate(emitter.Emittee, emitter.transform.position, emitter.transform.rotation); Register(emittee); if (emittee.TryGetComponent <Velocity>(out Velocity v)) { v.Value = emitter.transform.forward * emitter.EmissionSpeed; } // TODO: note... this is not totally correct as we could slightly overshoot... // TODO: consider fixing this AND the initial position based on this slight overshoot to get perfect behavior emitter.TimeTillEmission = emitter.EmissionPeriod; } } // Update deathtimers: modifies component arrays for (int i = 0; i < DeathTimers.Count; i++) { DeathTimer deathTimer = DeathTimers[i]; deathTimer.Value -= dt; if (deathTimer.Value <= 0) { UnRegister(deathTimer.gameObject); Destroy(deathTimer.gameObject); i--; } } // Update all free bodies foreach (var freebody in FreeBodies) { Velocity v = freebody.GetComponent <Velocity>(); foreach (var normalizedMass in NormalizedMasses) { float3 delta = normalizedMass.transform.position - freebody.transform.position; float3 direction = normalize(delta); float d = length(delta); v.Value += direction * dt * normalizedMass.Value * MassFactor * G / (d * d); } } // There are several versions of this system that will offer better and better behavior // The stupid chaser with fixed speed // Constantly tries to move towards the CURRENT location of their target foreach (var chaser in FindObjectsOfType <Chaser>()) { float3 delta = chaser.Target.position - chaser.transform.position; float3 direction = normalize(chaser.Target.position - chaser.transform.position); float remainingDistance = min(length(delta), chaser.MaxSpeed * dt); chaser.transform.position = chaser.transform.position + remainingDistance * (Vector3)direction; Debug.DrawLine(chaser.transform.position, chaser.Target.position, Color.red); } // The predictive chaser // Predicts the location of the target various times in the future // Chooses the first time where the distance to the object is within its range for that given time based on its max speed // Moves towards that future location at the required speed var predictiveChasers = FindObjectsOfType <PredictiveChaser>(); foreach (var predictiveChaser in predictiveChasers) { predictiveChaser.TimeElapsed += dt; predictiveChaser.transform.position = PathSystem.PositionFromTrajectoryAtTime(predictiveChaser.Trajectory, predictiveChaser.TimeElapsed); } foreach (var predictiveChaser in predictiveChasers) { if (predictiveChaser.TimeElapsed >= predictiveChaser.Trajectory.duration) { predictiveChaser.transform.SetParent(predictiveChaser.Target, true); predictiveChaser.transform.localPosition = Vector3.zero; Destroy(predictiveChaser); } } foreach (var traveler in Travelers) { // every traveler needs to calculate the position of their target } // Set the required radius of all orbiters that are geosynchronous foreach (var geosynchronous in Geosynchronouses) { if (geosynchronous.TryGetComponent <Orbiter>(out Orbiter orbiter)) { const float MASS = 1; orbiter.Radius = Orbiter.RequiredRadiusForPeriod(MASS, G, geosynchronous.Rotator.Period); } } // Update all orbiters foreach (var orbiter in Orbiters) { // TODO: This mass value is stubbed out here but probably should be taken from the body itself? const float MASS = 1; float period = Orbiter.Period(orbiter, MASS, G); orbiter.Radians = (orbiter.Radians + (float)orbiter.Direction * TWO_PI / period * dt) % (TWO_PI); orbiter.transform.localPosition = orbiter.Radius * UnitCircle(orbiter.Radians); } // Update all rotators foreach (var rotator in Rotators) { rotator.Radians = (rotator.Radians + (float)rotator.Direction * TWO_PI / rotator.Period * dt) % (TWO_PI); rotator.transform.localRotation = Quaternion.AngleAxis(-rotator.Radians * Mathf.Rad2Deg, Vector3.up); } // Update all target reflectors foreach (var targetreflector in TargetReflectors) { float3 toSource = targetreflector.Source.transform.position - targetreflector.transform.position; float3 toTarget = targetreflector.Target.transform.position - targetreflector.transform.position; float3 halfway = normalize((normalize(toSource) + normalize(toTarget)) / 2); targetreflector.transform.forward = halfway; } // Update Skyhook Rockets foreach (var skyhookRocket in SkyHookRockets) { switch (skyhookRocket.CurrentPlan) { case SkyHookRocket.Plan.EnterOrbit: skyhookRocket.TimeRemaining -= dt; if (skyhookRocket.TimeRemaining <= 0) { Orbiter o = (Orbiter)skyhookRocket.gameObject.AddComponent(typeof(Orbiter)); o.Radians = 0; o.Radius = skyhookRocket.orbitRadius; o.Direction = Orbiter.RotationalDirection.CounterClockwise; Orbiters.Add(o); skyhookRocket.TimeRemaining = 0; skyhookRocket.transform.SetParent(skyhookRocket.Origin, true); skyhookRocket.CurrentPlan = SkyHookRocket.Plan.RideTheHook; } else { float3 currentPosition = skyhookRocket.transform.position; float3 futurePosition = FuturePosition(skyhookRocket.Origin, skyhookRocket.TimeRemaining); float fraction = skyhookRocket.TimeRemaining / skyhookRocket.TransitionTime; Debug.DrawLine(currentPosition, futurePosition, Color.red); skyhookRocket.TimeRemaining -= dt; skyhookRocket.transform.position = lerp(futurePosition, currentPosition, fraction); } break; case SkyHookRocket.Plan.RideTheHook: // Check if we should release if (Input.GetKeyDown(KeyCode.Space)) { float3 trajectory = FutureVelocity(skyhookRocket.transform, 0); Orbiter orbiter = skyhookRocket.GetComponent <Orbiter>(); Velocity velocity = (Velocity)skyhookRocket.gameObject.AddComponent(typeof(Velocity)); Orbiters.Remove(orbiter); Velocities.Add(velocity); Destroy(orbiter); velocity.Value = trajectory; skyhookRocket.transform.SetParent(null, true); skyhookRocket.transform.forward = normalize(trajectory); skyhookRocket.CurrentPlan = SkyHookRocket.Plan.Free; } break; } } foreach (var velocity in Velocities) { velocity.transform.position += dt * (Vector3)velocity.Value; } foreach (var orbitPredictor in OrbitPredictors) { int count = orbitPredictor.LineRenderer.positionCount; for (int i = 0; i < count; i++) { float time = (float)i / (float)(count - 1) * OrbitPredictorTimeInTheFuture; float3 velocity = FutureVelocity(orbitPredictor.transform, 0); float3 trajectory = normalize(velocity); float3 toTarget = new float3(0, 0, 0) - (float3)orbitPredictor.transform.position; float3 towardsTarget = normalize(toTarget); float trajectoryDotTowardsTarget = dot(trajectory, towardsTarget); orbitPredictor.LineRenderer.SetPosition(i, FuturePosition(orbitPredictor.transform, time)); } } // Raytracing for light sources RaytracingSystem.LightSources = LightSources; RaytracingSystem.LightSourceCount = RaytracingSystem.LightSources.Length; RaytracingSystem.Schedule(); // Raytrace rendering for (int i = 0; i < RaytracingSystem.Traces.Count; i++) { var trace = RaytracingSystem.Traces[i]; var lr = LineRenderers[i]; var logEnergy = pow(trace.Energy, ScalePower); var width = EnergyToWidthCurve.Evaluate(logEnergy); var intensity = EnergyToIntensityCurve.Evaluate(logEnergy); var emissionColor = intensity * BeamColor; lr.positionCount = 2; lr.SetPosition(0, trace.From); lr.SetPosition(1, trace.To); lr.startWidth = width; lr.endWidth = width; lr.material.SetColor("Color", BeamColor); lr.material.SetColor("_EmissionColor", emissionColor); lr.gameObject.SetActive(true); } for (int i = RaytracingSystem.Traces.Count; i < LineRenderers.Length; i++) { LineRenderers[i].gameObject.SetActive(false); } // Render orbits OrbitRenderingSystem.Orbiters = Orbiters; OrbitRenderingSystem.Count = OrbitRenderingSystem.Orbiters.Count; OrbitRenderingSystem.Schedule(); // Update the spacefield SpaceField.NormalizedMasses = NormalizedMasses; SpaceField.Count = SpaceField.NormalizedMasses.Length; SpaceField.Schedule(); }