- Home /
Problem with adjusting vector.
I've been fighting to get my controller to behave as expected.
What I want is to have two different speeds for sideways movement (X axis) and forward and backwards movement (Z Axis).
// Get keyboard inputs
float moveZ = Input.GetAxis("Vertical");
float moveX = Input.GetAxis("Horizontal");
//Handle Movement
Vector3 horizontalMovement = transform.right * moveX; // Set new vector based on the local coordinates
Vector3 verticalMovement = transform.forward * moveZ;
Vector3 movement = horizontalMovement + verticalMovement;
movement = movement.normalized; // Normalize final vector
// Apply speed to normalized vector
movement.x *= _horizontalMoveSpeed * Time.deltaTime;
movement.z *= _verticalMoveSpeed * Time.deltaTime;
// If the player is moving then rotate his body to face the same direction as the camera.
if (movement.magnitude > 0)
{
Vector3 playerRotation = new Vector3(0, _cameraFollow.transform.eulerAngles.y, 0);
transform.rotation = Quaternion.Euler(playerRotation);
}
_characterController.Move(movement);
The issue I'm having seems to originate with these 2 lines.
// Apply speed to normalized vector
movement.x *= _horizontalMoveSpeed * Time.deltaTime;
movement.z *= _verticalMoveSpeed * Time.deltaTime;
My character moves fine along the forward axis at the speed of verticalMoveSpeed. When i strafe left and right he moves slower at the horizontalMoveSpeed. The issue arises when I then rotate my camera 90, so now my "forward" is facing along the x axis (if you get what i mean). Now If i try to run my forward speed seems to be the horizontalMoveSpeed. If I now strafe I seem to be running with the verticalMoveSpeed.
I have no idea whats going on.
If I remove those lines 2 lines and just do _characterController.Move(movement * speed * Time.deltaTime); Then he moves fast in both axis (which isnt what i want)
Answer by darksider2000 · May 08, 2020 at 11:48 PM
Your issue occurs because you're modifying the X and Z components of a world oriented Vector, but you're looking to modify your character's local horizontal vector.
Try instead to do your modifications to horizontalMovement and verticalMovement before normalizing the vector. This will mean that the normalized vector still comes out with a magnitude of 1, so you'll need to apply speed to it. Its direction however will be aligned based on the moveSpeed values, and you'll end up with what you're looking for.
//Handle Movement
Vector3 horizontalMovement = transform.right * moveX * _horizontalMoveSpeed; // Set new vector based on the local coordinates
Vector3 verticalMovement = transform.forward * moveZ * _verticalMoveSpeed;
Vector3 movement = horizontalMovement + verticalMovement;
movement = movement.normalized; // Normalize final vector
Hope this helps!
Answer by spylefkaditis · May 09, 2020 at 08:32 AM
Thanks for the reply @darksider2000 . Hmmmm I don't think I understand your solution. It was my understanding when I normalise a vector I'm getting rid of its magnitude and just remain with the direction only. Which is why I then multiply it's x and y components by my 2 different speeds.
If I apply your suggested code as above, then the multiplier values _horizontalMoveSpeed and _verticalMoveSpeed are ignored after normalizing the vector. If I apply a speed to it then it works, but that doesn't actually give me the result I want of having 2 different speeds.
Sorry if I'm misunderstanding.
What I "think" is happening is that
movement.x = movement.x * _horizontalMoveSpeed * Time.deltaTime;
movement.z = movement.z * _verticalMoveSpeed * Time.deltaTime;
is saying "Modify the movement in regards to the world axis". So whenever I move in global Z it's one speed and global X another speed. That's why when my character rotates it doesn't change. But I don't understand why. I thought
Vector3 horizontalMovement = transform.right * moveX;
Vector3 verticalMovement = transform.forward * moveZ;
Vector3 movement = horizontalMovement + verticalMovement;
would create movement vector based on the object local coordinates so then calling _characterController.Move(movement) would move it based on those local coordinates.
Clearly i dont understand vectors
I misunderstood what you meant by two speeds. In that case just don't normalize the vector, ins$$anonymous$$d add both vectors together (While multiplying each by their respective moveSpeed), and then divide their product by the square root of 2. (This is because combining two vectors that are perpendicular to each other results in a magnitude bigger than you want)
// Get keyboard inputs
float moveZ = Input.GetAxis("Vertical");
float moveX = Input.GetAxis("Horizontal");
//Handle $$anonymous$$ovement
Vector3 horizontal$$anonymous$$ovement = transform.right * moveX * _horizontal$$anonymous$$oveSpeed; // This will be the smaller vector
Vector3 vertical$$anonymous$$ovement = transform.forward * moveZ * _vertical$$anonymous$$oveSpeed;
Vector3 movement = horizontal$$anonymous$$ovement + vertical$$anonymous$$ovement;
movement /= $$anonymous$$athf.Sqrt(2f); // Get real magnitude from perpendicular input
// If the player is moving then rotate his body to face the same direction as the camera.
if (movement.magnitude > 0)
{
Vector3 playerRotation = new Vector3(0, _cameraFollow.transform.eulerAngles.y, 0);
transform.rotation = Quaternion.Euler(playerRotation);
}
_characterController.$$anonymous$$ove(movement * Time.deltaTime);
I tried your suggestion and it fixed the initial problem I had. However now whenever I my character ran in a diagonal motion his speed was being affected in a weird way.
I've decided to take a different approach and calculate the dot product of my movement vector and the characters forward vector to find out the direction he is moving relative to his forward. That way I adjust my velocity value based on whether he's moving backwards, forwards sideways etc.
Here is my final solution.
using System;
using UnityEngine;
public class Player$$anonymous$$ovement : $$anonymous$$onoBehaviour
{
[SerializeField] private GameObject _cameraFollow;
[SerializeField] private float _moveSpeed = 6;
private CharacterController _characterController;
private Animator _anim;
private float _velocity;
private Vector3 _movement;
void Start()
{
_anim = GetComponent<Animator>();
_characterController = GetComponent<CharacterController>();
Cursor.lockState = CursorLock$$anonymous$$ode.Locked;
}
private void Update()
{
CalculatePlayer$$anonymous$$ovement();
CalculatePlayerRotation();
}
void LateUpdate()
{
$$anonymous$$ovePlayer();
}
void CalculatePlayer$$anonymous$$ovement()
{
// Get keyboard inputs
float moveZ = Input.GetAxis("Vertical");
float moveX = Input.GetAxis("Horizontal");
//Animate $$anonymous$$ovement
_anim.SetFloat("VelocityZ", moveZ);
_anim.SetFloat("VelocityX", moveX);
//Handle $$anonymous$$ovement
Vector3 horizontal$$anonymous$$ovement = transform.right * moveX; // Set new vector based on the local coordinates
Vector3 vertical$$anonymous$$ovement = transform.forward * moveZ;
_movement = horizontal$$anonymous$$ovement + vertical$$anonymous$$ovement;
float dotProduct = Vector3.Dot(transform.forward, _movement.normalized);
if (dotProduct >= 0.4)
{
_velocity = _moveSpeed;
}
else if (dotProduct < 0.4 && dotProduct > -0.2)
{
_velocity = _moveSpeed / 1.5f;
}
else if (dotProduct <= -0.2)
{
_velocity = _moveSpeed / 2;
}
}
void CalculatePlayerRotation()
{
// If the player is moving then rotate his body to face the same direction as the camera.
if (_movement.magnitude > 0)
{
Vector3 playerRotation = new Vector3(0, _cameraFollow.transform.eulerAngles.y, 0);
transform.rotation = Quaternion.Euler(playerRotation);
}
}
void $$anonymous$$ovePlayer()
{
_characterController.$$anonymous$$ove(_movement * _velocity * Time.deltaTime);
}
}
Well if it works don't fix it! Glad you found a solution
Your answer