So, as some others have noticed, the bugfix in this lecture introduces another bug–namely that it ends up turning off all your rotation constraints when you only want to release Z.
Others have also posted some fixes to this, but I found that, at least in my version of Unity, they weren’t quite working–I still ended up with all my constraints unchecking themselves after the first time I turned the ship.
My solution to this is a bit of a hack, but I thought I’d share it in case anyone else finds it useful (or can see some gosh-awful problems with it that I should fix before going any further).
The first thing I did was just delete the original fix out of my code and check all the “Freeze Rotation” boxes in Unity under Rigidbody, and simply leave Z locked along with X and Y. This fixed the bug, but introduced the undesirable behavior that the ship doesn’t come back to a ‘neutral’ position when no force is applied. So I figured… let’s just apply a counter-rotation using the same method we’re using to rotate the ship for steering, only backward. The amount of this counter-rotation is controlled by a serialized field called “Recentering,” (it works the same way as the fields that let us adjust the Thrust and Rotation), so you can fiddle with the force and get it to feel good (an initial value around 10 worked for me, but you can set it extra-high at first just to make sure it’s doing something).
Basically this is all just a reuse of the steering code we’re already using, but with some fiddling.
What I’ve done is add a Boolean called “isThrusting” and set it to true if the player is using any of the controls, then reset it to false if no control key is active. I then put all this into a function called RecenterRotation() and called it during Update().
Basically, if you’re not applying any controls and your Z rotation transform is greater or less than zero, the ship automatically “steers” forward or back to counter the rotation and imitate the force of gravity pulling the heavier back-end down and nudging your ship toward a neutral “pointing up” position. With a little tuning to suit your preferences and match up with whatever values you chose for drag, etc., this imitates the behavior the ship’s rotation exhibits with gravity, but without having to rely on locking and unlocking Z every time you touch the controls.
I’ve included my code below, in case anyone else wants to use (or correct) it. Just in case there’s confusion about the differences from the lecture, please note that I use the arrow keys instead of WASD, and I renamed “Main Thrust” and “Steering Thrust” to simply “Thrust” and “Steering” because it looks cleaner. You can adjust that and the names of the variables and whatnot however you like, of course.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Movement : MonoBehaviour
{
Rigidbody rb;
[SerializeField] float Thrust = 1f;
[SerializeField] float Steering = 1f;
[SerializeField] float Recentering = 1;
bool isThrusting = false;
void Start()
{
rb = GetComponent<Rigidbody>();
}
void Update()
{
ProcessThrust();
ProcessRotation();
RecenterRotation();
}
void ProcessThrust()
{
if (Input.GetKey(KeyCode.UpArrow))
{
isThrusting = true;
rb.AddRelativeForce(Vector3.up * Thrust * Time.deltaTime);
}
}
void ProcessRotation()
{
if (Input.GetKey(KeyCode.LeftArrow))
{
isThrusting = true;
ApplyRotation(Steering);
}
else if (Input.GetKey(KeyCode.RightArrow))
{
isThrusting = true;
ApplyRotation(Steering * -1);
}
else isThrusting = false;
}
void RecenterRotation()
{
if (!isThrusting && gameObject.transform.rotation.z > 0)
{
transform.Rotate(Vector3.back * Recentering * Time.deltaTime);
}
else if (!isThrusting && gameObject.transform.rotation.z < 0)
{
transform.Rotate(Vector3.forward * Recentering * Time.deltaTime);
}
}
void ApplyRotation(float rotationThisFrame)
{
transform.Rotate(Vector3.forward * rotationThisFrame * Time.deltaTime);
}
}