Skip to content

Commit

Permalink
Add an option to display planet trajectories
Browse files Browse the repository at this point in the history
  • Loading branch information
SkutteOleg committed Nov 18, 2022
1 parent 67bf0e8 commit 34d386d
Show file tree
Hide file tree
Showing 11 changed files with 278 additions and 138 deletions.
8 changes: 5 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,16 +65,18 @@ jobs:

- name: Build Mod
run: dotnet build -c Release
working-directory: ${{ env.PROJ_NAME }}

- name: Upload Artifact
uses: "actions/upload-artifact@v3"
with:
name: "${{ env.PROJ_USERNAME }}.${{ env.PROJ_NAME }}"
path: "${{ env.PROJ_USERNAME }}.${{ env.PROJ_NAME }}"
path: ${{ env.PROJ_NAME }}/bin/Release/

- name: Move
run: mkdir ${{ env.PROJ_USERNAME }}.${{ env.PROJ_NAME }}; move ./${{ env.PROJ_NAME }}/bin/Release/* ${{ env.PROJ_USERNAME }}.${{ env.PROJ_NAME }}

- name: Zip For Release
run: 7z a ${{ env.PROJ_USERNAME }}.${{ env.PROJ_NAME }}.zip ${{ env.PROJ_NAME }}/bin/Release/**
run: 7z a ${{ env.PROJ_USERNAME }}.${{ env.PROJ_NAME }}.zip ${{ env.PROJ_USERNAME }}.${{ env.PROJ_NAME }}**

- name: Create Release
uses: "ncipollo/release-action@v1"
Expand Down
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,20 @@ This is a mod for **Outer Wilds** which predicts and visualizes future trajector

## Settings
![Settings](https://user-images.githubusercontent.com/45887963/202139282-0c378a33-0c12-4907-99e4-c98604dcabe1.jpg)
##### Simulation Settings
#### Simulation Settings
- **Seconds To Predict** - Determines how far into the future trajectories are predicted in seconds. Higher values take longer to compute.
- **High Precision Mode** - Toggles simulation time step between 1 second and `Time.fixedDeltaTime`. High Precision Mode takes 60 times longer to compute.
- **Predict GravityVolume Intersections** - Future trajectories may escape or enter gravity volumes of different celestial bodies. This setting toggles detection of which gravity volumes will be active at any given future time step. Takes longer to compute and allocates more memory.
##### Customization Settings
#### Customization Settings
- **Player Trajectory Color** - Hex RGBA color of trajectory line of the player.
- **Ship Trajectory Color** - Hex RGBA color of trajectory line of the ship.
- **Scout Trajectory Color** - Hex RGBA color of trajectory line of the scout.
##### Performance Settings
- **Display Planet Trajectories** - Determines whether planet trajectories should be displayed.
- **Planet Trajectory Color** - Hex RGBA color of trajectory lines of the planets.
#### Performance Settings
- **Multithreading** - Shifts computations to a separate thread.
- **RAM To Allocate (Megabytes)** - Allocates roughly specified amount of RAM to reduce the frequency of GC lag spikes.
##### Experimental Settings
#### Experimental Settings
- **Parallelization** - Runs simulation of all celestial bodies in parallel. Speeds up computation but makes results inaccurate.

## Limitations / Things to Improve
Expand Down
52 changes: 37 additions & 15 deletions TrajectoryPrediction/AstroObjectTrajectory.cs
Original file line number Diff line number Diff line change
@@ -1,25 +1,32 @@
using System;
using System.Threading;
using UnityEngine;

namespace TrajectoryPrediction;

public class AstroObjectTrajectory : MonoBehaviour
public class AstroObjectTrajectory : MonoBehaviour, ITrajectory
{
public bool Busy { get; private set; }
public Vector3[] Trajectory { get; private set; }
public Vector3[] TrajectoryCache { get; private set; }
private AstroObject _astroObject;
private OWRigidbody _body;
private GravityVolume _gravityVolume;
private float _triggerRadius;
private bool _updatedThisFrame;
private Vector3[] _trajectory;
private Vector3[] _trajectoryCache;
private Vector3 _framePosition;
private Vector3 _frameVelocity;
private TrajectoryVisualizer _visualizer;

private void Start()
{
_astroObject = GetComponent<AstroObject>();
_body = _astroObject.GetOWRigidbody();
_gravityVolume = _astroObject.GetGravityVolume();

if (_gravityVolume)
_visualizer = gameObject.AddComponent<TrajectoryVisualizer>();

TrajectoryPrediction.AstroObjectToTrajectoryMap[_astroObject] = this;
if (_gravityVolume)
{
Expand Down Expand Up @@ -55,8 +62,15 @@ private void OnDestroy()

private void ApplyConfig()
{
_trajectory = new Vector3[TrajectoryPrediction.StepsToSimulate];
_trajectoryCache = new Vector3[TrajectoryPrediction.StepsToSimulate];
Trajectory = new Vector3[TrajectoryPrediction.StepsToSimulate];
TrajectoryCache = new Vector3[TrajectoryPrediction.StepsToSimulate];
Busy = false;

if (_visualizer)
{
_visualizer.SetVisibility(TrajectoryPrediction.DisplayPlanetTrajectories);
_visualizer.SetColor(TrajectoryPrediction.PlanetTrajectoriesColor);
}
}

private void BeginFrame()
Expand All @@ -73,10 +87,10 @@ private void BeginFrame()

private void EndFrame()
{
_trajectory.CopyTo(_trajectoryCache);
Trajectory.CopyTo(TrajectoryCache);
}

internal void UpdateTrajectory()
public void UpdateTrajectory()
{
if (_updatedThisFrame)
return;
Expand All @@ -85,26 +99,34 @@ internal void UpdateTrajectory()

if (_body == null || _body.GetAttachedForceDetector() == null)
{
for (var i = 0; i < _trajectory.Length; i++)
_trajectory[i] = _astroObject.transform.position;
for (var i = 0; i < Trajectory.Length; i++)
Trajectory[i] = _astroObject.transform.position;
}
else
{
if (TrajectoryPrediction.Parallelization)
TrajectoryPrediction.SimulateTrajectoryMultiThreaded(_body, _framePosition, _frameVelocity, _trajectory);
if (TrajectoryPrediction.Parallelization || TrajectoryPrediction.Multithreading && Thread.CurrentThread == TrajectoryPrediction.MainThread)
{
Busy = true;
TrajectoryPrediction.SimulateTrajectoryMultiThreaded(_body, _framePosition, _frameVelocity, Trajectory, null, false, false, () => Busy = false);
}
else
TrajectoryPrediction.SimulateTrajectory(_body, _framePosition, _frameVelocity, _trajectory);
TrajectoryPrediction.SimulateTrajectory(_body, _framePosition, _frameVelocity, Trajectory);
}
}

public Vector3 GetCurrentPosition()
{
return _body ? _body.GetPosition() : _astroObject.transform.position;
}

public Vector3 GetFuturePosition(int step)
{
return _trajectory[Math.Max(step, 0)];
return Trajectory[Math.Max(step, 0)];
}

public Vector3 GetFuturePositionCached(int step)
{
return _trajectoryCache[Math.Max(step, 0)];
return TrajectoryCache[Math.Max(step, 0)];
}

public GravityVolume GetGravityVolume()
Expand Down
2 changes: 1 addition & 1 deletion TrajectoryPrediction/CanvasMapMarkerPatch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ public class CanvasMapMarkerPatch
private static void MapMarker_InitMarker(MapMarker __instance)
{
if (__instance._markerType is MapMarker.MarkerType.Ship or MapMarker.MarkerType.Probe or MapMarker.MarkerType.Player)
__instance._canvasMarker.gameObject.AddComponent<TrajectoryVisualizer>().SetMarkerType(__instance._markerType);
__instance._canvasMarker.gameObject.AddComponent<MapMarkerTrajectory>().SetMarkerType(__instance._markerType);
}
}
4 changes: 2 additions & 2 deletions TrajectoryPrediction/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ public static Vector3 CalculateForceAccelerationAtFuturePoint(this GravityVolume
return delta / distance * gravityMagnitude;
}

public static AstroObjectTrajectory GetTrajectory(this GravityVolume gravityVolume)
public static ITrajectory GetTrajectory(this GravityVolume gravityVolume)
{
return TrajectoryPrediction.GravityVolumeToTrajectoryMap.ContainsKey(gravityVolume) ? TrajectoryPrediction.GravityVolumeToTrajectoryMap[gravityVolume] : null;
}

public static AstroObjectTrajectory GetTrajectory(this AstroObject astroObject)
public static ITrajectory GetTrajectory(this AstroObject astroObject)
{
return TrajectoryPrediction.AstroObjectToTrajectoryMap.ContainsKey(astroObject) ? TrajectoryPrediction.AstroObjectToTrajectoryMap[astroObject] : null;
}
Expand Down
14 changes: 14 additions & 0 deletions TrajectoryPrediction/ITrajectory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using UnityEngine;

namespace TrajectoryPrediction;

public interface ITrajectory
{
bool Busy { get; }
Vector3[] Trajectory { get; }
Vector3[] TrajectoryCache { get; }
void UpdateTrajectory();
Vector3 GetCurrentPosition();
Vector3 GetFuturePosition(int step);
Vector3 GetFuturePositionCached(int step);
}
162 changes: 162 additions & 0 deletions TrajectoryPrediction/MapMarkerTrajectory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
using System;
using System.Linq;
using System.Threading;
using UnityEngine;

namespace TrajectoryPrediction;

public class MapMarkerTrajectory : MonoBehaviour, ITrajectory
{
public bool Busy { get; private set; }
public Vector3[] Trajectory { get; private set; }
public Vector3[] TrajectoryCache { get; private set; }
private CanvasMapMarker _marker;
private MapMarker.MarkerType _markerType;
private OWRigidbody _body;
private ForceDetector _forceDetector;
private bool _updatedThisFrame;
private Vector3 _framePosition;
private Vector3 _frameVelocity;
private TrajectoryVisualizer _visualizer;

private void Start()
{
_marker = GetComponent<CanvasMapMarker>();
_body = _marker._rigidbodyTarget;
_forceDetector = _body.GetAttachedForceDetector();
_visualizer = gameObject.AddComponent<TrajectoryVisualizer>();
ApplyConfig();

TrajectoryPrediction.OnConfigUpdate += ApplyConfig;
TrajectoryPrediction.OnBeginFrame += BeginFrame;
TrajectoryPrediction.OnEndFrame += EndFrame;
_marker.OnMarkerChangeVisibility += SetVisibility;

if (_markerType == MapMarker.MarkerType.Player)
{
GlobalMessenger.AddListener("EnterShip", OnEnterShip);
GlobalMessenger.AddListener("ExitShip", OnExitShip);
OnEnterShip();
}
}

private void OnDestroy()
{
TrajectoryPrediction.OnConfigUpdate -= ApplyConfig;
TrajectoryPrediction.OnBeginFrame -= BeginFrame;
TrajectoryPrediction.OnEndFrame -= EndFrame;
_marker.OnMarkerChangeVisibility -= SetVisibility;

if (_markerType == MapMarker.MarkerType.Player)
{
GlobalMessenger.RemoveListener("EnterShip", OnEnterShip);
GlobalMessenger.RemoveListener("ExitShip", OnExitShip);
}
}

internal void SetMarkerType(MapMarker.MarkerType markerType)
{
_markerType = markerType;
}

private void SetVisibility(bool value)
{
_visualizer.SetVisibility(value);
}

private void ApplyConfig()
{
Trajectory = new Vector3[TrajectoryPrediction.StepsToSimulate];
TrajectoryCache = new Vector3[TrajectoryPrediction.StepsToSimulate];
Busy = false;
switch (_markerType)
{
case MapMarker.MarkerType.Player:
_visualizer.SetColor(TrajectoryPrediction.PlayerTrajectoryColor);
break;
case MapMarker.MarkerType.Ship:
_visualizer.SetColor(TrajectoryPrediction.ShipTrajectoryColor);
break;
case MapMarker.MarkerType.Probe:
_visualizer.SetColor(TrajectoryPrediction.ScoutTrajectoryColor);
break;
default:
throw new ArgumentOutOfRangeException(nameof(_markerType));
}
}

private void BeginFrame()
{
_updatedThisFrame = false;
_framePosition = _body.GetPosition();
_frameVelocity = _body.GetVelocity();
}

private void EndFrame()
{
Trajectory.CopyTo(TrajectoryCache);
}

private void OnEnterShip()
{
_body = Locator.GetShipBody();
_forceDetector = _body.GetAttachedForceDetector();
}

private void OnExitShip()
{
_body = Locator.GetPlayerBody();
_forceDetector = _body.GetAttachedForceDetector();
}

public void UpdateTrajectory()
{
if (_updatedThisFrame)
return;

_updatedThisFrame = true;

if (_forceDetector._activeVolumes.Count == 0 || _forceDetector._activeVolumes.All(volume => volume is not GravityVolume))
{
if (TrajectoryPrediction.Multithreading)
{
Busy = true;
new Thread(() =>
{
for (var i = 0; i < Trajectory.Length; i++)
Trajectory[i] = Vector3.zero;

Busy = false;
}).Start();
}
else
for (var i = 0; i < Trajectory.Length; i++)
Trajectory[i] = _framePosition;
}
else
{
if (TrajectoryPrediction.Multithreading)
{
Busy = true;
TrajectoryPrediction.SimulateTrajectoryMultiThreaded(_body, _framePosition, _frameVelocity, Trajectory, Locator.GetReferenceFrame()?.GetAstroObject(), true, TrajectoryPrediction.PredictGravityVolumeIntersections, () => Busy = false);
}
else
TrajectoryPrediction.SimulateTrajectory(_body, _framePosition, _frameVelocity, Trajectory, Locator.GetReferenceFrame()?.GetAstroObject(), true, TrajectoryPrediction.PredictGravityVolumeIntersections);
}
}

public Vector3 GetCurrentPosition()
{
return _body.GetPosition();
}

public Vector3 GetFuturePosition(int step)
{
return Trajectory[Math.Max(step, 0)];
}

public Vector3 GetFuturePositionCached(int step)
{
return TrajectoryCache[Math.Max(step, 0)];
}
}
Loading

0 comments on commit 34d386d

Please sign in to comment.