Unable to set a rotation of the z axis while keeping the rotation of the Y axis

        move = Input.GetAxis("Vertical");
        turn = Input.GetAxisRaw("Horizontal");
        rotation = transform.rotation.y;

        transform.Rotate(new Vector3(0f, 1f, 0f) * Time.deltaTime * speed * move * -1);
        if (turn == 1)
        {
            transform.rotation = Quaternion.Euler(90f, rotation, -turnForce);
        }
        else if (turn == -1)
        {
            transform.rotation = Quaternion.Euler(90f, rotation, turnForce);
        }

I have been trying to make this code work for a while now but yet it keeps having the issue that whenever I set the rotation of the Z axis the Y axis is forced back to zero. Please could I get some help with this?

The y component of a quaternion rotation isn’t what you think it is. You probably want to get the y component of its euler angles: Unity - Scripting API: Quaternion.eulerAngles

Though that might cause odd behaviour as euler angles aren’t a reliable method of rotation. If you want to rotate on an axis, use Quaternion.AngleAxis to generate said rotation, then combine it with an existing operation by multiplying them together (* operator).

For example:

public class TestMonoBehaviour : MonoBehaviour
{
	public float speed = 50;

	public float turnForce = 50;

	private void Update()
	{
		float move = Input.GetAxis("Vertical");
		float turn = Input.GetAxisRaw("Horizontal");

		Quaternion hozRotation = Quaternion.AngleAxis(move * speed * Time.deltaTime, Vector3.up);
		Quaternion vertRotation = Quaternion.AngleAxis(turn * turnForce * Time.deltaTime, Vector3.right);

		Quaternion currentRotation = transform.rotation;
		Quaternion finalRotation = currentRotation * hozRotation * vertRotation;
		transform.rotation = finalRotation;
	}
}

The above will rotate them on their local axis. For global axis, honestly just use two rotate calls:

public class TestMonoBehaviour : MonoBehaviour
{
	public float speed = 50;

	public float turnForce = 50;

	private void Update()
	{
		float move = Input.GetAxis("Vertical");
		float turn = Input.GetAxisRaw("Horizontal");

		transform.Rotate(Vector3.up, move * speed * Time.deltaTime, relativeTo: Space.World);
		transform.Rotate(Vector3.right, turn * turnForce * Time.deltaTime, relativeTo: Space.World);
	}
}

In addition to what Spiney said, here’s what you specifically did wrong and why it didn’t work.

To make things more clear, allow me to replace every transform.Rotate with MoveBy, and every transform.rotation with position, think of this substitution as an analogy. This is what your code looks like

transform.MoveBy(...);
if (turn == 1)
        {
            transform.position = ...;
        }
        else if (turn == -1)
        {
            transform.position = ...;
        }

In other words, you directly overwrite whatever MoveBy already did in the first line.

So, logically you want to add to a position, in this analogy. But how do you add rotations, you ask? Like Spiney said above, we use * operator between two quaternions. In case of a position + would work just fine, however quaternions cannot be just added component-wise, they are applied instead (which is a fairly convoluted matrix multiplication).

And so in the above analogy this would suffice

transform.position += positionOffset;
// offset existing position by positionOffset

However in your code, you’d want to do this instead

transform.rotation = transform.rotation * nextRotation;
// apply nextRotation to an existing one (effectively rotates the stored orientation)

Or, as Spiney suggested, call transform.Rotate again.

1 Like