Does anyone have an idea on how to get WASD movement based on a 2D XY plane oriented facing the camera direction? Z axis doesn’t matter…
Essentially, I’m having a little bit of difficulty getting the movement to work… W, A and D work as expected → I wish to move forward based upon the camera coordinate system, not the world coordinate system. I have clamped my camera so it may rotate between 0 - (almost)90 degrees overhead. When in a bird’s eye view, pressing S causes my player to jump as well as slightly move backward, but when around 0 degrees my player will not jump… I have found that this is due to the rotation of the camera. I do not wish to jump, so how can I implement S such that I simply move backward?
None of your commented out examples for S follow the same pattern that you’re using for all three other directions. Have you tried the following yet, which is simply a modification of that pattern used for the other three?
Yeah, This was the first thing that I tried. I was trying other code out to make it work. That results in s jumping because the camera is rotated “behind” the player.
Ah, I think I get it now. What I believe you essentially want to do then is to project the forward/back/left/right vectors of the camera onto the XY plane, and then renormalize them to be unit vectors before multiplying them by speed and frame duration. Vector3.ProjectOnPlane() will help out with this.
Perhaps try this:
Rigidbody rb = GetComponent<Rigidbody>();
// Determine the forward unit vector of the camera, in terms of the XY plane only.
Vector3 forward = Vector3.ProjectOnPlane(localPlayerCam.transform.forward, Vector3.back).normalized;
// Cross product gives a perpendicular vector to the other two, pointing toward a hypothetical viewer
// which is positioned such that rotating from the first to the second is clockwise according to that
// viewer's position (in Unity's system specifically; others are exactly the reverse).
Vector3 right = Vector3.Cross(Vector3.up, forward);
// At this point, forward and right should be purely restricted to the XY plane, and
// should both be unit vectors, prepared to be scaled by speed and frame duration for
// uniform movement in any direction.
if(Input.GetKey(KeyCode.W))
{
rb.MovePosition(rb.position + forward * speed * Time.deltaTime);
}
if(Input.GetKey(KeyCode.S))
{
rb.MovePosition(rb.position - forward * speed * Time.deltaTime);
}
if(Input.GetKey(KeyCode.D))
{
rb.MovePosition(rb.position + right * speed * Time.deltaTime);
}
if(Input.GetKey(KeyCode.A))
{
rb.MovePosition(rb.position - right * speed * Time.deltaTime);
}
Also note that this code will cause diagonal movement to be about 41% faster than non-diagonal movement, forcing optimal players to favor diagonal movement when possible. Most games reduce the per-axis speed by 1/sqrt(2) when moving diagonally to compensate for this.
Hey, this is super close… I wasn’t too sure how to get the project on plane method to work so I ended up using your cross product logic to try and make my own implementation…The only problem I get is if I’m directly overhead (i.e. 90 degrees on the x or z axis…) If I go further past this, the direction reverts. Is there an easy workaround to this?
Rigidbody rb = GetComponent<Rigidbody>();
//Vector3 forward = Vector3.ProjectOnPlane(localPlayerCam.transform.forward, Vector3.ri).normalized;
Vector3 right = Vector3.Cross(localPlayerCam.transform.forward, Vector3.down).normalized;
Vector3 forward = Vector3.Cross(-right, Vector3.down).normalized;
// Cross product gives a perpendicular vector to the other two, pointing toward a hypothetical viewer
// which is positioned such that rotating from the first to the second is clockwise according to that
// viewer's position (in Unity's system specifically; others are exactly the reverse).
// At this point, forward and right should be purely restricted to the XY plane, and
// should both be unit vectors, prepared to be scaled by speed and frame duration for
// uniform movement in any direction.
if (Input.GetKey(KeyCode.W))
{
rb.MovePosition(rb.position + forward * speed * Time.deltaTime);
}
if (Input.GetKey(KeyCode.S))
{
rb.MovePosition(rb.position - forward * speed * Time.deltaTime);
}
if (Input.GetKey(KeyCode.D))
{
rb.MovePosition(rb.position + right * speed * Time.deltaTime);
}
if (Input.GetKey(KeyCode.A))
{
rb.MovePosition(rb.position - right * speed * Time.deltaTime);
}
Ah, because the camera forward is directly downward, and the cross product has a length of 0. I sometimes do something that in your case would amount to checking if the dot product of localPlayerCam.transform.forward with Vector3.down is less than roughly 0.7 (sqrt(2)/2 to be exact). If so, use the camera forward like you already are. Otherwise, the camera is pointing more downward than forward, so I simply swap out localPlayerCam.transform.forward with localPlayerCam.transform.up in the cross product, since the camera’s up vector will be closer to a right angle with the down vector than the camera’s forward, and will still otherwise give the desired result. Linear algebra is fun.
Thank you so much! Now (with my quite limited understanding of linear algebra) I have a correct implementation for my movement system! It works excellently!
Here it is for anyone else looking to do a similar implementation:
Rigidbody rb = GetComponent<Rigidbody>();
Vector3 right;
Vector3 forward;
/**
* We are to compute two cross products. Simply using the camera transform's
* rotation will not work for my WASD movement system as the camera
* may exhibit vertical and horizontal effects (which with a
* previous implementation caused the player to jump in the air)
* */
//In this case, we're more horizontal than we are vertical, so we should use the forward transform to compute the "right" vector
if (Vector3.Dot(localPlayerCam.transform.forward, Vector3.down) < Mathf.Sqrt(2)/2)
{
right = Vector3.Cross(localPlayerCam.transform.forward, Vector3.down).normalized;
}
//If we are more vertical than horizontal, we should use the up transform instead to compute the "right" vector
else
{
right = Vector3.Cross(localPlayerCam.transform.up, Vector3.down).normalized;
}
forward = Vector3.Cross(-right, Vector3.down).normalized;
// At this point, "forward" and "right" are be purely restricted to the XY plane, and
// should both be unit vectors, prepared to be scaled by speed and frame duration for
// uniform movement in any direction
if (Input.GetKey(KeyCode.W))
{
rb.MovePosition(rb.position + forward * speed * Time.deltaTime);
}
if (Input.GetKey(KeyCode.S))
{
rb.MovePosition(rb.position - forward * speed * Time.deltaTime);
}
if (Input.GetKey(KeyCode.D))
{
rb.MovePosition(rb.position + right * speed * Time.deltaTime);
}
if (Input.GetKey(KeyCode.A))
{
rb.MovePosition(rb.position - right * speed * Time.deltaTime);
}
You can optimize performance of keystrokes and vector stuff in your example by using:
if (Input.anyKey) {
// Youre code here
}
For sure test!
In my opinion it’s better to optimize and test in the process while coding directly. If you start to enhance your code if you mostly done, you get side effects and get not rid them. Otherwise while you work on your code and get some strange effects then you know you code logic currently.