0

Demo GIF

I have a smooth 6DoF controller from following a tutorial (this is the tutorial) and tweaked it to be exactly what I want with a few exceptions.

I found myself wanting the ability to lock the horizon line by pressing a toggle button so while the player is freely rotating around the horizon stays level until the lock is disabled.

I'd also like to have a button that smoothly rotates the horizon to be level with the Ship object/camera but keep the yaw and pitch the same. I'm not sure how it would work but I want this button to simply smoothly upright the Ship when the button is pressed once and have the horizon lock be a separate optional feature.

I have no idea where to even start on the math/logic for these two features but I have the buttons and functions mapped already hoping I might just gleam someone's smarter code and just having it magically work eventually. I have a feeling there's a better way of going about this than guessing or trial and error haha.

The entire script for the Ship object is self-contained and I'll put it here along with some images and GIFs of various hopefully useful details.

As an aside, I'm sure there's a simple solution I'm overlooking but the ship does roll when colliding with other objects (spherical collision mesh). Is there a way to either disable it or have less friction?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;

[RequireComponent (typeof(Rigidbody))]

public class Ship : MonoBehaviour { // Parameters [Header("=== Ship Movement Settings ===")] [SerializeField] private float pitchTorque = 500f; [SerializeField] private float yawTorque = 1000f; [SerializeField] private float rollTorque = 1000f; [SerializeField] private float thrust = 100f; [SerializeField] private float strafeThrust = 50f; [SerializeField] private float verticalThrust = 50f; public bool horizonLock = false;

[Header("=== Boost Settings ===")]
[SerializeField]
private float boostMultiplier = 5f;

[Header("=== Thrust Reduction Settings ===")]
[SerializeField]
private float drag = 1f;
[SerializeField]
private float angularDrag = 0.5f;
[SerializeField, Range(0.001f, 0.999f)]
private float thrustGlideReduction = 0.9f;
[SerializeField, Range(0.001f, 0.999f)]
private float strafeGlideReduction = 0.1f;
[SerializeField, Range(0.001f, 0.999f)]
private float verticalGlideReduction = 0.1f;


// Variables
Rigidbody rb;
float glide = 0f;
float strafeGlide = 0f;
float verticalGlide = 0f;
bool boosting = false;
bool altMove = false;
bool altRoll = false;
bool stop = false;
bool autoUpright = false;

// Input Values
private float thrust1D;
private float strafe1D;
private float vertical1D;
private float roll1D;
private Vector2 pitchYaw;


// Start is called before the first frame update
void Start()
{
    rb = GetComponent<Rigidbody>();
}

// Update is called once per frame
void FixedUpdate()
{
    HandleMovement();
}


void HandleMovement()
{
    /* The reason for all the altRoll and altMove statements is to have alternate momentary
     * way of moving and rolling the ship.
     * 
     * AltMove shifts horizontal and forward/backward movement inputs to vertical/horizontal movement.
     * It also shifts vertical movement inputs to forward/backward movement.
     * 
     * AltRoll shifts the yaw/pitch movement inputs to roll/pitch.
     * It also maps roll inputs to yaw for convenience.
     */


    // Roll
    if(!altRoll) rb.AddRelativeTorque(Vector3.back * roll1D * rollTorque * Time.deltaTime);
    else rb.AddRelativeTorque(Vector3.back * Mathf.Clamp(pitchYaw.x, -1f, 1f) * rollTorque * Time.deltaTime);

    // Pitch
    rb.AddRelativeTorque(Vector3.right * Mathf.Clamp(-pitchYaw.y, -1f, 1f) * pitchTorque * Time.deltaTime);

    // Yaw
    if (!altRoll) rb.AddRelativeTorque(Vector3.up * Mathf.Clamp(pitchYaw.x, -1f, 1f) * yawTorque * Time.deltaTime);
    else rb.AddRelativeTorque(Vector3.up * roll1D * yawTorque * Time.deltaTime);


    // Thrust
    if (!altMove && (thrust1D > 0.1f || thrust1D < -0.1f))
    {
        float currentThrust;
        if (boosting) currentThrust = thrust * boostMultiplier;
        else currentThrust = thrust;

        rb.AddRelativeForce(Vector3.forward * thrust1D * currentThrust * Time.deltaTime);
        glide = thrust;
    }
    else if(altMove && (vertical1D > 0.1f || vertical1D < -0.1f))
    {
        float currentThrust;
        if (boosting) currentThrust = thrust * boostMultiplier;
        else currentThrust = thrust;

        rb.AddRelativeForce(Vector3.forward * vertical1D * currentThrust * Time.deltaTime);
        glide = thrust;
    }
    else
    {
        rb.AddRelativeForce(Vector3.forward * glide * Time.deltaTime);
        glide *= thrustGlideReduction;
    }


    // Strafe
    if (strafe1D > 0.1f || strafe1D < -0.1f)
    {
        float currentThrust;
        if (boosting) currentThrust = thrust * boostMultiplier;
        else currentThrust = thrust;

        rb.AddRelativeForce(Vector3.right * strafe1D * currentThrust * Time.deltaTime);
        strafeGlide = strafe1D * strafeThrust;
    }
    else
    {
        rb.AddRelativeForce(Vector3.right * strafeGlide * Time.deltaTime);
        strafeGlide *= strafeGlideReduction;
    }


    // Vertical
    if (!altMove && (vertical1D > 0.1f || vertical1D < -0.1f))
    {
        float currentThrust;
        if (boosting) currentThrust = thrust * boostMultiplier;
        else currentThrust = thrust;

        rb.AddRelativeForce(Vector3.up * vertical1D * currentThrust * Time.deltaTime);
        verticalGlide = vertical1D * verticalThrust;
    }
    else if (altMove && (thrust1D > 0.1f || thrust1D < -0.1f))
    {
        float currentThrust;
        if (boosting) currentThrust = thrust * boostMultiplier;
        else currentThrust = thrust;

        rb.AddRelativeForce(Vector3.up * thrust1D * currentThrust * Time.deltaTime);
        verticalGlide = thrust1D * verticalThrust;
    }
    else
    {
        rb.AddRelativeForce(Vector3.up * verticalGlide * Time.deltaTime);
        verticalGlide *= verticalGlideReduction;
    }

    // What I call the "stop on a dime" button. Momentarily increases drag to freeze the ship in place.
    if (stop)
    {
        rb.drag = 50;
        rb.angularDrag = 50;
    }
    else
    {
        rb.drag = drag;
        rb.angularDrag = angularDrag;
    }
}



// Input handlers
#region Input Methods

public void OnThrust(InputAction.CallbackContext context)
{
    thrust1D = context.ReadValue<float>();
}

public void OnStrafe(InputAction.CallbackContext context)
{
    strafe1D = context.ReadValue<float>();
}

public void OnVertical(InputAction.CallbackContext context)
{
    vertical1D = context.ReadValue<float>();
}

public void OnRoll(InputAction.CallbackContext context)
{
    roll1D = context.ReadValue<float>();
}

public void OnPitchYaw(InputAction.CallbackContext context)
{
    pitchYaw = context.ReadValue<Vector2>();
}

public void OnBoost(InputAction.CallbackContext context)
{
    boosting = context.performed;
}

public void OnAltMove(InputAction.CallbackContext context)
{
    altMove = context.performed;
}

public void OnAltRoll(InputAction.CallbackContext context)
{
    altRoll = context.performed;
}

public void OnStop(InputAction.CallbackContext context)
{
    stop = context.performed;
}

public void OnHorizonLock(InputAction.CallbackContext context)
{
    horizonLock = !horizonLock;
}

public void OnAutoUpright(InputAction.CallbackContext context)
{
    autoUpright = context.performed;
}

#endregion

}



Inspector Window for the Ship object.

Inspector window for ship

DMGregory
  • 134,153
  • 22
  • 242
  • 357
Mazz1224
  • 1
  • 2
  • I was hoping you'd still be around haha. I was looking at that exact post but was having a really hard time wrapping my head around it since I don't fully understand the code I've written so far to begin with. So I decided to write my own post specific to my project. – Mazz1224 Nov 20 '22 at 21:39
  • I'm a moderator here - you can click on any user's profile to see how recently they've visited the site. I'd be happy to help you with my older post, but I'd need to know where you're running into difficulty, specifically. – DMGregory Nov 20 '22 at 21:52
  • Thank you for taking the time to help me, I appreciate it! I'm having trouble applying the logic and math from your older post(s) to the code that I've already written. I also don't fully understand how Unity's rotation system works and which built-in variables and such do what since I don't have a lot of experience working in this software. Specifically I don't understand how I'd get a plane of rotation to be separate from the ship and somehow lock the rotation around it, not to mention being able to smoothly upright the ship to that plane any time a button is pressed. – Mazz1224 Nov 20 '22 at 22:05
  • I really just don't know the most efficient way of going about this. I'm not sure if I have to use another empty to track the separate plane of rotation or if it can all be contained in a single empty/object/script. – Mazz1224 Nov 20 '22 at 22:12
  • It sounds to me like you have multiple questions that should probably be posted separately. Can you create a new question showing an example of methods in Unity's rotation system that confuse you, or variables you need explained? Breaking down a big complex feature like "spaceship camera" into smaller steps like "rotate n degrees around a local axis" can be a big help in making progress. – DMGregory Nov 21 '22 at 02:17
  • Yes that's a very good point. I'll make a new question for each feature I'm aiming to implement. – Mazz1224 Nov 21 '22 at 03:01
  • Start slow - don't post a batch of questions all at once. You never know when the answer to one will give you insight that solves another, or helps you ask it in a better way. – DMGregory Nov 21 '22 at 03:42

0 Answers0