3rd person controller spinning the wrong way around - c#

i've been trying to make a third person controller and its going ok. Right unltil i startet on the movement itself.
When the Y axis reaches anything above 360 it spins all the way from 360>300..150>50>5 instead of just 360>5 and i dont know how i could make it do what i want
Below is the two scripts that im using
Camera Rotation:
public bool LockMouse;
public Transform target;
public float Sens;
[Range(0,10)]
public float Distance = 2;
public Vector2 MinMaxPitch = new Vector2(-40, 85);
public float RotSmoothTime = 0.12f;
Vector3 rotationSmoothVelocity;
Vector3 currentRot;
float pitch;
float yaw;
private void Start()
{
if(LockMouse)
Cursor.lockState = CursorLockMode.Locked;
}
void LateUpdate()
{
yaw += Input.GetAxis("Mouse X") * Sens * Time.deltaTime;
pitch -= Input.GetAxis("Mouse Y") * Sens * Time.deltaTime;
pitch = Mathf.Clamp(pitch, MinMaxPitch.x, MinMaxPitch.y);
currentRot = Vector3.SmoothDamp(currentRot, new Vector3(pitch, yaw), ref rotationSmoothVelocity, RotSmoothTime);
transform.eulerAngles = currentRot;
transform.position = target.position - transform.forward * Distance;
}
Player Movement:
public float speed;
public float SmoothSpeed;
Transform camT;
Rigidbody rb;
Vector3 rot;
private Vector3 vel = new Vector3(0, 0, 0);
// Start is called before the first frame update
void Start()
{
camT = Camera.main.transform;
rb = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
rb.velocity = (transform.right * Input.GetAxis("Horizontal") * Time.deltaTime * speed) + (transform.forward * Input.GetAxis("Vertical") * Time.deltaTime * speed) + (transform.up * rb.velocity.y);
if(new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical")).magnitude > 0)
{
rot = new Vector3(0, camT.eulerAngles.y, 0);
Debug.Log(rot);
if(rot.y > 358)
{
transform.eulerAngles = new Vector3(rot.x, 3, rot.z);
}
if (rot.y < 2)
{
transform.eulerAngles = new Vector3(rot.x, 357, rot.z);
}
}
transform.eulerAngles = Vector3.SmoothDamp(transform.eulerAngles, rot, ref vel, SmoothSpeed);
}

Vector3.Smoothdamp doesn't understand that e.g., (x,359,z) can wrap around to (x,0,z).
Use Quaternions instead of vectors and use Quaternion.RotateTowards to calculate the rotation to use between the current rotation and rot:
public float speed;
public float smoothSpeed; // naming convention for fields is camelCase
Transform camT;
Rigidbody rb;
Quaternion rot;
private Vector3 vel = new Vector3(0, 0, 0);
// Start is called before the first frame update
void Start()
{
camT = Camera.main.transform;
rb = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
rb.velocity = (transform.right * Input.GetAxis("Horizontal") * Time.deltaTime * speed) + (transform.forward * Input.GetAxis("Vertical") * Time.deltaTime * speed) + (transform.up * rb.velocity.y);
if(new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical")).magnitude > 0)
{
rot = Quaternion.Euler(0f,camT.eulerAngles.y,0f);
}
transform.rotation = Quaternion.RotateTowards(transform.rotation, rot, Time.deltaTime * smoothSpeed);
}

Related

When look up my character moves forwards in unity

I am using this movement code with a box collider in unity (I am quite a beginner)
public class Movement : MonoBehaviour
{
public float speed = 10.0f;
void Update()
{
float horizontalInput = Input.GetAxis("Horizontal");
float verticalInput = Input.GetAxis("Vertical");
transform.Translate(new Vector3(horizontalInput, 0, verticalInput) * Time.deltaTime * speed);
And this mouselook code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Mouselook : MonoBehaviour
{
public float sensitivity = 100.0f;
public float clampAngle = 80.0f;
private float rotX = 0.0f;
private float rotY = 0.0f;
void Start()
{
Vector3 rot = transform.localRotation.eulerAngles;
rotX = rot.x;
rotY = rot.y;
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
}
void Update()
{
float mouseX = Input.GetAxis("Mouse X");
float mouseY = -Input.GetAxis("Mouse Y");
rotX += mouseY * sensitivity * Time.deltaTime;
rotY += mouseX * sensitivity * Time.deltaTime;
rotX = Mathf.Clamp(rotX, -clampAngle, clampAngle);
Quaternion localRotation = Quaternion.Euler(rotX, rotY, 0.0f);
transform.localRotation = localRotation;
}
}
I tried making a different movement code and I was expecting it to let me use wasd to move in a unity puzzle game.
The thing is
transform.Translate(new Vector3(horizontalInput, 0, verticalInput) * Time.deltaTime * speed);
uses local space and since your object is rotated and looking up it will start to move upwards as well.
You could eliminate that by using global space and erase any movement on Y by doing e.g.
var move = transform.rotation * new Vector3(horizontalInput, 0, verticalInput) * Time.deltaTime * speed;
move.y = 0f;
transform.position += move;

How do I make it so that the Rotation of the player object does not affect the Input.GetAxisRaw("Horizonatal")?

In my code, when I change the rotation of the player object, the Input.GetAxisRaw("Horizontal") also follows that rotation and messes the whole player controller up. How do I make it so that the Input.GetAxis is permanent and stays in place? The whole rotation is being done in the if statement of if Input.GetKey("a").
using UnityEngine;
public class JUSTMOVE : MonoBehaviour
{
public float speed = 7f;
Vector3 moveDirection;
public Rigidbody rb;
float horizontalMovement;
float verticalMovement;
public float movementMultiplier = 10f;
bool grounded;
public float groundDrag = 6;
public LayerMask whatIsGround;
public float airMultiplier = 3f;
public float playerHeight = 2f;
public float jumpForce = 5f;
void FixedUpdate()
{
grounded = Physics.Raycast(transform.position, Vector3.down, playerHeight * 0.5f + 0.2f, whatIsGround);
horizontalMovement = Input.GetAxisRaw("Horizontal");
if (grounded)
rb.drag = groundDrag;
else
rb.drag = 0;
moveDirection = transform.forward * verticalMovement + transform.right * horizontalMovement;
if(grounded)
{
rb.AddForce(moveDirection.normalized * speed * movementMultiplier, ForceMode.Force);
} else if(!grounded)
{
rb.AddForce(moveDirection.normalized * speed * movementMultiplier * airMultiplier, ForceMode.Force);
}
if(Input.GetKey("a"))
{
transform.localRotation = Quaternion.Euler(0,270,0);
}
Vector3 flatVel = new Vector3(rb.velocity.x, 0f,rb.velocity.z);
if(flatVel.magnitude > speed)
{
Vector3 limitedVel = flatVel.normalized * speed;
rb.velocity = new Vector3(limitedVel.x, rb.velocity.y, limitedVel.z);
}
if(Input.GetKey("w") && grounded)
{
rb.velocity = new Vector3(rb.velocity.x, 0f, rb.velocity.z);
rb.AddForce(transform.up * jumpForce, ForceMode.Impulse);
}
}
}
If I get it right, you want a top/down controller instead of a first/third person controller ?
If so, you need to move align the camera rotation instead of you character rotation.
Replace this :
moveDirection = transform.forward * verticalMovement + transform.right * horizontalMovement;
By this :
moveDirection = Camera.main.transform.forward * verticalMovement + Camera.main.transform.right * horizontalMovement;

Clamp a quaternion rotation in unity

I have this code but I have not been able to find a decent solution to clamp the X axis between 2 angle values. How would it be done in this case?
public class CameraController : MonoBehaviour {
public float cameraSmoothness = 5f;
private Quaternion targetGlobalRotation;
private Quaternion targetLocalRotation = Quaternion.Identity;
private void Start(){
targetGlobalRotation = transform.rotation;
}
private void Update(){
targetGlobalRotation *= Quaternion.Euler(
Vector3.up * Input.GetAxis("Mouse X"));
targetLocalRotation *= Quaternion.Euler(
Vector3.right * -Input.GetAxis("Mouse Y"));
transform.rotation = Quaternion.Lerp(
transform.rotation,
targetGlobalRotation * targetLocalRotation,
cameraSmoothness * Time.deltaTime);
}
}
For this you would rather use Euler angles and only convert them to Quaternion after applying the clamp!
Never directly touch individual components of a Quaternion unless you really know exactly what you are doing! A Quaternion has four components x, y, z and w and they all move in the range -1 to 1 so what you tried makes little sense ;)
It could be something like e.g.
// Adjust via Inspector
public float minXRotation = 10;
public float maxXRotation = 90;
// Also adjust via Inspector
public Vector2 sensitivity = Vector2.one;
private float targetYRotation;
private float targetXRotation;
private void Update()
{
targetYRotation += Input.GetAxis("Mouse X")) * sensitivity.x;
targetXRotation -= Input.GetAxis("Mouse Y")) * sensitivity.y;
targetXRotation = Mathf.Clamp(targetXRotation, minXRotation, maxXRotation);
var targetRotation = Quaternion.Euler(Vector3.up * targetYRotation) * Quaternion.Euler(Vector3.right * targetXRotation);
transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation, cameraSmoothness * Time.deltaTime);
}
You need to Clamp Mouse Y + transform.rotation.x like this
_mouseX += Input.GetAxis("Mouse X") * _horizontalSensitive;
_camera.transform.localRotation = Quaternion.Euler(0, _mouseX, 0);
_mouseY -= Input.GetAxis("Mouse Y") * _verticalSensitive;
_mouseY = Mathf.Clamp(_mouseY, -90, 90);
_camera.transform.localRotation = Quaternion.Euler(_mouseY, 0, 0);
PS: _camera represent any gameobject on your scene

Stop my first person character controller going through the wall in Unity using C#?

As seen in the video here: https://i.gyazo.com/ad45ef9e231fd2f9ec6d4cf76889aece.mp4
My code:
MouseLook.cs:
using UnityEngine;
using System.Collections;
public class MouseLook : MonoBehaviour
{
public enum RotationAxes { MouseXAndY = 0, MouseX = 1, MouseY = 2 }
public RotationAxes axes = RotationAxes.MouseXAndY;
public float sensitivityX = 3F;
public float sensitivityY = 3F;
public Camera playerCamera;
public float minimumX = -360F;
public float maximumX = 360F;
public float minimumY = -60F;
public float maximumY = 60F;
private float rotationX = 0F;
private float rotationY = 0F;
private Quaternion originalRotation;
void Update()
{
if (axes == RotationAxes.MouseXAndY)
{
rotationX += Input.GetAxis("Mouse X") * sensitivityX;
rotationY += Input.GetAxis("Mouse Y") * sensitivityY;
rotationX = ClampAngle(rotationX, minimumX, maximumX);
rotationY = ClampAngle(rotationY, minimumY, maximumY);
Quaternion xQuaternion = Quaternion.AngleAxis(rotationX, Vector3.up);
Quaternion yQuaternion = Quaternion.AngleAxis(rotationY, -Vector3.right);
transform.localRotation = originalRotation * xQuaternion * yQuaternion;
}
if (axes == RotationAxes.MouseX)
{
rotationX += Input.GetAxis("Mouse X") * sensitivityX;
rotationX = ClampAngle(rotationX, minimumX, maximumX);
Quaternion xQuaternion = Quaternion.AngleAxis(rotationX, Vector3.up);
transform.localRotation = originalRotation * xQuaternion;
}
if (axes == RotationAxes.MouseY || playerCamera != null)
{
rotationY += Input.GetAxis("Mouse Y") * sensitivityY;
rotationY = ClampAngle(rotationY, minimumY, maximumY);
Quaternion yQuaternion = Quaternion.AngleAxis(-rotationY, Vector3.right);
if (playerCamera != null)
{
playerCamera.transform.localRotation = originalRotation * yQuaternion;
}
else
{
transform.localRotation = originalRotation * yQuaternion;
}
}
}
void Start()
{
/*
if (gameObject.GetComponent<Rigidbody>())
{
gameObject.GetComponent<Rigidbody>().freezeRotation = true;
}
*/
originalRotation = transform.localRotation;
}
public static float ClampAngle(float angle, float min, float max)
{
if (angle < -360F)
{
angle += 360F;
}
if (angle > 360F)
{
angle -= 360F;
}
return Mathf.Clamp(angle, min, max);
}
}
FirstPersonController.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
using Cursor = UnityEngine.Cursor;
public class FirstPersonController : MonoBehaviour
{
private float speed = 5;
private float jumpPower = 4;
Rigidbody rb;
CapsuleCollider col;
public GameObject crossHair;
bool isActive;
float HorizontalInput;
float VerticalInput;
void Start()
{
Cursor.visible = false;
Cursor.lockState = CursorLockMode.Locked;
rb = GetComponent<Rigidbody>();
col = GetComponent<CapsuleCollider>();
crossHair = GameObject.FindWithTag("CrossHair");
}
void Update()
{
HorizontalInput = Input.GetAxisRaw("Horizontal");
VerticalInput = Input.GetAxisRaw("Vertical");
if (Input.GetKeyDown("escape"))
{
Cursor.lockState = CursorLockMode.None;
}
if (Input.GetButtonDown("Sprint"))
{
speed = 15;
}
if (Input.GetButtonUp("Sprint"))
{
speed = 5;
}
if (Input.GetKeyDown(KeyCode.H))
{
isActive = !isActive;
}
if (isActive)
{
crossHair.SetActive(true);
}
else
{
crossHair.SetActive(false);
}
}
void FixedUpdate()
{
Vector3 xMovement = transform.right * speed * HorizontalInput * Time.deltaTime;
Vector3 zMovement = transform.forward * speed * VerticalInput * Time.deltaTime;
rb.velocity = new Vector3(HorizontalInput, 0, VerticalInput) * speed;
if (isGrounded() && Input.GetButtonDown("Jump"))
{
rb.AddForce(Vector3.up * jumpPower, ForceMode.Impulse);
}
}
private bool isGrounded()
{
return Physics.Raycast(transform.position, Vector3.down, col.bounds.extents.y + 0.1f);
}
}
Is there anything wrong I am doing in this code, if so how do I fix it?
Entire project can be downloaded here: https://github.com/Some-T/FirstPersonController-CSharp
Project has relevant colliders and rigidbodies set up!
Someone has advised me to use shapecast, but I believe that may incorrect? I can't see how that would work as my player does not have character controller component added to it?
Overall how do I stop my first person character controller going through the wall like in the initial video specified above?
Upon further research I have discovered the following:
The answer is to use:
https://docs.unity3d.com/ScriptReference/Rigidbody.AddForce.html as a quick fix.
But definitively for flawlessness use:
https://docs.unity3d.com/ScriptReference/Rigidbody-velocity.html
As to how in C# I am not so sure, but here is a screen shot I did in bolt asset.
Currently I have movmement working with velocity but it does not work properly, not sure as to why? So overall my question now is how do I get movement working using velocity? I added a line in FirstPersonController.cs that moves the character using velocity of rb.velocity = new Vector3(HorizontalInput, 0, VerticalInput) * speed; so my only question and issue now is my player does not move in the direction the camera on my player is facing so I am not sure how to fix this specific thing overall?
In your project code is different from one that you provided here.
Try to enable rigidbody`s rotation constraint - freeze X and Z rotation, leave only Y. When you rotate your capsule collider (as it works in your project), it can "climb" on a wall.
Do the isGrounded check when you move, and lock movement, if not grounded.
Try to increase Collider.contactOffset
If above does not helps, try to use Rigidbody.velocity instead of Rigidbody.MovePosition.
You can also reduce Rigidbody.drag
In general, pretty good technique is to use NavMesh for movement - in this way, you able to explicitly lock player from movement outside of navmesh surface. But, of course, it doesn`t fit to many gameplays.
Hope, it helps.
upd
You move your player like this:
void FixedUpdate()
{
Vector3 xMovement = transform.right * speed * HorizontalInput * Time.deltaTime;
Vector3 zMovement = transform.forward * speed * VerticalInput * Time.deltaTime;
}
Note, that you not even apply it to rigidbody;
But you get your input this way:
float HorizontalInput = Input.GetAxis("Horizontal");
float VerticalInput = Input.GetAxis("Vertical");
in Update, so input just stays inside Update, and does not applied at all. You declared your variables twice, once on class top, another in Update().

Rotating Rigidbody controller

There is my script Rigidbody controller -
public float Speed = 5f;
public float JumpHeight = 2f;
public float GroundDistance = 0.2f;
public float DashDistance = 5f;
public LayerMask Ground;
private Rigidbody _body;
private Vector3 _inputs = Vector3.zero;
private bool _isGrounded = true;
private Transform _groundChecker;
void Start()
{
_body = GetComponent<Rigidbody>();
_groundChecker = transform.GetChild(0);
}
void Update()
{
_isGrounded = Physics.CheckSphere(_groundChecker.position, GroundDistance, Ground, QueryTriggerInteraction.Ignore);
_inputs = Vector3.zero;
_inputs.x = Input.GetAxis("Horizontal");
_inputs.z = Input.GetAxis("Vertical");
if (_inputs != Vector3.zero)
transform.forward = _inputs;
if (Input.GetButtonDown("Jump") && _isGrounded)
{
_body.AddForce(Vector3.up * Mathf.Sqrt(JumpHeight * -2f * Physics.gravity.y), ForceMode.VelocityChange);
}
if (Input.GetButtonDown("Sprint"))
{
Vector3 dashVelocity = Vector3.Scale(transform.forward, DashDistance * new Vector3((Mathf.Log(1f / (Time.deltaTime * _body.drag + 1)) / -Time.deltaTime), 0, (Mathf.Log(1f / (Time.deltaTime * _body.drag + 1)) / -Time.deltaTime)));
_body.AddForce(dashVelocity, ForceMode.VelocityChange);
}
}
void FixedUpdate()
{
_body.MovePosition(_body.position + _inputs * Speed * Time.fixedDeltaTime);
}
What the best way to make a turn on y in the direction of the camera ? That is,the player turns to the side where the mouse is turned? Is it in fixedUpdate or update?
This is the camera script:
public float Smoothness = 0.3F;
public Vector2 Sensitivity = new Vector2(4, 4);
public Vector2 LimitX = new Vector2(-70, 80);
private Vector2 NewCoord;
public Vector2 CurrentCoord;
private Vector2 vel;
public GameManager GameMangerS;
public Transform Target;
public float TransformSpeed;
public Animator CameraAnimator;
void Update()
{
NewCoord.x = Mathf.Clamp(NewCoord.x, LimitX.x, LimitX.y);
NewCoord.x -= Input.GetAxis("Mouse Y") * Sensitivity.x;
NewCoord.y += Input.GetAxis("Mouse X") * Sensitivity.y;
CurrentCoord.x = Mathf.SmoothDamp(CurrentCoord.x, NewCoord.x, ref vel.x, Smoothness / 2);
CurrentCoord.y = Mathf.SmoothDamp(CurrentCoord.y, NewCoord.y, ref vel.y, Smoothness / 2);
transform.rotation = Quaternion.Euler(CurrentCoord.x, CurrentCoord.y, 0);
}
And added this line to the controller script -
void FixedUpdate()
{
_body.MovePosition(_body.position + _inputs * Speed * Time.fixedDeltaTime);
transform.rotation = Quaternion.Euler(0, MainCamera.CurrentCoord.y, 0);
}
When I'm standing the player normally rotates, but when I start to move, all rotations are reset and the player is not moving.
Update
Simple Rotation can be achieved using transform.Rotate().
Example:
this.transform.Rotate(Vector3.up, 30);
This example is gonna rotate the transform by 30° around the Vector that points upwards.
LookAtMouse:
To make your object turn towards the mouse, you need the ScreenToWorldSpace() method from your camera. In order to convert the ScreenCoordinates into your WorldCoordinates.
Example:
Please note:
You have to set the camera instance! If you don't add that, you'll get a NullReferenceException.
This Snippets shall only show the steps needed to achieve the behavior you wish.
You will have to find out yourself how to integrate that lines of code into your code to make it work. Consider what Programmer told you in the comment when integrating that.
Vector3 mousePosition = Input.mousePosition; //get the screenSpaceMousePosition
Vector3 worldPosition = this.camera.ScreenToWorldPoint(mousePosition); //convert it into worldSpacePosition
Vector3 calculatedDirection = worldPosition - transform.position; //calucate the looking direction
calculatedDirection.y = 0;
Quaternion rotation = Quaternion.LookRotation(calculatedDirection);
transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.deltaTime);

Categories