Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A floating origin script for Unity.
// Based on script found at http://wiki.unity3d.com/index.php/Floating_Origin
// on 2021-05-13, modified substantially - mostly to accomodate multiplayer,
// by introducing threshold and offset values.
using UnityEngine;
public class FloatingOrigin : MonoBehaviour {
public static FloatingOrigin Instance;
// Largest value allowed for the main camera's X or Z coordinate before that
// coordinate is moved by the same amount towards 0 (which updates offset).
// Pick a power of two for this, as floating point precision (the thing
// we are trying to regulate) decreases with every successive power of two.
public const float threshold = (float) Threshold._4;
private ParticleSystem.Particle[] parts = null;
private Transform anchor;
// The origin is offset by offset * threshold
public (byte x, byte y, byte z) Offset { get; private set; } = (0, 0, 0);
public enum Threshold {
_2 = 2,
_4 = 4,
_8 = 8,
_16 = 16,
_32 = 32,
_64 = 64,
_128 = 128,
_256 = 256,
_512 = 512,
_1024 = 1024
}
public void OnEnable() {
// Ensure singleton
if (Instance != null) {
Destroy(gameObject);
throw new System.Exception(
"More than one instance of singleton detected."
);
} else {
Instance = this;
}
}
public void LateUpdate() {
if (anchor == null) {
var camera = Camera.main;
if (camera != null) {
anchor = camera.transform;
} else {
return;
}
}
// Calculate offset
Vector3 offsetToApply;
float value;
if (Mathf.Abs(anchor.position.x) > threshold) {
value = anchor.position.x;
offsetToApply = new Vector3(1f, 0f, 0f);
} else if (Mathf.Abs(anchor.position.y) > threshold) {
value = anchor.position.y;
offsetToApply = new Vector3(0f, 1f, 0f);
} else if (Mathf.Abs(anchor.position.z) > threshold) {
value = anchor.position.z;
offsetToApply = new Vector3(0f, 0f, 1f);
} else {
return;
}
float times = Mathf.Floor(Mathf.Abs(value) / threshold);
float offsetSign = Mathf.Sign(value) * -1f;
Offset = (
(byte) (Offset.x + (offsetToApply.x * times * offsetSign)),
(byte) (Offset.y + (offsetToApply.y * times * offsetSign)),
(byte) (Offset.z + (offsetToApply.z * times * offsetSign))
);
float delta = threshold * times * offsetSign;
offsetToApply *= delta;
// Offset scene root objects
GameObject[] objects = UnityEngine.SceneManagement.SceneManager
.GetActiveScene().GetRootGameObjects();
foreach (var o in objects) {
Transform t = o.GetComponent<Transform>();
t.position += offsetToApply;
}
// Offset world-space particles
ParticleSystem[] particleSystems = FindObjectsOfType<ParticleSystem>();
foreach (var sys in particleSystems) {
if (sys.main.simulationSpace != ParticleSystemSimulationSpace.World)
continue;
int particlesNeeded = sys.main.maxParticles;
if (particlesNeeded <= 0)
continue;
bool wasPaused = sys.isPaused;
bool wasPlaying = sys.isPlaying;
if (!wasPaused)
sys.Pause ();
// 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 = sys.GetParticles(parts);
for (int i = 0; i < num; i++) {
parts[i].position += offsetToApply;
}
sys.SetParticles(parts, num);
if (wasPlaying)
sys.Play ();
}
}
}
@INeatFreak
Copy link

INeatFreak commented May 31, 2022

Do you mind explaining why you're pausing and replaying the ParticleSystems? Was this causing an issue for you? This should set particle positions at the same frame so there shouldn't be any problems in theory :/

@marcospgp
Copy link
Author

marcospgp commented Jun 6, 2022

@Grav8 The particle logic is based on the original script (link now dead, archived version here). I believe the only issue is that particles in world space need to be moved together with the rest of the world, otherwise it would seem as if they teleported far away.

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