The OrbitPoint component places the attached game object or NBody at a specific location on an existing orbit. Uses include showing a label for apoapsis and periapsis or indicating a position of a future course change.
This video demonstrates the uses of this component:
The choices for the location of an OrbitPoint are:
- APOAPSIS: Farthest point of the orbit (in the case of an ellipse). Not defined for an orbit with eccentricty >= 1.0
- PERIAPSIS: Closest point of approach
- ALTITUDE: Position at which the radius will be the value indicated in the Point Data field. If the altitude is outside apo/peri for the orbit the point will be placed at the apo/peri point.
- ASCENDING_NODE: the point where an inclined orbit cross the XY plane in the ascending direction
- DESCENDING_NODE: the point where an inclined orbit crosses the XY plane in a descending direction
- FIXED_TIME: a position a fixed GE time ahead of an NBody object specified by the TimeRefBody field. The time offset is specified with Point Data
- PHASE: a fixed phase position in the orbit specified by Point Data
- PHASE_FROM_MOUSE: An interactive mode that allows a user to click on the orbit to define the location of the orbit point.
Inspector Fields
- Mouse Control: Handle mouse input directly. When disabled a controller can call HandleMouseInput() to allow this component to operate. Only relevant for PHASE_FROM_MOUSE point type.
- Orbit Predictor: The orbit on which the orbit point will be placed
- Point Type: The type of point (see above)
- Point Data: auxillary data. Specific use depends on the point type
- Time Wrt Ref Body: The NBody on the orbit specified to be used as a reference point when the point type is FIXED_TIME.
- Scene Camera: the camera active in the scene. Only used in PHASE_FROM_MOUSE mode to map 3D location to screen space.
Implementation Notes
The point type logic is found in an obvious switch() statement in the OrbitPoint Update() method. Several of the point types are straight forward, but the implementation of FIXED_TIME and PHASE_FROM_MOUSE provide useful examples of code that may be useful in other contexts. They are expanded upon below.
FIXED_TIME
In FIXED_TIME mode the position of the orbit point is determined by finding a point on the orbit that is a specific time ahead of the TimeRefBody. To accomplish this the logic flow is:
- Create an OrbitUniversal from the current data of the TimeRefBody and set this to Kepler mode.
- Lock this OrbitUniversal object at the required time (current time + the time offset from PointData)
- Retrieve the position from the OrbitUniversal and use this to position the OrbitPoint
- Destroy the OrbitUniversal
In code this is:
// Don't assume the NBody has an orbit, build a new OrbitData from internal details
// Greedy implementation - could move some to SetTime
// TODO: Check if things have changed before doing all this every frame
OrbitUniversal targetOrbit = timeRefBody.gameObject.AddComponent<OrbitUniversal>();
targetOrbit.InitFromActiveNBody(timeRefBody, orbitU.centerNbody, OrbitUniversal.EvolveMode.KEPLERS_EQN);
double[] r_new = new double[] { 0, 0, 0 };
targetOrbit.LockAtTime(GravityEngine.Instance().GetPhysicalTimeDouble() + pointData);
Vector3d pos = new Vector3d();
Vector3d vel = new Vector3d();
double time0 = 0;
targetOrbit.GetRVTLastEvolve(ref pos, ref vel, ref time0);
if (nbody != null) {
ge.SetPositionDoubleV3(nbody, pos);
ge.SetVelocityDoubleV3(nbody, vel);
} else {
transform.position = ge.MapPhyPosToWorld(new Vector3((float)r_new[0], (float)r_new[1], (float)r_new[2]));
}
MonoBehaviour.Destroy(targetOrbit);
PHASE_FROM_MOUSE Implementation
This implementation demonstrates a technique for using player mouse clicks to make reference to objects in the 3D GE world. The mouse is used to indicate a direction with respect to the center body of the orbit. This phase angle is then used to position the OrbitPoint.
The logic for this is relies on finding a directional line for the mouse click and then using it’s angle with respect the line to the periapsis of the orbit (where phase=0)
- obtain the mouse click in screen space coordinates
- map the orbit center (using the transform position directly since this is the 3D world position) into screen space using the Camera WorldToScreenPoint() method
- use these to determine a directional vector of the line from the mouse click to the orbit center in screen space
- determine a periapsis line in screen space by taking the position of phase=0 mapped to screen space with respect to the center body position in screen space
- subtract the mouse phase from the reference phase
Note that the use of Atan2 avoid the need for quadrant checks for these angles.
The corresponding code is:
Vector3 mousePos = Input.mousePosition;
Vector3 originPos = orbitU.centerNbody.transform.position;
Vector3 origin = sceneCamera.WorldToScreenPoint(originPos);
relativeMousePos = (mousePos - origin).normalized;
// peri: Need to map peri position onto the screen via world position.
// (peri position includes center body offset)
Vector3 periPos = ge.MapPhyPosToWorld( orbitU.PositionForPhase(0f));
Vector3 periLine = (sceneCamera.WorldToScreenPoint(periPos) - origin).normalized;
float periPhase = Mathf.Atan2(periLine.y, periLine.x) * Mathf.Rad2Deg;
mousePhase = Mathf.Atan2(relativeMousePos.y, relativeMousePos.x) * Mathf.Rad2Deg - periPhase;