using UnityEngine; using UnityEngine.InputSystem; public class ShipControls : MonoBehaviour, SpaceRaceInput.IShipActions { public Camera aim; public Timing timing; SpaceRaceInput input; InputAction lookAction; InputAction accelerateAction; InputAction decelerateAction; Rigidbody rigidBody; Vector3 initialPosition; Vector3 initialForce; Quaternion initialAim; Vector3 thrust; Vector3 strafe; float strafeMag; float accelerateMag; float decelerateMag; float stabliseMag; ForceMode forceMode; // Camera Controls float camPitch; float camYaw; float clampAngle = 90.0f; void Start() { Cursor.lockState = CursorLockMode.Locked; rigidBody = GetComponent(); // just stop the body from being affected by rotation for now // until the 3p camera is in to explain why rigidBody.freezeRotation = true; initialPosition = rigidBody.transform.position; initialAim = rigidBody.transform.rotation; initialForce = Vector3.forward; thrust = Vector3.zero; strafe = Vector3.zero; accelerateMag = 50.0f; decelerateMag = 100.0f; // do not ask how, there is no reason strafeMag = 25.0f; stabliseMag = 75.0f; camPitch = initialAim.eulerAngles.x; camYaw = initialAim.eulerAngles.y; forceMode = ForceMode.Acceleration; if (input == null) { input = new SpaceRaceInput(); input.ship.SetCallbacks(this); } input.ship.Enable(); lookAction = input.FindAction("look"); accelerateAction = input.FindAction("accelerate"); decelerateAction = input.FindAction("decelerate"); } void OnDestroy() { input.ship.RemoveCallbacks(this); } void Update() { // to minimise input lag we poll for the thrust vector every frame and set it thrust = getThrust(); // if (rigidBody.velocity.magnitude < 0.5f) rigidBody.velocity = Vector3.zero; // same with the camera, we always look around UpdateCamera(); } void FixedUpdate() { // when you hit the trigger... GO // needs to be done here in fixedUpdate because otherwise the input is checked on every frame // so they will have already hit the button before this tick and they actually start moving // aka ripoff bool shouldStartTimer = !timing.timerEnabled && (thrust != Vector3.zero || strafe != Vector3.zero); if (shouldStartTimer) timing.StartTimer(); // the ship being propelled forward rigidBody.AddForce(thrust, forceMode); // moving side to side rigidBody.AddRelativeForce(strafe, forceMode); // rotating towards the camera's orientation // not being done by a force because of strange behaviour // needs investigation float step = stabliseMag * Time.fixedDeltaTime; // stop the ship jiggling because of micro camera movement if (Quaternion.Angle(aim.transform.rotation, rigidBody.rotation) > 1f) rigidBody.rotation = Quaternion.RotateTowards(transform.rotation, aim.transform.rotation, step); } void UpdateCamera() { // there's some hokey scaling that went on with the old input // we were originally using // in the action mapping the mouse is scaled by 0.05 // delta *= 0.5f; // Account for scaling applied directly in Windows code by old input system. // delta *= 0.1f; // Account for sensitivity setting on old Mouse X and Y axes. // def needs to be configurable Vector2 lookDelta = lookAction.ReadValue(); camYaw += lookDelta.x; camPitch -= lookDelta.y; camPitch = Mathf.Clamp(camPitch, -clampAngle, clampAngle); aim.transform.eulerAngles = new Vector3(camPitch, camYaw, 0.0f); } public void OnStrafe(InputAction.CallbackContext ctx) { Vector2 str = ctx.ReadValue(); strafe = new Vector3(str.x, str.y, 0f) * strafeMag; } // if you are decelerating you can't accelerate as well // otherwise you can flip the ship around and // decelerate + accelerate against the velocity vector // aka cheating? Vector3 getThrust() { float accelerateInput = accelerateAction.ReadValue(); float decelerateInput = decelerateAction.ReadValue(); // decelerates the ship against the direction it is moving if (decelerateInput > 0f) { // the velocity is already in world space so we just invert it return rigidBody.velocity.normalized * -1f * decelerateMag * decelerateInput; } // thrust in the direction of the camera if (accelerateInput > 0f) // transform from the camera's forward vector into world space return aim.transform.TransformDirection(Vector3.forward) * accelerateMag * accelerateInput; return Vector3.zero; } public void OnQuit(InputAction.CallbackContext ctx) { Application.Quit(); } public void OnRespawn(InputAction.CallbackContext ctx) { Respawn(); } public void Respawn() { // IMPORTANT // stop the timer before we move the ship or anything // otherwise it might generate a checkpoint time // for the final sector timing.StopTimer(); thrust = new Vector3(0.0f, 0.0f, 0.0f); transform.position = initialPosition; rigidBody.transform.forward = initialForce; rigidBody.velocity = new Vector3(0f, 0f, 0f); rigidBody.angularVelocity = new Vector3(0f, 0f, 0f); rigidBody.transform.rotation = initialAim; aim.transform.rotation = initialAim; camPitch = initialAim.eulerAngles.x; camYaw = initialAim.eulerAngles.y; } void OnDrawGizmosSelected() { if (Application.isPlaying == false) return; Gizmos.color = Color.red; Vector3 right = aim.transform.TransformDirection(Vector3.right) * 10; Gizmos.DrawRay(transform.position, right); Gizmos.color = Color.green; Vector3 direction = aim.transform.TransformDirection(Vector3.up) * 10; Gizmos.DrawRay(transform.position, direction); Gizmos.color = Color.blue; Vector3 forward = aim.transform.TransformDirection(Vector3.forward) * 10; Gizmos.DrawRay(transform.position, forward); Gizmos.color = Color.yellow; Vector3 velocity = rigidBody.velocity; Gizmos.DrawRay(transform.position, velocity); Gizmos.color = Color.cyan; Gizmos.DrawRay(transform.position, thrust); } public float Speed() { return rigidBody.velocity.magnitude; } // We don't use these because we poll instead // the callbacks are only invoked when the value changes // aka if you hold accelerate it doesn't do what you expect public void OnAccelerate(InputAction.CallbackContext ctx) { } public void OnDecelerate(InputAction.CallbackContext ctx) { } public void OnLook(InputAction.CallbackContext ctx) { } }