i have an fps controller with some parkour movement and i did build the game i had 300 fps or so and it was unplayable because the gravity value was too hgih and the movement speed was high too
but in the other hand i get 60 fps in playmode and it works fine idk what causes it or how to fix it actually my movement system is based on character controller so everything is on the update method should i switch it to fixed update or ?
what i want is a constant values regardless of the fps
@DenisIsDenis some details doesnt matter like states or what ever i used fixed delta time with normal delta time to smooth it a bit but as i said it gets buggy with higher fps even when i used just delta time i faced the same issue and its kinda jerky with just delta time
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Movement : MonoBehaviour
{
public enum States
{
Walking,
WallRunig,
Grappling,
verticalWallRuning,
}
States state;
[Header("Controller Settings")]
[SerializeField] CharacterController cc;
[SerializeField] float Movement_Speed;
[SerializeField] float walkSpeed = 15f;
[SerializeField] float sprintSpeed = 23f;
[SerializeField] float JumpForce = 15.0f;
[SerializeField] float CrouchSpeed;
bool crouching = false;
[SerializeField] float WallJump = 5f;
float originalHight;
[SerializeField] float crouchHight;
private Vector3 forward, right;
private Vector3 moveDir = Vector3.zero;
bool stopped = false;
[Header("Gravity")]
[SerializeField] float CurrentGravity = 13.0f;
[SerializeField] float NormalGravityOnGround = 14f;
[SerializeField] float wallJumpGravity = 18f;
[SerializeField] float wallGravity = .5f;
[SerializeField] float HookGravity = 19f;
[SerializeField] [Range(.0f, .5f)] float moveSmoothTime = 0.3f;
private Vector2 currentDir, currentDirVelocity;
float velovityY = 0.0f;
[Header("WallRun")]
[SerializeField] private float wallSpeed;
[SerializeField] LayerMask Wall;
[SerializeField] float SideWallforce;
private Vector3 WallDir;
private bool Right, left;
[SerializeField] float Limit;
private float YonWall;
[SerializeField] float MaxWallDistance;
float LWallDistance, RwallDistance;
RaycastHit Lhit;
RaycastHit hit;
[Header("Camera")]
[SerializeField] Camera cam;
[SerializeField] private float fov;
[SerializeField] private float wallrunFov;
[SerializeField] private float wallrunfovTime;
[SerializeField] private float camTilt;
[SerializeField] private float camTiltTime;
[SerializeField] private float GrappleFov;
[SerializeField] private float GrappleFovTime;
public float tilt { get; private set; }
[Header("Grabble")]
[SerializeField] float GrappleRange;
[SerializeField] float GrappleSpeed;
private Vector3 hookPosition;
[SerializeField] float GrappleJump = 1000f;
[SerializeField] LayerMask Grapple;
LineRenderer lr;
[Header("vertical Wallrun settings")]
[SerializeField] private float VerticalgravityForce = 5f;
Vector3 verticalWallRunDirection = Vector3.zero;
public LayerMask verticalWRLayer;
[SerializeField] float maxcastdistance;
bool JumpedFromWall = false;
[SerializeField] float verticalWallSPeed = 10f;
[SerializeField] float CLimbingJump = 750f;
RaycastHit vhit;
private void Awake()
{
lr = GetComponent<LineRenderer>();
}
void Start()
{
originalHight = cc.height;
state = States.Walking;
}
private void LateUpdate()
{
if (state == States.Grappling)
{
DrawRope();
}
}
private void Update()
{
if (state == States.Walking)
{
movementInput();
normalMovement();
Crouch();
sprint();
resetfov();
lr.positionCount = 0;
}
else if (state == States.WallRunig)
{
wallruning();
}
else if (state == States.Grappling)
{
GrappleOnAir();
}
else if (state == States.verticalWallRuning)
{
VerticalWallrun();
}
RwallDistance = Vector3.Distance(transform.position, hit.point);
LWallDistance = Vector3.Distance(transform.position, Lhit.point);
float vWallDistance = Vector3.Distance(transform.position, vhit.point);
if (stopped && cc.isGrounded)
{
stopped = false;
}
else if (stopped && LWallDistance >= MaxWallDistance && RwallDistance >= MaxWallDistance)
{
stopped = false;
}
if (JumpedFromWall && cc.isGrounded)
{
JumpedFromWall = false;
}
else if (JumpedFromWall && vWallDistance >= MaxWallDistance)
{
JumpedFromWall = false;
}
checkWall();
GrappleCheck();
//checking for vertical climbing;
if (Physics.Raycast(cam.transform.position, cam.transform.forward, out vhit, maxcastdistance, verticalWRLayer) && !JumpedFromWall && !cc.isGrounded)
{
state = States.verticalWallRuning;
}
}
void movementInput()
{
Vector2 Targetdir = new Vector2(Input.GetAxisRaw("Horizontal"),
Input.GetAxisRaw("Vertical"));
Targetdir.Normalize();
currentDir = Vector2.SmoothDamp(currentDir, Targetdir, ref currentDirVelocity, moveSmoothTime);
if (cc.isGrounded)
{
velovityY -= CurrentGravity * Time.fixedDeltaTime;
CurrentGravity = NormalGravityOnGround;
}
if (!cc.isGrounded)
{
velovityY -= CurrentGravity * Time.fixedDeltaTime;
}
if (Input.GetButtonDown("Jump") && cc.isGrounded) velovityY = JumpForce * Time.fixedDeltaTime;
cc.Move(moveDir * Time.fixedDeltaTime);
}
void normalMovement()
{
forward = cam.transform.forward.normalized;
right = transform.right.normalized;
moveDir = (forward * currentDir.y + right * currentDir.x) * Movement_Speed;
moveDir.y = velovityY;
}
void Crouch()
{
if (Input.GetKey(KeyCode.C))
{
crouching = true;
}
else crouching = false;
if (crouching)
{
cc.height = crouchHight;
Movement_Speed = CrouchSpeed;
}
else if (!crouching)
{
cc.height = originalHight;
Movement_Speed = walkSpeed;
}
}
void sprint()
{
if (Input.GetKey(KeyCode.LeftShift))
{
Movement_Speed = sprintSpeed;
}
else if (Input.GetKeyUp(KeyCode.LeftShift))
{
Movement_Speed = walkSpeed;
}
}
void checkWall()
{
if (Physics.Raycast(transform.position, transform.right * SideWallforce, out hit, Limit, Wall) && !cc.isGrounded && !stopped)
{
Right = true;
left = false;
WallDir.y = YonWall;
YonWall = wallGravity * Time.fixedDeltaTime;
state = States.WallRunig;
}
else if (Physics.Raycast(transform.position, -transform.right * SideWallforce, out Lhit, Limit, Wall) && !cc.isGrounded && !stopped)
{
left = true;
Right = false;
WallDir.y = wallGravity * Time.fixedDeltaTime;
state = States.WallRunig;
}
else
{
Right = false;
left = false;
state = States.Walking;
}
}
void startwallrun()
{
cam.fieldOfView = Mathf.Lerp(cam.fieldOfView, wallrunFov, wallrunfovTime * Time.deltaTime);
if (Right == true)
{
WallDir = (forward * wallSpeed + right * SideWallforce);
tilt = Mathf.Lerp(tilt, camTilt, camTiltTime * Time.deltaTime);
}
else if (left == true)
{
WallDir = (forward * wallSpeed + -right * SideWallforce);
tilt = Mathf.Lerp(tilt, -camTilt, camTiltTime * Time.deltaTime);
}
cc.Move(WallDir * Time.deltaTime);
}
void stopWallrun()
{
state = States.Walking;
Right = false;
left = false;
stopped = true;
}
void wallruning()
{
startwallrun();
if (Input.GetButtonDown("Jump"))
{
stopWallrun();
velovityY = WallJump * Time.fixedDeltaTime;
moveDir.y = velovityY;
CurrentGravity = wallJumpGravity;
}
else if (!Right && !left)
{
stopWallrun();
}
}
void resetfov()
{
cam.fieldOfView = Mathf.Lerp(cam.fieldOfView, fov, wallrunfovTime * Time.deltaTime);
tilt = Mathf.Lerp(tilt, 0, camTiltTime * Time.deltaTime);
}
void GrappleCheck()
{
if (Physics.Raycast(cam.transform.position, cam.transform.forward, out RaycastHit hit, GrappleRange, Grapple) && Input.GetKey(KeyCode.E))
{
hookPosition = hit.point;
state = States.Grappling;
}
}
void GrappleOnAir()
{
cam.fieldOfView = Mathf.Lerp(cam.fieldOfView, GrappleFov, GrappleFovTime * Time.deltaTime);
velovityY = -1f * Time.deltaTime;
Vector3 GrappleDirection = (hookPosition - transform.position).normalized;
cc.Move(GrappleDirection * GrappleSpeed * Time.deltaTime);
if (Input.GetKeyUp(KeyCode.E))
{
velovityY = GrappleJump * Time.fixedDeltaTime;
CurrentGravity = HookGravity;
moveDir -= hit.normal * GrappleJump;
}
if (Vector3.Distance(transform.position, hit.point) >= 10f)
{
velovityY = GrappleJump * Time.fixedDeltaTime;
moveDir -= hit.normal * GrappleJump;
state = States.Walking;
}
}
void DrawRope()
{
lr.enabled = true;
lr.positionCount = 2;
lr.SetPosition(0, transform.position);
lr.SetPosition(1, hookPosition);
}
void VerticalWallrun()
{
StartverticalWallrun();
velovityY = -1f;
}
void StartverticalWallrun()
{
cam.fieldOfView = Mathf.Lerp(cam.fieldOfView, wallrunFov, wallrunfovTime * Time.deltaTime);
verticalWallRunDirection = (forward * VerticalgravityForce + transform.up * verticalWallSPeed) * Time.deltaTime;
cc.Move(verticalWallRunDirection * Time.fixedDeltaTime);
if (Input.GetButtonDown("Jump"))
{
stopverticalWallrun();
JumpedFromWall = true;
velovityY = CLimbingJump * Time.fixedDeltaTime;
moveDir.y = velovityY;
}
}
void stopverticalWallrun()
{
JumpedFromWall = true;
state = States.Walking;
}
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Finish") && state == States.verticalWallRuning)
{
stopverticalWallrun();
JumpedFromWall = true;
velovityY = CLimbingJump * Time.fixedDeltaTime;
moveDir.y = velovityY;
}
}
}
I have tested your script. Replacing all fixedDeltaTimes
with deltaTime
solves the FPS dependency problem.
_
You should never use fixedDeltaTime
in the Update()
function. After all, this leads to the following consequences:
_
1) By default FixedUpdate()
is called 50 times per second. That is, when using fixedDeltaTime
in Update()
, normal behavior will only be observed at 50 frames per second.
_
2) If the user has more than 50 fps, then all script values will be very large (for example, at 500 fps, the values will be 10 times more than normal).
_
3) If the user has less than 50 fps, then on the contrary, he will have a slowdown, all values will be less than normal (for example, at 25 fps, all values will be 2 times less than normal).
_
Therefore, I recommend using only Time.deltaTime
in the Update()
function and achieving smoothness at the expense of Lerp
, LerpUnclamped
, MoveTowards
and RotateTowards
functions.