Movement Relative to both Camera and XZ Plane

I have simple movement set up with a sphere object using a Mouse Orbit style camera and Rigidbody.AddForce(). The main issue is that I want it to move in the direction the camera is facing but only receiving input force along the XZ plane, but when I add force when the camera is facing up or down, I fly up into the air. I understand what is causing this, but I am wondering if there is a way to calculate its XZ rotation in relation to the world space that doesn’t involve what I used as a workaround: I attached an empty gameobject as a child to the main camera that the camera movement script doesn’t send it the Y rotation.

This is my code prior to me implementing my workaround fix:

function FixedUpdate() 
{
	var cameraRight : Vector3 = Camera.main.transform.TransformDirection(Vector3.right);
	var cameraFront : Vector3 = Camera.main.transform.TransformDirection(Vector3.forward);	
	cameraRight.Normalize();
	cameraFront.Normalize();
	
	if(Input.GetAxis("Horizontal") > 0)
	{
		Debug.Log("Right");
		GetComponent(Rigidbody).AddForce(cameraRight * moveSpeed * Time.deltaTime);
	}
	
	if(Input.GetAxis("Horizontal") < 0)
	{
		Debug.Log("Left");
		GetComponent(Rigidbody).AddForce(-cameraRight * moveSpeed * Time.deltaTime);
	}
	
	if(Input.GetAxis("Vertical") > 0)
	{
		Debug.Log("Forward");
		GetComponent(Rigidbody).AddForce(cameraFront * moveSpeed * Time.deltaTime);
	}
	
	if(Input.GetAxis("Vertical") < 0)
	{
		Debug.Log("Back");
		GetComponent(Rigidbody).AddForce(-cameraFront * moveSpeed * Time.deltaTime);
	}
}

I clearly understand why this doesn’t work, but I am at a loss as to how to calculate a way to get 100% of the force to be applied forward and backward along the XZ plane whenever the camera’s Y rotation isn’t perfectly zero.

Any help is much appreciated. Thanks!

Probably the simplest thing is to zero out the y-axis on your reference vectors:

var cameraRight : Vector3 = Camera.main.transform.TransformDirection(Vector3.right);
var cameraFront : Vector3 = Camera.main.transform.TransformDirection(Vector3.forward);  
cameraRight.Normalize();
cameraFront.Normalize();

Change to:

var cameraRight : Vector3 = Camera.main.transform.TransformDirection(Vector3.right);
var cameraFront : Vector3 = Camera.main.transform.TransformDirection(Vector3.forward); 
cameraRight.y = 0;
cameraFront.y = 0;
cameraRight.Normalize();
cameraFront.Normalize();

There is probably a general solution involving cross products, but that’s a lot more work. :wink:

Your cameraFront and cameraRight variables are both of type Vector3. This means that they have values for all 3 axes. If you do not want to take in the Y-value for applying force, before you get to your if-statements, zero out the Y-value of both cameraFront and cameraRight e.g.

cameraFront.y = 0;
cameraRight.y = 0;

That might not work fully, I’m not too versed in Javascript; I am more used to C#, but it should enable you to work out the Javascript equivalent if it does not work :slight_smile:

Based on above suggestions, I have made a script for adding force based on another body’s transform values.
just sharing it here with comments and extra codes for some future souls help.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerMovements : MonoBehaviour
{
	//each step force
	public float step=80;

	//axis to move with
	private float _horizAxes, _vertAxis;

	//rigidbody of player to apply force
	private Rigidbody _rb;

	//direction transform for referral
	public Transform dirTrans;

	//to divide directional force across XZ plane
	private Vector3 forwardDir, rightDir;

	private void Start()
	{
		_rb = GetComponent<Rigidbody>();
	}

	private void Update()
	{
		//get axis horizontally and vertically
		_horizAxes = Input.GetAxis("Horizontal");
		_vertAxis = Input.GetAxis("Vertical");


		//Add Force only when Axes are not zero, as Zero means player is stationary
		if (!(_horizAxes == 0 && _vertAxis == 0))
		{
			/*
			 * Tried this way, but when camera looks with down angle, the player starts to fly for reverse
			 * planning to add new Vector3 references and force only across XZ plane
			if(_horizAxes > 0 ){
			_rb.AddForce(step * dirTrans.right * Time.deltaTime);
			}
			else if(_horizAxes < 0){
				_rb.AddForce(step * -dirTrans.right * Time.deltaTime);
			}
			if (_vertAxis > 0){
				_rb.AddForce(step * dirTrans.forward * Time.deltaTime);
			}
			else if (_vertAxis < 0){
				_rb.AddForce(step * -dirTrans.forward * Time.deltaTime);
			}
			*/

			forwardDir = dirTrans.forward;
			forwardDir.y = 0;
			rightDir = dirTrans.right;
			rightDir.y = 0;

			if (_horizAxes > 0)
			{
				_rb.AddForce(step * rightDir * Time.deltaTime);
			}
			else if (_horizAxes < 0)
			{
				_rb.AddForce(step * -rightDir * Time.deltaTime);
			}

			if (_vertAxis > 0)
			{
				_rb.AddForce(step * forwardDir * Time.deltaTime);
			}
			else if (_vertAxis < 0)
			{
				_rb.AddForce(step * -forwardDir * Time.deltaTime);
			}
		}
	}
}