Camera relative movement is pushing into/off the ground instead of parallel

I'm building upon roll a ball tutorial in unity, and I have managed to rotate the camera around the ball just fine with the code below. But there's one problem.

In this code, when I add force to the ball, with W for example, the force will be added in the exact direction the camera is looking at the ball, meaning pushing the ball onto the ground, increasing friction and lowering speed. And if I press S the force will push the ball to the air, lowering friction and making the ball move fast.

So my question is, how can I achieve rotation without this issue?

Code for Camera (CameraController):

public GameObject Player;
private Vector3 offset;
public float cameraSpeed;
private Vector3 point;
static public Vector3 finalMovement;

void Start () {
offset = transform.position - Player.transform.position;

point = Player.transform.position;
transform.LookAt(point);
}

void LateUpdate()
{
point = Player.transform.position;

offset = Quaternion.AngleAxis(Input.GetAxis("Mouse X") * cameraSpeed, Vector3.up) * offset;
transform.position = point + offset;
transform.LookAt(point);
}

Vector3 movement;
void FixedUpdate()
{
movement = new Vector3();
float moveHorizontal = Input.GetAxis("Horizontal");
float moveVertical = Input.GetAxis("Vertical");
movement = new Vector3(moveHorizontal, 0f, moveVertical);

finalMovement = GetComponent<Camera>().transform.TransformDirection(movement);
}

Code for ball:

public float speed;
void FixedUpdate()
{
}
• Unrelated to your question; your variable scoping seems off- I seems you're declaring private class level variables that actually should be method level (local) variables. Either point is at method level or point is a property Vector3 point { get: return Player.transform.position; } similar movement should be declared inside the FixedUpdate() method. Jul 22 '16 at 11:24
• Thanks for your input, but the thing is that i'm really new to programming in general, and declaring variables as private was just testing. now I cant follow you on "{ get: return Player.transform.position; }" and i'm really intrigued to know. could you elaborate? Jul 22 '16 at 12:02
• There are a few other problems here. You're calculating movement in the camera's FixedUpdate, which could run after your ball uses it in FixedUpdate, introducing a frame of input latency. If the ball needs this info, why not calculate it in its script instead, so you don't have any execution order complications? Also, GetComponent<Camera>().transform is equivalent to transform alone if this script is on your camera, and throws a NullReferenceException otherwise, so probably better to use the simpler version. As for what Felsir mentioned, it's a C# Property. Jul 22 '16 at 12:42

I often like to give myself a little convenience method:

Vector3 CameraRelativeFlatten(Vector3 input, Vector3 localUp)
{
// If this script is on your camera object, you can use this.transform instead.
Transform cam = Camera.main.transform;

// The first part creates a rotation looking into the ground, with
// "up" matching the camera's look direction as closely as it can.
// The second part rotates this 90 degrees, so "forward" input matches
// the camera's look direction as closely as it can in the horizontal plane.
Quaternion flatten = Quaternion.LookRotation(
-localUp,
cam.forward
)
* Quaternion.Euler(Vector3.right * -90f);

// Now we rotate our input vector into this frame of reference
return flatten * input;
}

When I have an input in controller space, I can use this convert it to a direction in the world horizontal plane, relative to the camera:

Vector3 input = new Vector3(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical"));

Vector3 worldSpaceInput = CameraRelativeFlatten(input, Vector3.up);

If we wanted, we could also use a local surface normal instead of Vector3.up to get our input parallel to an arbitrary tilted surface, instead of always in the XZ plane.

Since we're rotating the input vector, the length is preserved, so we won't just chop off the portion of a vector that was digging into the surface, slowing our forward-back movement relative to left-right.

• It took me longer that it should've to understand and use it, but i finally managed to use it. and it worked very well. thanks. sorry both of you for not upvoting, im currently low on rep. Jul 22 '16 at 17:58
• No worries, I've got enough votes/rep for my present purposes. I'm glad it's working for you! :) Jul 23 '16 at 2:34

So the problem you're having is that the Y movement is also used when applying the force. Easiest solution is to simply set the Y movement to 0 when adding the force: