Skip to content

Instantly share code, notes, and snippets.

@brihernandez
Last active April 26, 2024 18:36
Show Gist options
  • Star 47 You must be signed in to star a gist
  • Fork 12 You must be signed in to fork a gist
  • Save brihernandez/9ebbaf35070181fa1ee56f9e702cc7a5 to your computer and use it in GitHub Desktop.
Save brihernandez/9ebbaf35070181fa1ee56f9e702cc7a5 to your computer and use it in GitHub Desktop.
Floating origin to handle large worlds in Unity.
// Based on the Unity Wiki FloatingOrigin script by Peter Stirling
// URL: http://wiki.unity3d.com/index.php/Floating_Origin
using UnityEngine;
using UnityEngine.SceneManagement;
public class FloatingOrigin : MonoBehaviour
{
[Tooltip("Point of reference from which to check the distance to origin.")]
public Transform ReferenceObject = null;
[Tooltip("Distance from the origin the reference object must be in order to trigger an origin shift.")]
public float Threshold = 5000f;
[Header("Options")]
[Tooltip("When true, origin shifts are considered only from the horizontal distance to orign.")]
public bool Use2DDistance = false;
[Tooltip("When true, updates ALL open scenes. When false, updates only the active scene.")]
public bool UpdateAllScenes = true;
[Tooltip("Should ParticleSystems be moved with an origin shift.")]
public bool UpdateParticles = true;
[Tooltip("Should TrailRenderers be moved with an origin shift.")]
public bool UpdateTrailRenderers = true;
[Tooltip("Should LineRenderers be moved with an origin shift.")]
public bool UpdateLineRenderers = true;
private ParticleSystem.Particle[] parts = null;
void LateUpdate()
{
if (ReferenceObject == null)
return;
Vector3 referencePosition = ReferenceObject.position;
if (Use2DDistance)
referencePosition.y = 0f;
if (referencePosition.magnitude > Threshold)
{
MoveRootTransforms(referencePosition);
if (UpdateParticles)
MoveParticles(referencePosition);
if (UpdateTrailRenderers)
MoveTrailRenderers(referencePosition);
if (UpdateLineRenderers)
MoveLineRenderers(referencePosition);
}
}
private void MoveRootTransforms(Vector3 offset)
{
if (UpdateAllScenes)
{
for (int z = 0; z < SceneManager.sceneCount; z++)
{
foreach (GameObject g in SceneManager.GetSceneAt(z).GetRootGameObjects())
g.transform.position -= offset;
}
}
else
{
foreach (GameObject g in SceneManager.GetActiveScene().GetRootGameObjects())
g.transform.position -= offset;
}
}
private void MoveTrailRenderers(Vector3 offset)
{
var trails = FindObjectsOfType<TrailRenderer>() as TrailRenderer[];
foreach (var trail in trails)
{
Vector3[] positions = new Vector3[trail.positionCount];
int positionCount = trail.GetPositions(positions);
for (int i = 0; i < positionCount; ++i)
positions[i] -= offset;
trail.SetPositions(positions);
}
}
private void MoveLineRenderers(Vector3 offset)
{
var lines = FindObjectsOfType<LineRenderer>() as LineRenderer[];
foreach (var line in lines)
{
Vector3[] positions = new Vector3[line.positionCount];
int positionCount = line.GetPositions(positions);
for (int i = 0; i < positionCount; ++i)
positions[i] -= offset;
line.SetPositions(positions);
}
}
private void MoveParticles(Vector3 offset)
{
var particles = FindObjectsOfType<ParticleSystem>() as ParticleSystem[];
foreach (ParticleSystem system in particles)
{
if (system.main.simulationSpace != ParticleSystemSimulationSpace.World)
continue;
int particlesNeeded = system.main.maxParticles;
if (particlesNeeded <= 0)
continue;
// ensure a sufficiently large array in which to store the particles
if (parts == null || parts.Length < particlesNeeded)
{
parts = new ParticleSystem.Particle[particlesNeeded];
}
// now get the particles
int num = system.GetParticles(parts);
for (int i = 0; i < num; i++)
{
parts[i].position -= offset;
}
system.SetParticles(parts, num);
}
}
}
@dawkstorm
Copy link

Thank very much!

@marcospgp
Copy link

Nice, I also built one based on the same example! I optimized it specifically for multiplayer: https://gist.github.com/marcospgp/42562d3b23b37610f29828cfef674b3a

@AviaSimDev
Copy link

AviaSimDev commented Jan 24, 2022

Its a nice script, which does its job! but my rigidbody physics are messed up. I use this template https://github.com/gasgiant/Aircraft-Physics , which feel very realistic and are working fine. Shoulddn't i add this to the rigidbody object? should I create a extra empty gameobject in the plane? pls help me!

here is a example video: https://youtu.be/OfvPGPyzdAM

Cheers, Jonas

@brihernandez
Copy link
Author

brihernandez commented Jan 25, 2022

@AviaSimDev It looks like the wings "feel" that they're pushed back by the floating origin. If that's the case, when the origin is shifted you'll need to also do something with the wings to make sure their state is unaffected by the origin shift.

E.g. what they see now is the below, where there is some major discontinuity between the previous position and current position.

Frame Position
1 210
2 211
3 212
4 0
5 1
6 2

You'll need to edit the code so that this discontinuity is accounted for. How you do that will depend on how the Aircraft-Physics code works, but I'm guessing (I could be wrong!) that there is some internal position that is being cached and used to derive a velocity. When the origin shifts, you'll need to adjust that internal position appropriately.

I use a static event that things can register to, so they can listen to the origin shift and shift internal data that is independent of GameObjects. E.g. public static System.Action<Vector3> OnOriginShiftFinished;

@Sixoul
Copy link

Sixoul commented Feb 2, 2022

Does this support multiplayer?

@skyspy
Copy link

skyspy commented Jan 20, 2023

Unfortunately, this threshold-based shifting is not floating origin. It never was. Real floating origin has no threshold. It never moves the player from the origin. Therefore, this shifting system is really the exact opposite.
Please rename the references to "floating origin" to something that reflects the truth: Unreal used the term "origin-rebasing". It serves no good purpose to misrepresent the work of others and mislead developers.
@AviaSimDev whenever there is a shift there is a large instantaneous acceleration, so it is not a surprise there are issues. Also, try moving at a speed that exceeds the shift size per second. That is when the shifting system goes into continuous failure.

@marcospgp
Copy link

@skyspy mom: we have floating origin at home
floating origin at home:

@boarnoah
Copy link

boarnoah commented Jan 8, 2024

It fascinates me, how at least at a surface level this looks relatively straightforward to do in Unity.
In terms of getting references to things like all relevant particle systems in the world and then moving them around etc...

One of the blessings that come with a curse in Unreal I've noticed is that you get a lot of very feature rich systems out of the box, but doing something like this is very much non trivial without making relatively significant changes to the engine source.

@skyspy
Copy link

skyspy commented Jan 8, 2024

It fascinates me, how at least at a surface level this looks relatively straightforward to do in Unity. In terms of getting references to things like all relevant particle systems in the world and then moving them around etc...

One of the blessings that come with a curse in Unreal I've noticed is that you get a lot of very feature rich systems out of the box, but doing something like this is very much non trivial without making relatively significant changes to the engine source.

You are right, the basic idea is very simple, and the continuous floating origin method is simpler than the shifty approach, but designing such an algorithm at the core of a framework for games/sim requires an entirely new structure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment