UNITY VERSION 2022.3.11f1 3D URP project
I am working on a simple player controller script and I am experiencing difficulty determining why the velocity is behaving in unintended ways.
Two Issues:
1 - When I rotate the player Rigidbody while moving forward - the velocity/direction snaps to the worlds x-axis.
The yellow line is Velocity The red line is acting forces (pushes based on key (W) input and direction with mouse input)
Video Demo : https://clipchamp.com/watch/kpePTVpR73s
Question - Why would a velocity snap to a world axis like that and how can I start to resolve?
2 - When I hit the space key to apply a impulse force in the y direction there is a force being applied in the z world axis direction that is pushing the Rigidbody and I have spend a lot of time commenting out other acting forces and looking through the UI to find what might be acting.
again
The yellow line is Velocity The red line is acting forces (pushes based on key (W) input and direction with mouse input)
Video Demo : https://clipchamp.com/watch/phAYs1v4ULU
Question - Why would the velocity be immediately angled to the z direction when the impulse is set world y and why is there a deflection at the end of the arc path as though the rigidbody is colliding against an invisible wall?
Asset structure
CAMERA Parent - CameraHolder - MoveCamera Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MoveCamera : MonoBehaviour
{
public Transform cameraPosition;
// Update is called once per frame
void Update()
{
transform.position = cameraPosition.position;
}
}
Child - PlayerCam (main camera) - PlayerCam script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerCam : MonoBehaviour
{
public float sensX;
public float sensY;
public Transform orientation;
float xRotation;
float yRotation;
// Start is called before the first frame update
void Start()
{
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
}
// Update is called once per frame
void Update()
{
// Get Mouse Input
float mouseX = Input.GetAxisRaw("Mouse X") * Time.deltaTime * sensX;
float mouseY = Input.GetAxisRaw("Mouse Y") * Time.deltaTime * sensY;
yRotation += mouseX;
xRotation -= mouseY;
xRotation = Mathf.Clamp(xRotation, -90f, 90f);
transform.rotation = Quaternion.Euler(xRotation, yRotation, 0);
orientation.rotation = Quaternion.Euler(0, yRotation, 0);
}
}
PLAYER Parent - Player - Rigidbody - Player Movement Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
[Header("Movement")]
public float moveSpeed;
float horizontalInput;
float verticalInput;
Vector3 moveDirection;
Rigidbody rb;
public float groundDrag;
public float jumpForce;
public float jumpCooldown;
public float airMultiplier;
bool readyToJump = true;
[Header("Keybinds")]
public KeyCode jumpKey = KeyCode.Space;
[Header("Ground Check")]
public float playerHeight;
public LayerMask whatIsGround;
public Transform orientation;
bool grounded;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody>();
rb.freezeRotation = true;
}
// Update is called once per frame
void Update()
{
Debug.DrawRay(rb.position, rb.velocity, Color.yellow);
Debug.DrawRay(rb.position, rb.GetAccumulatedForce(), Color.red);
// Ground Check
grounded = Physics.Raycast(transform.position, Vector3.down, playerHeight * 0.5f + 0.2f, whatIsGround);
MyInput();
SpeedControl();
// Handle drag
if (grounded)
rb.drag = groundDrag;
else
rb.drag = 0;
}
private void FixedUpdate()
{
MovePlayer();
}
private void MyInput()
{
horizontalInput = Input.GetAxis("Horizontal");
verticalInput = Input.GetAxis("Vertical");
if (Input.GetKey(jumpKey) && readyToJump && grounded)
{
readyToJump = false;
Jump();
Invoke(nameof(ResetJump), jumpCooldown);
}
}
private void MovePlayer()
{
// Calculate movement direction
moveDirection = orientation.forward * verticalInput + orientation.right * horizontalInput;
Debug.Log("Orientation --- " + orientation.position);
Debug.Log("Orientation --- " + orientation.forward);
Debug.DrawLine(rb.position, moveDirection, Color.green);
// On Ground
if (grounded)
rb.AddForce(moveDirection.normalized * moveSpeed * 10f, ForceMode.Force);
else if (!grounded)
rb.AddForce(moveDirection.normalized * moveSpeed * 10f * airMultiplier, ForceMode.Force);
}
private void SpeedControl()
{
Vector3 flatVelocity = new Vector3(rb.velocity.x, 0f, rb.velocity.y);
// Limit velocity if needed
if(flatVelocity.magnitude > moveSpeed)
{
Vector3 limitedVelocity = flatVelocity.normalized * moveSpeed;
rb.velocity = new Vector3(limitedVelocity.x, rb.velocity.y, limitedVelocity.z);
}
}
private void Jump()
{
// Reset Y Velocity
rb.velocity = new Vector3(rb.velocity.x, 0f, rb.velocity.z);
rb.AddForce(transform.up * jumpForce, ForceMode.Impulse);
}
private void ResetJump()
{
readyToJump = true;
}
}
Child - PlayerObj - Capsule Collider
Child - Orientation - just transform for orientation
Child - CameraPos - just transform for position
I have tried a variety of code commenting, logging and UI configurations to try and reason about how the forces are acting on the rigidbody.
I see in your SpeedControl()
method you have Vector3 flatVelocity = new Vector3(rb.velocity.x, 0f, rb.velocity.y);
.
Which will obviously map the RigidBody's velocity Y
component to its Z
component.
This would explain the snapping since Y
looks like it would be zero most of the time. So when you reach your speed limit, you will set the Z
component to 0 which will snap it along the X
axis.
This would also explain the jumping issue since in that case Y
velocity will not be 0 which means the mapped Y
velocity will be mapped to the Z
axis, pushing the character forward when jumping.
I think what you meant to say is:
Vector3 flatVelocity = new Vector3(rb.velocity.x, 0f, rb.velocity.z);