So I'm going to expound on something programming WRT coordinate system handling. tl;dr - You probably don't want to do this. It's overkill with a nuclear-powered sledgehammer. Also, Unity is inconsistent over whether or not it draws what it considers "unreasonably large or distant" objects. But It's been a long time since I wrote a long post on something programming, and this is useful if you ever want to, say, write a game called, say, "Star Dangerous" or something. Or the next SurvivalCraft. Or are writing your own game engine. *cough*
Upside: Using just integers, it allows a map size of 119 light-minutes. Using long integers, it can be easily expanded to 974,904 light-years when using Unity's float32 vectors. Downside: You need to write your own coordinate system for which Unity's is a mere back-end, and also requires writing your own physics engine hooks.
The system: It looks something like floating-origin. Every time any discrete, top-level entity (such as the spaceship, but not the guns on the spaceship) moves more than 1,000m away from the origin, it's re-centered. Except rather than being re-centered with respect to some other top-level entity, the re-centering is entirely internal.
Inside that script, you maintain three integers or long integers named something like GlobalX, GlobalY, GlobalX, and a Vector3 named something like Local. Then, to get the position of anything, you just subtract your GlobalXYZ from the center top-level entity's GlobalXYZ, and the same for the LocalXYZ's. Then you add the resulting GlobalXYZ * 1000.0F to the resulting Vector3. Then you rotate by the inverse of the center-point's rotation.
Simple. Everything's in the exact right position, and everything displays correctly. Right, my job's done here.
...Orrr maybe I should demystify that with some code. Code is in Unity Standard Old C#, as the last I've heard, the Net Standard 2.0 was still experimental, and not everyone can update.
Code: Select all
using System;
using UnityEngine;
public class UniverseCoordinates : MonoBehaviour
{
[SerializeField]
private int globalX, globalY, globalZ;
[SerializeField]
private Vector3 local;
[SerializeField]
private Quaternion orientation;
private void Update()
{
// Do stuff that changes local.
// Maybe use an accumulative adder, so that small values are kept.
// Maybe keep track of any precision not added.
int globalMoveX += ((int)local.x / 1000);
int globalMoveY += ((int)local.y / 1000);
int globalMoveZ += ((int)local.z / 1000);
local.x -= globalMoveX * 1000.0F;
local.y -= globalMoveY * 1000.0F;
local.z -= globalMoveZ * 1000.0F;
globalX += globalMoveX;
globalY += globalMoveY;
globalZ += globalMoveZ;
}
public static Coord operator-(UniverseCoordinates lhs, UniverseCoordinates rhs)
{
int globalDiffX = lhs.globalX - rhs.global.X;
int globalDiffY = lhs.globalX - rhs.global.Y;
int globalDiffZ = lhs.globalX - rhs.global.Z;
Vector3 localDiff = lhs.local - rhs.local;
Vector3 displacement =
localDiff +
new Vector3(
globalDiffX * 1000.0F,
globalDiffY * 1000.0F,
globalDiffZ * 1000.0F
);
// With this, we can act like our center is pointing straight forward.
// This also means our view matrix doesn't need to rotate.
return
new Coord(
Quaternion.Inverse(rhs.orientation) * displacement,
lhs.orientation
);
}
}
[RequireComponent(typeof(UniverseCoordinates))]
public class CenterCoordinates : MonoBehaviour
{
private static UniverseCoordinates _CENTER;
public static UniverseCoordinates CENTER
{
get { return _CENTER; }
set
{
// This guard formation handles the _CENTER being set after we check.
if (_CENTER != null)
{
lock (_CENTER)
{
if (_CENTER != null)
{
_CENTER = value;
}
else
{
throw new ArgumentOutOfRangeException("Too many centers.");
}
}
}
}
}
private void Start()
{
CENTER = GetComponent<UniverseCoordinates>();
}
}
public struct Coord
{
// You shouldn't need to change this.
public readonly Vector3 Displacement;
// You shouldn't need to change this.
public readonly Quaterion Orientation;
public Coord(Vector3 displacement, Quaternion orientation)
{
Displacement = displacement;
Orientation = orientation;
}
}