First of all I want to apologise for my English cause I come from Poland and I'm still learning. I'm a beginner C#/Unity programmer and I have a pretty stupid/noob question/issue where player jumps twice when mashing the space. When the framerate is low for ex. 30, the problem occurs almost everytime and when framerate is for ex. 144 - hardly ever. I did some research and tried different methods. Firstly I checked whether I have all my inputs in Update and not FixedUpdate. That wasn't the problem at all. Then I tried replacing Input.GetKey to Input.GetKeyDown and GetKeyUp in Update as to ensure I have my boolean _spacePressed checked or not. That wasn't the solution too. The third thing I tried was to use Raycasting to check whether the player is grounded. With that I also checked whether when I jump, ray doesn't get checked twice. To make things clear I'm currently trying to make a 2.5D platformer. So to sum up I'm asking what could be the main issue (with input probably) where player jumps twice or even three times in a single frame when mashing space. Here's also my prototype code. I used enums to make a simple state "machine". If you have also any advice for me and my code to make things better apart from my question I would love to hear them. Thank you in advance!
using UnityEngine;
public class Movement : MonoBehaviour
{
[Header("Start Properties")]
[SerializeField] private Transform _playerTransform;
[SerializeField] private Rigidbody _playerRigidbody;
private enum MovementStates { reset, idle, walking, walkingInAir, sprinting, jumping };
private MovementStates _currentState;
[Header("Player Height Adjustables")]
[SerializeField] CapsuleCollider _playerCollider;
[SerializeField] private float _reducedHeight;
private float _originalHeight;
[Header("Player Rotation Adjustables")]
[SerializeField] private float _rotationSpeed;
Quaternion _from;
Quaternion _to;
[Header("Input Properties")]
private float _xAxis;
private bool _rightLook, _leftLook;
private bool _idle, _walking, _walkingInAir, _sprinting, _reset;
private bool _leftShiftPressed;
private bool _spacePressed;
[Header("Player Movement Adjustables")]
[SerializeField] private float _walkingSpeedMultiplier;
[SerializeField] private float _maximumWalkingSpeed;
[SerializeField] private float _walkingInAirSpeedMultiplier;
[SerializeField] private float _maximumWalkingInAirSpeed;
[SerializeField] private float _sprintingSpeedMultiplier;
[SerializeField] private float _maximumSprintingSpeed;
[SerializeField] private float _deaccelerationMultiplier;
[Header("Smooth Damp Adjustables")]
[SerializeField] private float _smoothTime;
private Vector3 _currentVelocity;
private Vector3 _smoothAxis;
[Header("Player Jump Adjustables")]
[SerializeField] private float _jumpMultiplier;
private bool _jumping;
[Header("Ground Check Adjustables")]
[SerializeField] private LayerMask _groundCheck_Layer;
[SerializeField] private float _distanceGroundCheck;
private Ray _groundCheck_Ray;
private RaycastHit _groundCheck_HitInfo;
private bool _grounded;
private void Awake()
{
_originalHeight = _playerCollider.height;
}
// input and ground checks
private void Update()
{
Process_Input();
Process_Rotation();
GroundRay_Check();
}
private void Process_Input()
{
_xAxis = Input.GetAxisRaw("Horizontal");
_rightLook = (_xAxis > 0) ? _rightLook = true : _rightLook = false;
_leftLook = (_xAxis < 0) ? _leftLook = true : _leftLook = false;
if (Input.GetKeyDown(KeyCode.LeftShift)) _leftShiftPressed = true;
if (Input.GetKeyUp(KeyCode.LeftShift)) _leftShiftPressed = false;
if (Input.GetButtonDown("Jump")) _spacePressed = true;
if (Input.GetButtonUp("Jump")) _spacePressed = false;
_idle = (_xAxis == 0 && _grounded) ? _idle = true : _idle = false;
_walking = (_xAxis != 0 && !_leftShiftPressed && _grounded) ? _walking = true : _walking = false;
_walkingInAir = (_xAxis != 0 && !_grounded) ? _walkingInAir = true : _walkingInAir = false;
_sprinting = (_xAxis != 0 && _leftShiftPressed && _grounded) ? _sprinting = true : _sprinting = false;
_jumping = (_spacePressed && _grounded) ? _jumping = true : _jumping = false;
_reset = (!_idle && !_walking && !_walkingInAir && !_sprinting && !_jumping) ? _reset = true : _reset = false;
if (Input.GetKeyDown(KeyCode.Alpha1)) Application.targetFrameRate = 30;
if (Input.GetKeyDown(KeyCode.Alpha2)) Application.targetFrameRate = 60;
if (Input.GetKeyDown(KeyCode.Alpha3)) Application.targetFrameRate = 120;
if (Input.GetKeyDown(KeyCode.Alpha4)) Application.targetFrameRate = 144;
}
private void Process_Rotation()
{
if (_rightLook)
{
_from = _playerTransform.rotation;
_to = Quaternion.Euler(0f, 0f, 0f);
_playerTransform.rotation = Quaternion.Lerp(_from, _to, _rotationSpeed * Time.deltaTime);
}
else if (_leftLook)
{
_from = _playerTransform.rotation;
_to = Quaternion.Euler(0f, 180f, 0f);
_playerTransform.rotation = Quaternion.Lerp(_from, _to, _rotationSpeed * Time.deltaTime);
}
}
private void GroundRay_Check()
{
_groundCheck_Ray.origin = _playerTransform.position;
_groundCheck_Ray.direction = Vector2.down;
Debug.DrawRay(_playerTransform.position, Vector2.down * _distanceGroundCheck, Color.green);
_grounded = Physics.Raycast(_groundCheck_Ray, out _groundCheck_HitInfo, _distanceGroundCheck, _groundCheck_Layer, QueryTriggerInteraction.Ignore);
}
// movement by states
private void FixedUpdate()
{
Process_States();
}
private void Process_States()
{
if (_idle) _currentState = MovementStates.idle;
if (_walking) _currentState = MovementStates.walking;
if (_walkingInAir) _currentState = MovementStates.walkingInAir;
if (_sprinting) _currentState = MovementStates.sprinting;
if (_jumping) _currentState = MovementStates.jumping;
if (_reset) _currentState = MovementStates.reset;
switch (_currentState)
{
case MovementStates.idle:
Process_Idle();
break;
case MovementStates.walking:
Process_Walking();
break;
case MovementStates.walkingInAir:
Process_WalkingInAir();
break;
case MovementStates.sprinting:
Process_Sprinting();
break;
case MovementStates.jumping:
Process_Jumping();
break;
case MovementStates.reset:
print("resetting");
return;
}
}
private void Process_Idle()
{
print("currently idle");
_playerCollider.height = _originalHeight;
Deaccelerate(_deaccelerationMultiplier);
}
private void Process_Walking()
{
print("currently walking");
Move(_walkingSpeedMultiplier, _maximumWalkingSpeed, ForceMode.Force);
}
private void Process_WalkingInAir()
{
print("currently walking in air");
Move(_walkingInAirSpeedMultiplier, _maximumWalkingInAirSpeed, ForceMode.Force);
}
private void Process_Sprinting()
{
print("currently sprinting");
Move(_sprintingSpeedMultiplier, _maximumSprintingSpeed, ForceMode.Force);
}
private void Process_Jumping()
{
print("currently jumping");
Jump(_jumpMultiplier, ForceMode.VelocityChange);
_spacePressed = false;
}
// movement functions
private void Move(float _speedMultiplier, float _maximumSpeed, ForceMode _forceMode)
{
CapMaximumSpeed(_maximumSpeed);
Vector2 _getAxisDirection = _xAxis * Vector2.right;
Vector2 _normalizeAxis = _getAxisDirection.normalized;
Vector2 _multiplyAxis = _normalizeAxis * _speedMultiplier * Time.deltaTime;
_smoothAxis = Vector3.SmoothDamp(_multiplyAxis, _smoothAxis, ref _currentVelocity, _smoothTime);
_playerRigidbody.AddForce(_smoothAxis, _forceMode);
}
private void CapMaximumSpeed(float _maximumSpeed)
{
float _cappedXVelocity = Mathf.Min(Mathf.Abs(_playerRigidbody.velocity.x), _maximumSpeed) * Mathf.Sign(_playerRigidbody.velocity.x);
float _cappedYVelocity = _playerRigidbody.velocity.y;
_playerRigidbody.velocity = new Vector3(_cappedXVelocity, _cappedYVelocity, 0);
}
private void Deaccelerate(float _deaccelerationMultiplier)
{
float _currentSpeed = _playerRigidbody.velocity.magnitude;
float _newSpeed = _currentSpeed - _deaccelerationMultiplier;
if (_newSpeed < 0) _newSpeed = 0;
_playerRigidbody.velocity = _playerRigidbody.velocity.normalized * _newSpeed;
}
private void Jump(float _jumpMultiplier, ForceMode _forceMode)
{
Vector2 _direction = Vector2.up;
Vector2 _multiplyDirection = _direction * _jumpMultiplier;
_playerRigidbody.AddForce(_multiplyDirection, _forceMode);
}
}
Keep in mind that FixedUpdate() can happen a few times within a single frame. Check if _spacePressed == true in the beginning of your Process_Jumping().
Related
I'm starting to make my game and ran into a problem with movement. My movement is based on the new input system and .AddForce(). The problem is that when I jump, the character's speed drops after landing and then starts picking up again.
A Physic Material with a Dynamic Friction of 2 hangs on the player's Capsule Collider (if it affects the problem)
How to make the character move without slowing down?
Video demonstration (I hope you can see what the problem is).
Rigidbody and Player Mover settings.
Code (there is no more associated code):
using System;
using UnityEngine;
[RequireComponent(typeof(Rigidbody))]
[RequireComponent(typeof(CapsuleCollider))]
public class PlayerMover : MonoBehaviour
{
[SerializeField] private LayerMask _groundLayer = 6;
[SerializeField] private float _moveForce;
[SerializeField] private float _maxForce;
[SerializeField] private float _jumpForce;
[SerializeField] private float _mouseSensitivity;
private PlayerInput _input;
private Rigidbody _rb;
private CapsuleCollider _collider;
private Camera _camera;
private float _rotationX;
private const float MinAngle = -90f;
private const float MaxAngle = 90f;
private void Awake()
{
_input = new PlayerInput();
_rb = GetComponent<Rigidbody>();
_collider = GetComponent<CapsuleCollider>();
_camera = GetComponentInChildren<Camera>();
_rb.constraints = RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationZ;
if (_groundLayer == gameObject.layer)
{
Debug.LogError("Сортувальний рівень гравця повинен відрізнятися від сортувального рівня землі!");
}
_input.Player.Jump.performed += context => Jump();
}
private void OnEnable()
{
_input.Enable();
}
private void OnDisable()
{
_input.Disable();
}
private void FixedUpdate()
{
Move();
}
private void LateUpdate()
{
Look();
}
private bool IsGrounded
{
get
{
var bottomCenterPoint = new Vector3(_collider.bounds.center.x, _collider.bounds.center.y,
_collider.bounds.center.z);
return Physics.CheckCapsule(_collider.bounds.center, bottomCenterPoint, _collider.bounds.size.x * 1.1f,
_groundLayer);
}
}
private Vector3 MovementVector
{
get
{
var cameraDirectionF = _camera.transform.forward;
var cameraDirectionR = _camera.transform.right;
cameraDirectionF.y = 0;
cameraDirectionR.y = 0;
cameraDirectionF = cameraDirectionF.normalized;
cameraDirectionR = cameraDirectionR.normalized;
var direction = _input.Player.Move.ReadValue<Vector2>();
return cameraDirectionF * direction.y + cameraDirectionR * direction.x;
}
}
private Vector2 MouseVector => _input.Player.Look.ReadValue<Vector2>() * _mouseSensitivity * Time.deltaTime;
private void Jump()
{
if (IsGrounded == false) return;
_rb.AddForce(new Vector3(0, _jumpForce, 0), ForceMode.Impulse);
}
private void Move()
{
_rb.AddForce(MovementVector * _moveForce, ForceMode.Impulse);
if (Math.Sqrt(Math.Pow(_rb.velocity.x, 2) + Math.Pow(_rb.velocity.z, 2)) < _maxForce) return;
var maxVelocity = _rb.velocity.normalized * _maxForce;
_rb.velocity = new Vector3(maxVelocity.x, _rb.velocity.y, maxVelocity.z);
}
private void Look()
{
_rotationX -= MouseVector.y;
_rotationX = Math.Clamp(_rotationX, MinAngle, MaxAngle);
_camera.transform.localEulerAngles = new Vector3(_rotationX, MouseVector.y, 0);
transform.Rotate(Vector3.up * MouseVector.x);
}
}
I tried changing the different .AddForce() values in the .Move() and .Jump() methods. Changed the value when assigning Vector3 to _rb.velocity in the .Move() method, where there is a check for the maximum allowable speed (without this check, .AddForce() will constantly increase the speed of movement).
I also thought that the problem was in the MovementVector property, but it is not so, it gives the correct values.
(The following statement is most likely incorrect)
In my opinion, after the jump, at the lowest point of the jump (almost near the ground), the character falls perpendicularly down and thus the speed is completely extinguished, so it needs to be regained.
Edit: I just modified the code to see the lowest velocity values in the console. Added the following: two variables and .Update(), one check in the MovementVector property, and changing the _flag in the .Jump() method.
private bool _flag;
private double _min = double.MaxValue;
private void Update()
{
if (!_flag) return;
var current = Math.Sqrt(Math.Pow(_rb.velocity.x, 2) + Math.Pow(_rb.velocity.z, 2));
if (current < _min) _min = current;
Debug.Log(_min);
}
ㅤ
private Vector3 MovementVector
{
get
{
....
var direction = _input.Player.Move.ReadValue<Vector2>();
if (direction.magnitude == 0)
{
_flag = false;
}
return cameraDirectionF * direction.y + cameraDirectionR * direction.x;
}
}
ㅤ
private void Jump()
{
_flag = true;
if (IsGrounded == false) return;
//_rb.AddForce(new Vector3(0, _jumpForce, 0), ForceMode.Impulse);
_rb.AddForce(transform.up * _jumpForce, ForceMode.Impulse);
}
Now, on landing, I will get accurate velocity values. With this, I noticed something, when changing _maxForce to a higher side, the landing velocity also changes. If for _maxForce equal to five the velocity dropped to zero, for _maxForce equal to 10 it drops already to 4, for _maxForce equal to 15 - to 9.
In Jump() Method you are adding the force of (0,_jumpforce,0) so this zeroes out the current movement. instead of zero you should addforce with the vector direction multiplied by jumpForce
Example:
_rb.AddForce(transform.up * _jumpForce, ForceMode.Impulse);
I hope this is fix works fine as per your requirement.
I'm making a prototype for a horror game in Unity, and I'm trying to create a script that makes it so you can hide from the enemy in certain objects.
The intention is to make it that the camera lerps to the hiding spot in a smooth animation, and then will lerp the camera back to the player's position on exit.
I have it working so the player can enter and exit with a lerp, but the issue is that after exiting the hiding spot, interactions with any object cause the camera to sporadically rotate, rather than doing the intended interaction.
Here is my hide script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Hidey : MonoBehaviour, IInteractable
{
[SerializeField] private Camera mainCamera;
[SerializeField] private Camera hideCamera;
[SerializeField] private float hideSpeed;
private float timer;
private Vector3 endPosition;
private Quaternion endRotation;
private Vector3 endPosition2;
private Quaternion endRotation2;
private bool canToggle;
private bool togglingLerp;
private bool hidden;
private void Awake()
{
hideCamera.enabled = false;
mainCamera.enabled = true;
canToggle = true;
togglingLerp = false;
hidden = false;
endPosition = hideCamera.transform.position;
endRotation = hideCamera.transform.rotation;
}
public void Interaction()
{
if (canToggle && !hidden)
{
hideCamera.enabled = true;
hideCamera.transform.position = mainCamera.transform.position;
hideCamera.transform.rotation = mainCamera.transform.rotation;
mainCamera.enabled = false;
endPosition2 = mainCamera.transform.position;
endRotation2 = mainCamera.transform.rotation;
togglingLerp = true;
}
else return;
}
private void Update()
{
if(togglingLerp && !hidden)
{
canToggle = false;
timer = Time.deltaTime * hideSpeed;
HideAnimation(hideCamera.transform.position, endPosition, hideCamera.transform.rotation, endRotation, timer, true, hideCamera);
}
if(hidden && Input.GetKeyDown(KeyCode.E))
{
canToggle = false;
mainCamera.enabled = true;
mainCamera.transform.position = hideCamera.transform.position;
mainCamera.transform.rotation = hideCamera.transform.rotation;
hideCamera.enabled = false;
timer = Time.deltaTime * hideSpeed;
HideAnimation(mainCamera.transform.position, endPosition2, mainCamera.transform.rotation, endRotation2, timer, false, mainCamera);
}
}
private void HideAnimation(Vector3 startPos, Vector3 endPos, Quaternion startRot, Quaternion endRot, float lerpTimer, bool isHidden, Camera currentCamera)
{
currentCamera.transform.position = Vector3.Lerp(startPos, endPos, lerpTimer);
currentCamera.transform.rotation = Quaternion.Lerp(startRot, endRot, lerpTimer);
if (Quaternion.Dot(currentCamera.transform.rotation, endRot) > .9999f && Vector3.Dot(currentCamera.transform.position, endPos) > .9999f)
{
hidden = isHidden;
togglingLerp = false;
canToggle = true;
}
}
}
I have no idea what is causing this issue at the moment.
I am new to unity and am trying to take two scripts that I have written based on youtube tutorials and make a script that allows my character to have dynamic jump, coyote time, jump buffering, and dash. Everything is working except for the dash now. When I play, all of the features work, including the anti-gravity during the dash, but my character does not speed up; it remains at the same speed as when walking. I think the two IEnumerator may be conflicting, but I am unsure of how to resolve it. So far, everything I have tried has not worked. I appreciate the help! enter image description here
using System.Collections;
using UnityEngine;
public class PlayerMovementBendux : MonoBehaviour
{
private float horizontal;
public float speed = 8f;
public float jumpingPower = 16f;
private bool isFacingRight = true;
private bool isJumping;
private float coyoteTime = 0.2f;
private float coyoteTimeCounter;
private float jumpBufferTime = 0.2f;
private float jumpBufferCounter;
private bool canDash = true;
private bool isDashing;
public float dashingPower = 24f;
public float dashingTime = 0.2f;
public float dashingCooldown = 1f;
[SerializeField] private Rigidbody2D rb;
[SerializeField] private Transform groundCheck;
[SerializeField] private LayerMask groundLayer;
[SerializeField] private TrailRenderer tr;
private void Update()
{
if (isDashing)
{
return;
}
horizontal = Input.GetAxisRaw("Horizontal");
if (IsGrounded())
{
coyoteTimeCounter = coyoteTime;
}
else
{
coyoteTimeCounter -= Time.deltaTime;
}
if (Input.GetButtonDown("Jump"))
{
jumpBufferCounter = jumpBufferTime;
}
else
{
jumpBufferCounter -= Time.deltaTime;
}
if (coyoteTimeCounter > 0f && jumpBufferCounter > 0f && !isJumping)
{
rb.velocity = new Vector2(rb.velocity.x, jumpingPower);
jumpBufferCounter = 0f;
StartCoroutine(JumpCooldown());
}
if (Input.GetButtonUp("Jump") && rb.velocity.y > 0f)
{
rb.velocity = new Vector2(rb.velocity.x, rb.velocity.y * 0.5f);
coyoteTimeCounter = 0f;
}
if (Input.GetKeyDown(KeyCode.LeftShift) && canDash)
{
StartCoroutine(Dash());
}
Flip();
}
private void FixedUpdate()
{
rb.velocity = new Vector2(horizontal * speed, rb.velocity.y);
}
private bool IsGrounded()
{
return Physics2D.OverlapCircle(groundCheck.position, 0.2f, groundLayer);
}
private void Flip()
{
if (isFacingRight && horizontal < 0f || !isFacingRight && horizontal > 0f)
{
Vector3 localScale = transform.localScale;
isFacingRight = !isFacingRight;
localScale.x *= -1f;
transform.localScale = localScale;
}
}
private IEnumerator JumpCooldown()
{
isJumping = true;
yield return new WaitForSeconds(0.4f);
isJumping = false;
}
private IEnumerator Dash()
{
canDash = false;
isDashing = true;
float originalGravity = rb.gravityScale;
rb.gravityScale = 0f;
rb.velocity = new Vector2(transform.localScale.x * dashingPower, 0f);
tr.emitting = true;
yield return new WaitForSeconds(dashingTime);
tr.emitting = false;
rb.gravityScale = originalGravity;
isDashing = false;
yield return new WaitForSeconds(dashingCooldown);
canDash = true;
}
}
I'm really new to Unity 3d and I'm trying to make a respawn with my character. It seems that the answer is really easy but I cannot see why my code is not working. If this is a duplicate, let me know.
public Vector3 PointSpawn;
void Start()
{
PointSpawn = gameObject.transform.position;
}
void Update()
{
if (gameObject.transform.position.y < 10)
{
gameObject.transform.position = PointSpawn; // This doesn't work
// gameObject.transform.LookAt(PointSpawn); ---> This DOES work ok
}
}
Parallel Script
public float HorizontalMove;
public float VerticalMove;
private Vector3 playerInput;
public CharacterController player;
public float MoveSpeed;
private Vector3 movePlayer;
public float gravity = 9.8f;
public float fallVelocity;
public float JumpForce;
public bool DoubleJump = false;
public Camera mainCamera;
private Vector3 camForward;
private Vector3 camRight;
void Start()
{
player = GetComponent<CharacterController>();
}
void Update()
{
HorizontalMove = Input.GetAxis("Horizontal");
VerticalMove = Input.GetAxis("Vertical");
playerInput = new Vector3(HorizontalMove, 0, VerticalMove);
playerInput = Vector3.ClampMagnitude(playerInput, 1);
CamDirection();
movePlayer = playerInput.x * camRight + playerInput.z * camForward;
movePlayer = movePlayer * MoveSpeed;
player.transform.LookAt(player.transform.position + movePlayer);
setGravity();
PlayerSkills();
player.Move(movePlayer * Time.deltaTime );
}
void CamDirection()
{
camForward = mainCamera.transform.forward;
camRight = mainCamera.transform.right;
camForward.y = 0;
camRight.y = 0;
camForward = camForward.normalized;
camRight = camRight.normalized;
}
void PlayerSkills()
{
if (player.isGrounded && Input.GetButtonDown("Jump"))
{
fallVelocity = JumpForce;
movePlayer.y = fallVelocity;
DoubleJump = true;
}
else if (player.isGrounded == false && Input.GetButtonDown("Jump") && DoubleJump == true)
{
fallVelocity = JumpForce *2;
movePlayer.y = fallVelocity;
DoubleJump = false;
}
}
void setGravity()
{
if (player.isGrounded)
{
fallVelocity = -gravity * Time.deltaTime;
movePlayer.y = fallVelocity;
}
else
{
fallVelocity -= gravity * Time.deltaTime;
movePlayer.y = fallVelocity;
}
}
Thanks in advance!
Just so the answer to the question is not in the comments:
The original problem is that the assignment gameObject.transform.position = PointSpawn appeared to do nothing. As the line is written properly, the position of this gameObject, must have been getting overwritten elsewhere.
With the addition of OP's movement script, the position of the player was getting overwritten in the movement's Update function. As the other assignment was being done in Update, the call order was not guaranteed to work as intended. The fix is either to assure that the movement Update is run not the frame of the new position assignment or to move the conditional and the assignment to a function that always runs after Update regardless of script execution order, LateUpdate.
I've hit a roadblock where when I move the camera, while moving, the character rotates on the spot however, their direction in where they are heading changes when I update the left thumbstick.
This is odd as updating it in Update doesn't work and forces the character to move in circles and placing it in a bit of script that updates with the right thumbstick is moved, causes the character to rotate and move in very different directions that what I want it to go in. This is temporarily fixed when I move the left thumbstick, updating the character's movement.
The controls for this are: Left Thumbstick - Move player, Right Thumbstick - Move camera, East Button - Jump, North Button - Run.
The goal is to allow the character to rotate themselves as well as their direction when I move the camera rather than them only updating their direction when I move the left thumbstick.
Before the code, these are the packages I'm currently using within Unity that effect this: Cinemachine & Input System.
Here's the movement code that this is effecting:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Cinemachine;
using UnityEngine.AI;
using UnityEngine.InputSystem;
public class PlayerMovement : MonoBehaviour
{
private DefaultControls controls;
[Header("Unity General")]
[SerializeField]
private CharacterController controller;
public Transform cameraTransform;
public InputActionReference cameraControl;
[Header("General Settings")]//Player movement.
public bool canMovePlayer;
private Vector2 currentMovementInput;
private Vector3 currentMovement;
private Vector3 currentRunMovement;
private bool isMovementPressed;
private bool isRunPressed;
[Space]//Animator stuff.
public Animator characterAnimator;
private int isWalkingHash;
private int isRunningHash;
[Space]//Player running speed & how fast the player will turn when going left or right.
public float rotationFactorPerFrame = 15.0f;
public float runMultiplier = 3.0f;
[Space]//Default gravity for when the player is falling and gravity for when the player is grounded.
public float gravity = -9.81f;
public float groundedGravity = -0.05f;
[Space]//Playing jumping.
public float initialJumpVelocity;
private bool isJumpPressed = false;
private float maxJumpHeight = 1f;
private float maxJumpTime = 0.5f;
private bool isJumping = false;
private int isJumpingHash;
private bool isJumpAnimating = false;
private void Start()
{
Cursor.lockState = CursorLockMode.Locked;
npcInteraction = GetComponent<NPCInteraction>();
}
private void Awake()
{
controls = new DefaultControls();
controller = GetComponent<CharacterController>();
isWalkingHash = Animator.StringToHash("isWalking");
isRunningHash = Animator.StringToHash("isRunning");
isJumpingHash = Animator.StringToHash("isJumping");
controls.Movement.Walking.started += OnMovementInput;
controls.Movement.Walking.canceled += OnMovementInput;
controls.Movement.Walking.performed += OnMovementInput;
controls.Movement.Run.started += OnRun;
controls.Movement.Run.canceled += OnRun;
controls.Movement.Jump.started += OnJump;
controls.Movement.Jump.canceled += OnJump;
SetupJumpVariables();
}
private void SetupJumpVariables()
{
float timeToApex = maxJumpTime / 2;
gravity = (-2 * maxJumpHeight) / Mathf.Pow(timeToApex, 2);
initialJumpVelocity = (2 * maxJumpHeight) / timeToApex;
}
private void HandleJump()
{
if (!isJumping && controller.isGrounded && isJumpPressed)
{
characterAnimator.SetBool(isJumpingHash, true);
isJumpAnimating = true;
isJumping = true;
currentMovement.y = initialJumpVelocity * 0.5f;
currentRunMovement.y = (initialJumpVelocity + 0.5f) * 0.5f;
}
else if (!isJumpPressed && isJumping && controller.isGrounded)
{
isJumping = false;
}
}
private void OnJump(InputAction.CallbackContext context)
{
isJumpPressed = context.ReadValueAsButton();
}
private void OnRun(InputAction.CallbackContext context)
{
isRunPressed = context.ReadValueAsButton();
}
private void HandleRotation()
{
Vector3 positionToLookAt;
//Change in position our character should point to.
positionToLookAt.x = currentMovement.x;
positionToLookAt.y = 0.0f;
positionToLookAt.z = currentMovement.z;
//Current rotation of our character.
Quaternion currentRotation = transform.rotation;
if (currentMovementInput != Vector2.zero)
{
//Creates a new rotation based on where the player is currently pressing.
float targetAngle = Mathf.Atan2(currentMovementInput.x, currentMovementInput.y) * Mathf.Rad2Deg + cameraTransform.eulerAngles.y;
Quaternion targetRotation = Quaternion.Euler(0f, targetAngle, 0f);
transform.rotation = Quaternion.Lerp(currentRotation, targetRotation, rotationFactorPerFrame * Time.deltaTime);
}
}
private void OnMovementInput(InputAction.CallbackContext context)
{
currentMovementInput = context.ReadValue<Vector2>();
currentMovement = new Vector3(currentMovementInput.x, 0f, currentMovementInput.y);
currentRunMovement.x = currentMovementInput.x * runMultiplier;
currentRunMovement.z = currentMovementInput.y * runMultiplier;
MovementDirection();
isMovementPressed = currentMovementInput.x != 0 || currentMovementInput.y != 0;
}
private void MovementDirection()
{
currentMovement = cameraTransform.forward * currentMovement.z + cameraTransform.right * currentMovement.x;
currentMovement.y = 0f;
currentRunMovement = cameraTransform.forward * currentRunMovement.z + cameraTransform.right * currentRunMovement.x;
currentRunMovement.y = 0f;
}
private void HandleAnimation()
{
bool isWalking = characterAnimator.GetBool(isWalkingHash);
bool isRunning = characterAnimator.GetBool(isRunningHash);
if (isMovementPressed && !isWalking)
{
characterAnimator.SetBool(isWalkingHash, true);
}
else if (!isMovementPressed && isWalking)
{
characterAnimator.SetBool(isWalkingHash, false);
}
if ((isMovementPressed && isRunPressed) && !isRunning)
{
characterAnimator.SetBool(isRunningHash, true);
}
else if ((!isMovementPressed || !isRunPressed) && isRunning)
{
characterAnimator.SetBool(isRunningHash, false);
}
}
private void HandleGravity()
{
bool isFalling = currentMovement.y <= 0.0f;
float fallMultiplier = 1.5f;
if (controller.isGrounded)
{
characterAnimator.SetBool(isJumpingHash, false);
isJumpAnimating = false;
currentMovement.y = groundedGravity;
currentRunMovement.y = groundedGravity;
}
else if (isFalling)
{
float previousYVelocity = currentMovement.y;
float newYVelocity = currentMovement.y + (gravity * fallMultiplier * Time.deltaTime);
float nextYVelocity = (previousYVelocity + newYVelocity) * 0.5f;
currentMovement.y = nextYVelocity;
currentRunMovement.y = nextYVelocity;
}
else
{
float previousYVelocity = currentMovement.y;
float newYVelocity = currentMovement.y + (gravity * Time.deltaTime);
float nextYVelocity = (previousYVelocity + newYVelocity) * 0.5f;
currentMovement.y = nextYVelocity;
currentRunMovement.y = nextYVelocity;
}
}
// Update is called once per frame
public void Update()
{
HandleRotation();
HandleAnimation();
controller.Move(currentMovement * Time.deltaTime);
characterAnimator.SetFloat("Speed", controls.Movement.Walking.ReadValue<Vector2>().magnitude);
if (isRunPressed)
{
controller.Move(currentRunMovement * Time.deltaTime);
}
else
{
controller.Move(currentMovement * Time.deltaTime);
}
HandleGravity();
HandleJump();
if (cameraControl.action.triggered)
{
MovementDirection();
}
LockOnTarget();
Interaction();
}
private void OnEnable()
{
controls.Movement.Enable();
}
private void OnDisable()
{
controls.Movement.Disable();
}
}```
Before diving into your code that has quite deep stuff regarding the camara movement, find this simple way in which a gameObject can face the direction of the camera in a simple way. Just in case it helps. This answers directly your question "How to get a character to turn in the direction of the camera?"
using UnityEngine;
public class LookToCamForward : MonoBehaviour
{
void Start()
{
transform.rotation = Quaternion.LookRotation(Camera.main.transform.forward, Camera.main.transform.up);
}
}