Unity: Add strafing to Player Controller - c#

For a few weeks, I've been trying to add strafing to my Player. I now have a working Script but when I try using the Mouse to Rotate the Player and the Camera the script is reacting wierd. When the Player is facing towards the Z-direction the Player is walking and running normaly, but as soon as I turn him around he slows down and moves super slow. Here's the code:
void Update () {
// input
Vector2 input = new Vector2 (Input.GetAxisRaw ("Horizontal"), Input.GetAxisRaw ("Vertical"));
Vector2 inputDir = input.normalized;
bool running = Input.GetKey (KeyCode.LeftShift);
if (!Input.GetMouseButtonDown(0) || Input.GetKey(KeyCode.JoystickButton2)) {
Move (inputDir, running);
Time.timeScale = 1;
}
if (Input.GetKeyDown (KeyCode.Space) || Input.GetKeyDown (KeyCode.JoystickButton0)) {
Jump ();
}
// animator
float animationSpeedPercent = ((running) ? currentSpeed.magnitude / runSpeed : currentSpeed.magnitude / walkSpeed * .5f);
void Move(Vector2 inputDir, bool running) {
float targetRotation = cameraT.eulerAngles.y;
transform.eulerAngles = Vector3.up * Mathf.SmoothDampAngle(transform.eulerAngles.y, targetRotation, ref turnSmoothVelocity, GetModifiedSmoothTime(turnSmoothTime));
Vector2 targetSpeed = new Vector2(
((running) ? runSpeed : walkSpeed) * inputDir.normalized.x,
((running) ? runSpeed : walkSpeed) * inputDir.normalized.y);
currentSpeed = Vector2.SmoothDamp(currentSpeed, targetSpeed,
ref speedSmoothVelocity,
GetModifiedSmoothTime(speedSmoothTime));
velocityY += Time.deltaTime * gravity;
Vector3 velocity = (transform.forward * currentSpeed.y) +
(transform.right * currentSpeed.x) +
Vector3.up * velocityY;
controller.Move(velocity * Time.deltaTime);
currentSpeed = new Vector2(controller.velocity.x, controller.velocity.z);
if (controller.isGrounded)
{
velocityY = 0;
}
}

In order to allow a player to strafe in the default PlayerController, a couple edits must be made. I won't write the code for you, but here are the steps you need to take, they should be relatively easy to implement:
First, you need to comment out the code related to rotation
Then rather than obtaining the target speed as a scalar float, you should either calculate it as a Vector2 or as 2 separate floats (preferably the former)
Vector2 targetSpeed = new Vector2(
((running) ? runSpeed : walkSpeed) * inputDir.normalized.x,
((running) ? runSpeed : walkSpeed) * inputDir.normalized.y);
currentSpeed is a float, that relies on targetSpeed, but rather than~~ adjusting currentSpeed to also be a Vector2, you can use targetSpeed.magnitude when calculating the SmoothDamp
Since you need speed in both the Z context and the X context, you should make currentSpeed a Vector2 Thankfully there already exists a Vector2.SmoothDamp so that should be easy to refactor.
currentSpeed = Vector2.SmoothDamp(currentSpeed, targetSpeed,
ref speedSmoothVelocity,
GetModifiedSmoothTime(speedSmoothTime));
You need to include the X component in the velocity calculation. (remember that a Vector2's X and Y correspond to X and Z respectively on a Vector3)
Vector3 velocity = (transform.forward * currentSpeed.y) +
(transform.right * currentSpeed.x) +
Vector3.up * velocityY;
Finally, you want to adjust the currentSpeed to include the appropriate X, and Z velocities. This can be done simply by calculating the magnitude based on a Vector3 instead of a Vector2
currentSpeed = new Vector2(controller.velocity.x, controller.velocity.z);
By the way, welcome to StackOverflow - In the future you want to be sure to include a minimal, verifiable, and complete example, and not just the default unmodified code.
People are more willing to help if you've shown that you've made an effort yourself.

Related

Character controller get stuck

I am making a game with Unity. For some reason the character controller gets stuck.
Here is a video of that
This is the player controller code:
private CharacterController CharacterController;
public float Speed = 12;
private Vector3 Velocity;
private GameObject GroundCheck;
private bool IsGrounded;
public float CheckDistance = 0.4f;
public LayerMask GroundMask;
public float JumpHeight = 3;
public float Gravity = -9.81f;
private void Start()
{
GroundCheck = GameObject.Find("GroundCheck");
CharacterController = GetComponent<CharacterController>();
}
private void Update()
{
IsGrounded = Physics.CheckSphere(GroundCheck.transform.position, CheckDistance, GroundMask);
float X = Input.GetAxis("Horizontal");
float Y = Input.GetAxis("Jump");
float Z = Input.GetAxis("Vertical");
Vector3 Move = transform.right * X + transform.forward * Z;
CharacterController.Move(Move * Speed * Time.deltaTime);
if (Y == 1 && IsGrounded)
{
Velocity.y = Mathf.Sqrt(JumpHeight * -2 * Gravity);
}
if (IsGrounded && Velocity.y < 0)
{
Velocity.y = -2;
}
Velocity.y += Gravity * Time.deltaTime;
CharacterController.Move(Velocity * Time.deltaTime);
}
Happened to me too, judging by the code we watched the same youtube tutorial. The problem here is that you (and that tutorial guy) are making two calls to the Move function inside the Update function, this can cause the jitter you show on the video among other unexpected behaviors. You should only call the Move function once in the Update function and everything will be fine. So you need to combine X, Y, and Z movement in a single Vector3:
private void Update()
{
IsGrounded = Physics.CheckSphere(GroundCheck.transform.position, CheckDistance, GroundMask);
float X = Input.GetAxis("Horizontal");
float Y = Input.GetAxis("Jump");
float Z = Input.GetAxis("Vertical");
if (Y == 1 && IsGrounded)
{
Velocity.y = Mathf.Sqrt(JumpHeight * -2 * Gravity);
}
if (IsGrounded && Velocity.y < 0)
{
Velocity.y = -2;
}
Vector3 Move = transform.right * X * speed
+ transform.forward * Z * speed
+ transform.up * Velocity.y;
CharacterController.Move(Move * Time.deltaTime);
}
Note that now we are multiplying by the speed before, just the X and Z components, because we don't want the vertical velocity affected by the movement speed.
Although it should work now, there are a few details worth noting:
· You should name your variables in lower camel case: "move", not "Move".
· You don't really need a Vector3 for the vertical velocity, it could be just a float, since you are just using it for one axis. Note that you are reading Velocity.y, but never Velocity.x or Velocity.z. AFAIK this is because you copied the script from the mentioned Youtube tutorial, happened to me too until I realized.
· I don't really know why you are using "float Y = Input.GetAxis("Jump");" since the Jump key should not be an axis, if you run into problems try this instead:
private void Update()
{
IsGrounded = Physics.CheckSphere(GroundCheck.transform.position, CheckDistance, GroundMask);
if (IsGrounded && Velocity.y < 0)
{
Velocity.y = -2f;
}
float X = Input.GetAxis("Horizontal");
float Z = Input.GetAxis("Vertical");
if (Input.GetButtonDown("Jump") && IsGrounded)
{
Velocity.y = Mathf.Sqrt(JumpHeight * -2f * Gravity);
}
Vector3 Move = transform.right * X * speed
+ transform.forward * Z * speed
+ transform.up * Velocity.y;
CharacterController.Move(Move * Time.deltaTime);
}
Does it get stuck as in "It vibrates near the cube trying to climb up" or in "I can't move the character as soon as it touches the cube"?
My guess would be that your character tries to climb up the cube. The cube is approximately 2 meters high (at least if I can correctly get that from the video), your step offset is only 1, though.
Another thing to consider would be your character's skin width. From the Unity Manual:
It’s good practice to keep your Skin Width at least greater than 0.01 and more than 10% of the Radius.
But yours seems to have a good value.
If nothing helps, I would recommend actually going back to a CharacterController with a size of 2 meters. Citing the Unity Manual again:
You can modify the Height and Radius to fit your Character’s mesh
. It is recommended to always use around 2 meters for a human-like character.
One more thing: Start naming your variables after the C#-conventions, which would be camelCase. For example, your Velocity would become velocity, your GroundMask would become groundMask etc.

Camera behaviour weird when rotating around player

I am going for a RuneScape-style camera that rotates around the player using WASD. Rotating horizontally works fine but when I mix the two (as in pitching up or down) the camera rotates around the player really awkwardly, the camera might invert or will sort of gimbal I guess.
Here's my code:
public float pitch;
public float zoomSpeed = 4f;
public float minZoom = 5f;
public float maxZoom = 15f;
public Transform target;
public Vector3 offset;
public float yawSpeed = 100f;
private float currentZoom = 10f;
private float currentYaw = 0f;
private float currentPitch = 0f;
void Update()
{
currentZoom -= Input.GetAxis("Mouse ScrollWheel") * zoomSpeed;
currentZoom = Mathf.Clamp(currentZoom, minZoom, maxZoom);
currentYaw -= Input.GetAxis("Horizontal") * yawSpeed * Time.deltaTime;
currentPitch -= Input.GetAxis("Vertical") * yawSpeed * Time.deltaTime;
Debug.Log("Yaw: " + currentYaw + " Pitch: " + currentPitch);
}
void LateUpdate()
{
transform.position = target.position - offset * currentZoom;
transform.LookAt(target.position + Vector3.up * pitch);
transform.RotateAround(target.position, Vector3.up, currentYaw);
transform.RotateAround(target.position, Vector3.forward, currentPitch);
}
Any help would be gladly appreciated!
It looks to me that you are using currentPitch, but rotating it around the forward axis? Which would create roll on the world foward axis?
If your up vector is always world up, then the yaw you have will work. But what you want to do is recalculate the right vector from your current location to your target after you apply the yaw.
void LateUpdate() {
transform.position = target.position - offset * currentZoom;
transform.LookAt(target.position + Vector3.up * pitch);
transform.RotateAround(target.position, Vector3.up, currentYaw);
transform.RotateAround(target.position, Vector3.Cross((target.position - transform.position).normalized, Vector3.up), currentPitch);
}

How to limit camera's vertical rotation Unity 3D

I would like to limit my vertical rotation of the camera so that it can't do a 360 spin. I have tried a lot of tutorials but nothing worked for me so i .
Please also check my code.
[RequireComponent(typeof(PlayerMoto))]
public class PlayerController: MonoBehaviour {
public float speed = 5, sensetivity;
private PlayerMoto motor;
public GameObject hands;
public camera cam;
float lookUpMax = .6 f;
void Start() {
motor = GetComponent < PlayerMoto > ();
cam = GetComponent < camera > ();
}
void Update() {
//cam.transform.localEulerAngles = new Vector3(cam.transform.localEulerAngles.x, 0, 0);
float _xmove = Input.GetAxis("Horizontal");
float _zmove = Input.GetAxis("Vertical");
Vector3 _moveHorizontal = transform.right * _xmove;
Vector3 _movVertical = transform.forward * _zmove;
Vector3 _velocity = (_moveHorizontal + _movVertical) * speed;
motor.Move(_velocity);
float _yRot = Input.GetAxis("Mouse X");
Vector3 _rotation = new Vector3(0 f, _yRot, 0 f) * sensetivity;
motor.Rotate(_rotation);
float _xRot = Input.GetAxis("Mouse Y");
Vector3 _cameraRotation = new Vector3(_xRot, 0 f, 0 f) * sensetivity;
motor.RotateCamera(_cameraRotation);
if (Input.GetKey(KeyCode.LeftShift) && Input.GetKey(KeyCode.W)) {
for (; speed <= 15; speed++) {
speed = 15;
}
} else {
speed = 10;
}
}
}
Thank you very much for your kind help. I really appreciate every single comment to try and help me in this amazing journey.
Try this out
private void Rotation()
{
x_axis += speed * Input.GetAxis("Mouse X"); // speed = 2f;
y_axis -= speed * Input.GetAxis("Mouse Y");
y_axis = Mathf.Clamp (y_axis, -45, 45); // limits vertical rotation
transform.eulerAngles = new Vector3(y_axis, x_axis, 0.0f);
}
If I understood your code, the part that moves the camera is:
float _xRot = Input.GetAxis("Mouse Y");
Vector3 _cameraRotation = new Vector3(_xRot, 0f, 0f) * sensetivity;
Then, your code calls this method on another class
motor.RotateCamera(_cameraRotation);
which probably (since the previous suggestion did not work) applies the rotation through
GameObject.Rotate(_cameraRotation);
In order to limit your camera’s rotation, we need to directly apply the rotation, and it cannot be clamped if the rotation is applied by adding a rotation to the existing rotation.
So, let’s assume you can skip that call and directly apply the rotation (I don’t know if there will be side effects), and let’s assume your cam variable is your camera.
If all these assumptions are correct, you can try:
float _xRot += Input.GetAxis("Mouse Y") * sensetivity;
xRot = Mathf.Clamp(_xRot, xMin, xMax);
cam.transform.rotation = Quaternion.Euler(_xRot, 0.0f, 0.0f);
Remember to comment out
//motor.RotateCamera(_cameraRotation);
You can declare
public float xMin;
public float xMax;
And experiment with values until you find your optimal ones.
I strongly suspect that the code I suggested will not solve all your problems, or it could add issues, because the actual transformations of both your player and the camera are applied by another script, that is not provided. In that case, you can provide us also that code, but I suggest you try writing your own code, that you can customize to your needs.
As for why clamping a rotation is not as easy as it seems, this post is interesting:
Rotations, Quaternions and Euler Angles

Unity3D - Third Person Player movement in diagonal direction is slower than vertical and horizontal movement?

I'm trying to create a character controller that allows the movement speed to be affected by how far the player is pressing the left stick. When I can get that much to work, the other issue I encounter is the players speed decreasing while moving diagonally.
Most of the information I've found online deal with the opposite issue where diagonal movement is faster (movement vector goes above 1). The solution there would be to clamp the magnitude so pressing the stick into a corner doesn't go above 1. I've tried clamping to see if that would work, however, I don't believe that's the solution to my problem.
The code here allows the player to move in relation to the camera and the speed is influenced by input vector.
Vector3 input = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
Vector3 inputDir = input.normalized;
void Move(Vector3 inputDir, Vector3 input)
{
running = Input.GetKey(KeyCode.Joystick1Button1); // B button on xbox one controller
// Rotation stuff
if (inputDir != Vector3.zero) {
float targetRotation = Mathf.Atan2(inputDir.x, inputDir.z) * Mathf.Rad2Deg + cameraTransform.eulerAngles.y;
transform.eulerAngles = Vector3.up * Mathf.SmoothDampAngle(transform.eulerAngles.y, targetRotation, ref turnSmoothVelocity, GetModifiedSmoothTime(speedSmoothTime));
}
// Pretty sure this is where things are breaking
float targetSpeed = ((running) ? runSpeed : walkSpeed) * inputDir.magnitude;
currentSpeed = Mathf.SmoothDamp(currentSpeed, targetSpeed, ref speedSmoothVelocity, GetModifiedSmoothTime(speedSmoothTime));
currentSpeed *= input.magnitude;
vel = transform.forward * currentSpeed + Vector3.up * velocityY;
currentSpeed = new Vector3(_controller.velocity.x, 0, _controller.velocity.z).magnitude;
}
void ExecuteMovement()
{
_controller.Move(vel * Time.deltaTime);
}

When I press the arrow key forward player goes to the left

So I made this script
public float walkSpeed = 2;
public float runSpeed = 6;
public float turnSmoothTime = 0.2f;
float turnSmoothVelocity;
public float speedSmoothTime = 0.1f;
float speedSmoothVelocity;
float currentSpeed;
Animator animator;
void Start () {
animator = GetComponent<Animator> ();
}
void Update () {
Vector2 input = new Vector2 (Input.GetAxisRaw ("Horizontal"), Input.GetAxisRaw ("Vertical"));
Vector2 inputDir = input.normalized;
if (inputDir != Vector2.zero) {
float targetRotation = Mathf.Atan2 (inputDir.x, inputDir.y) * Mathf.Rad2Deg;
transform.eulerAngles = Vector3.up * Mathf.SmoothDampAngle(transform.eulerAngles.y, targetRotation, ref turnSmoothVelocity, turnSmoothTime);
}
bool running = Input.GetKey (KeyCode.LeftShift);
float targetSpeed = ((running) ? runSpeed : walkSpeed) * inputDir.magnitude;
currentSpeed = Mathf.SmoothDamp (currentSpeed, targetSpeed, ref speedSmoothVelocity, speedSmoothTime);
transform.Translate (transform.forward * currentSpeed * Time.deltaTime, Space.World);
float animationSpeedPercent = ((running) ? 1 : .5f) * inputDir.magnitude;
animator.SetFloat ("speedPercent", animationSpeedPercent, speedSmoothTime, Time.deltaTime);
}
Now the problem is whenever I click forward with my keys or left right anyway the character is facing 90 degrees away from the way I am actually trying to go. I have tried rotating the character in unity and the 3d modeling program it was made in and still doesnt work. I have been trying to figure this problem out for awhile now please help. Here is an example video : https://streamable.com/u7i0m
Every time I click a key it goes in the opposite direction its supposed to.
Your Atan2 is backwards. It should be Mathf.Atan2(inputDir.y, inputDir.x).
Depending on how your player is set up, you may also need to offset by + or - 90deg.
Mathf.Atan2(inputDir.x, inputDir.y) * Mathf.Rad2Deg + 90;
Yeah, looks like your model face isn't the transform forwards direction. Either add the offset to your Code or create a Child object to hold the model and rotate that by the offset needed. If you created the model yourself you should fix it their and remember to always create models facing that direction.

Categories

Resources