Since it’s saturday I have some spare time and fired up Unity once again (hadn’t done anything in the last year). I quick and dirty created a space charactercontroller very similar to the one of space engineers. Though, no magnetic boots outside a gravity field and when in jetpack mode gravity is generally off, even when the inertia dampers are off. So gravity only works when the jetpack is off.
SpaceCharacterController.cs
using UnityEngine;
public class SpaceCharacterController : MonoBehaviour
{
public bool useJetpack = false;
public bool inertiaDamper = true;
public Camera cam;
public float mouseSensitivity = 10f; // 2f for webgl
public float rollSpeed = 90f;
public float alignSpeed = 180f;
public float moveSpeed = 5.0f;
public float flyAcc = 5.0f;
public float maxAngle = 80f;
private Transform player;
private Transform camT;
private Rigidbody rb;
public bool onGround; // for debugging purposes
void Start()
{
player = transform;
camT = cam.transform;
rb = GetComponent<Rigidbody>();
}
void Update()
{
// update gravity based on local gravity wells.
Physics.gravity = GravityWell.GetClosestGravity(player.position);
if (Input.GetMouseButtonDown(0))
Cursor.lockState = CursorLockMode.Locked;
if (Input.GetKeyDown(KeyCode.X))
useJetpack = !useJetpack;
if (Input.GetKeyDown(KeyCode.Z))
inertiaDamper = !inertiaDamper;
rb.useGravity = !useJetpack;
if (useJetpack)
JetPackBehaviour();
else
GravityWellBehaviour();
}
void JetPackBehaviour()
{
// camera align with player, as jetpack movement will rotate the whole player
camT.localRotation = Quaternion.RotateTowards(camT.localRotation, Quaternion.identity, 10 * alignSpeed * Time.deltaTime);
// rotation
player.rotation = Quaternion.AngleAxis(Input.GetAxis("Mouse X") * mouseSensitivity, player.up) * player.rotation;
player.rotation = Quaternion.AngleAxis(-Input.GetAxis("Mouse Y") * mouseSensitivity, player.right) * player.rotation;
if (Input.GetKey(KeyCode.Q))
player.rotation = Quaternion.AngleAxis(rollSpeed * Time.deltaTime, player.forward) * player.rotation;
else if (Input.GetKey(KeyCode.E))
player.rotation = Quaternion.AngleAxis(-rollSpeed * Time.deltaTime, player.forward) * player.rotation;
// movement
Vector3 force = Vector3.zero;
force += player.forward * Input.GetAxis("Vertical") * flyAcc;
force += player.right * Input.GetAxis("Horizontal") * flyAcc;
if (Input.GetKey(KeyCode.Space))
force += player.up * flyAcc;
if (Input.GetKey(KeyCode.LeftControl))
force += -player.up * flyAcc;
rb.AddForce(force);
if (inertiaDamper)
{
Quaternion q = Quaternion.identity;
Vector3 vel = rb.velocity;
float f = 0f;
if (force.sqrMagnitude > 0.01f)
{
q = Quaternion.LookRotation(force);
vel = Quaternion.Inverse(q) * rb.velocity;
f = vel.z;
vel.z = 0;
}
float acc = Mathf.Clamp(vel.magnitude*0.5f, 1f, 10f)*Mathf.Clamp(vel.magnitude, 0.1f, 1f) * flyAcc;
vel = Vector3.MoveTowards(vel, Vector3.zero, acc * Time.deltaTime);
if (force.sqrMagnitude > 0.01f)
vel.z = f;
rb.velocity = q * vel;
}
}
void GravityWellBehaviour()
{
// player gravity align
player.rotation = Quaternion.RotateTowards(player.rotation, Quaternion.FromToRotation(player.up, -Physics.gravity) * player.rotation, alignSpeed * Time.deltaTime);
// rotation
player.rotation = Quaternion.AngleAxis(Input.GetAxis("Mouse X") * mouseSensitivity, player.up) * player.rotation;
camT.localRotation = Quaternion.AngleAxis(-Input.GetAxis("Mouse Y") * mouseSensitivity, Vector3.right) * camT.localRotation;
// limit vertical view
float angle = Vector3.SignedAngle(camT.forward, player.forward, -player.right);
if (angle > maxAngle)
camT.localRotation = Quaternion.AngleAxis(maxAngle, Vector3.right);
if (angle < -maxAngle)
camT.localRotation = Quaternion.AngleAxis(-maxAngle, Vector3.right);
onGround = false;
// movement
if (Physics.SphereCast(player.position, 0.5f,Physics.gravity, out var _,0.7f))
{
onGround = true;
Vector3 localVelocity = player.InverseTransformVector(rb.velocity);
localVelocity.x = Input.GetAxis("Horizontal") * moveSpeed;
localVelocity.z = Input.GetAxis("Vertical") * moveSpeed;
if (Input.GetKeyDown(KeyCode.Space))
localVelocity.y = 5f;
rb.velocity = player.TransformVector(localVelocity);
}
}
private void OnGUI()
{
GUILayout.BeginVertical("box");
GUILayout.Label("grav: " + Physics.gravity);
GUILayout.Label("vel: " + rb.velocity.magnitude.ToString("F4"));
GUI.color = useJetpack ? Color.green : Color.red;
GUILayout.Label("Jetpack(X):" + useJetpack);
GUI.color = inertiaDamper ? Color.green : Color.red;
GUILayout.Label("Inertia(Z):" + inertiaDamper);
GUILayout.EndVertical();
}
}
GravityWell.cs
using System.Collections.Generic;
using UnityEngine;
public class GravityWell : MonoBehaviour
{
public static List<GravityWell> gravityWells = new List<GravityWell>();
public enum GravityType { Linear, Spherical }
public GravityType type;
public Vector3 min;
public Vector3 max;
public float radius;
public float gravity = 9.81f;
private void Awake()
{
gravityWells.Add(this);
}
private void OnDestroy()
{
gravityWells.Remove(this);
}
public Vector3 GetGravity(Vector3 aPos)
{
if (type == GravityType.Linear)
{
Bounds b = new Bounds((min + max) * 0.5f, max - min);
if (b.Contains(transform.InverseTransformPoint(aPos)))
return -transform.up * gravity;
}
else if (type == GravityType.Spherical)
{
Vector3 d = aPos - transform.position;
if (d.magnitude < radius)
return -d.normalized * gravity;
}
return Vector3.zero;
}
public static Vector3 GetClosestGravity(Vector3 aPos)
{
float gravity = 0;
Vector3 vec = Vector3.zero;
foreach(var well in gravityWells)
{
Vector3 v = well.GetGravity(aPos);
float g = v.magnitude;
if (g > gravity)
{
gravity = g;
vec = v;
}
}
return vec;
}
private void OnDrawGizmos()
{
if (type == GravityType.Linear)
{
Gizmos.matrix = transform.localToWorldMatrix;
Gizmos.DrawWireCube((min + max) * 0.5f, max - min);
}
else if (type == GravityType.Spherical)
{
Gizmos.matrix = Matrix4x4.identity;
Gizmos.DrawWireSphere(transform.position,radius);
}
}
}
Of course I created a webGL build if you want to try it out. The controls are inspired by SE. So press X to toggle the jetpack, space to jump / move up, CTRL to move down. Press “Z” to toggle the inertia dampers when in jetpack mode. Just for giggles I creates a spherical gravity well as well as some linear gravity fields. They have an effective area, when you leave it there will be no gravity anymore. For fun I managed to speed up with my jetpack around the “planet” and switched off the jetpack and I got into an elliptic orbit
After 30 minutes running in the background I was still orbiting.
Anyways I implemented two different behaviour. The jetpack behaviour will have the camera fixed to the player as you will rotate the player itself freely in space and the camera will just move with the player. In “gravity mode” without the jetpack, you get the usual “down” aligned character controller. So the player only rotates around the y axis which is automatically aligned with the gravity vector. When you look up and down we just rotate the camera inside the player on the x axis within reasonable limits. You can set them to ±90°, I have it at 80°. Greater angles don’t make any sense as you would bend over backwards which would of course somewhat invert the yaw rotaion since you’re looking backwards upside down.
In jetpack mode you can use Q and E to roll. I did launch Space Engineers to make some tests and measurements. The inertia dampers for ships used to be stronger than manual braking, but it seems they fixed it and made it more realistically. Though the jetpack inertia dampers are still up to almost 10 times stronger than manual decelleration ^^. It was quite hard to replicate the behaviour and it’s still not right, but works somewhat. In WebGL everything is a bit off anyways. I had to cut down the mouse sensitivity by a factor of 5, but that’s a known issue. I couldn’t be bothered to create a settings menu ^^. Don’t forget to click once to capture the mouse.
Though the main point was to show the free rotation in action. As I explained earlier, in actual 6 degree of freedom movement, when we just rotate around 2 axis we implicitly rotate around the the third. That’s why we actually need roll controls in the first place. The behaviour is very similar to Space Engineers.
ps: I’m in firefox and I have to be careful with CTRL+W as it closes the current tab 