- Home /
how to make player run-speed independent of computer?
Hi everyone, I'm new to unity and I've written a script for my player run. Although I used Time.deltaTime but yet the run speed is changing in different computers. Could you please check my code and tell me where I'm doing wrong?
Thanks in advance
using UnityEngine;
// attach the script to the player
public class PlayerMovement : MonoBehaviour
{
public float runSpeed = 140f;
public float jumpSpeed = 9f;
public float gravity = 9.81f;
public float laneWidth = 2.5f;
public float smooth;
public float minRunSpeed = 130f;
public float maxRunSpeed = 250f;
public float accelerationTime = 60;
private float time;
CharacterController controller;
Vector3 direction;
float directionY;
bool isMoveStarted = false;
int lane = 1; // 0:left 1:middle 2:righ;
void Start()
{
controller = GetComponent<CharacterController>();
direction = new Vector3(0, 0, 0);
time = 0;
}
void Update()
{
if (isMoveStarted)
{
runSpeed = Mathf.SmoothStep(minRunSpeed, maxRunSpeed, time / accelerationTime);
time += Time.deltaTime;
ProcessUserInput();
direction.z = runSpeed * Time.deltaTime;
HandleSliding();
Run();
}
}
void LateUpdate()
{
UpdateXPosition();
}
void Jump()
{
directionY = jumpSpeed;
}
void Run()
{
directionY -= gravity * Time.deltaTime;
direction.y = directionY;
controller.Move(direction * Time.deltaTime);
}
void StartMoving()
{
isMoveStarted = true;
GameObject.Find("TileManager").SendMessage("PlayEnvironmentSound");
}
void ProcessUserInput()
{
if ((Input.GetKeyDown(KeyCode.LeftArrow) || Input.GetKeyDown(KeyCode.A)) && lane > 0)
{
lane--;
}
if ((Input.GetKeyDown(KeyCode.RightArrow) || Input.GetKeyDown(KeyCode.D)) && lane < 2)
{
lane++;
}
}
void UpdateXPosition()
{
if (!controller.isGrounded)
{
return;
}
Vector3 newPosition = transform.localPosition.z * transform.forward + transform.localPosition.y * transform.up;
if (lane == 0)
{
newPosition += Vector3.left * laneWidth;
}
else if (lane == 2)
{
newPosition += Vector3.right * laneWidth;
}
transform.localPosition = Vector3.Lerp(transform.localPosition, newPosition, smooth);
}
void HandleSliding()
{
if (Input.GetKeyDown(KeyCode.S) || Input.GetKeyDown(KeyCode.DownArrow))
{
controller.height = 1.5f;
Vector3 tempCenter = controller.center;
tempCenter.y = controller.height / 2;
controller.center = tempCenter;
Invoke("SetCenter", 2f);
}
}
void SetCenter()
{
controller.height = 3.64f;
Vector3 tempCenter = controller.center;
tempCenter.y = 1.91f;
controller.center = tempCenter;
}
void UpdateRunSpeed()
{
runSpeed = runSpeed + 1;
}
}
Time.deltaTime (time between frames), Update() and LateUpdate() (called once each frame) are inherently framerate dependent. Why don't you use Time.fixedDeltaTime and FixedUpdate() instead?
While it's true that both Update()
and LateUpdate()
are frame-dependant by design, it's not true necessarily that transformations happening there must mirror this behavior. Time.deltaTime
, when applied correctly, fixes this issue.
I see. This however does not answer my question. I believe that OP may be way more competent than I am so I ask not to boast but to learn.
I think you're correct at suggesting FixedUpdate
as a better place to call controller.Move
from.
Although, since one can't read Input.GetKeyDown
reliably in FixedUpdate
and this code provided by OP is an unnecessarily complicated mess already, i suggested a way to fix this as it is - in rendering steps, just so this new changed code looks similar to it's original and will be easier to understand.
Is it correct that one can circumvent this issue by setting a flag spacePressed = Input.GetKeyDown(KeyCode.Space) in Update() to then read from it in FixedUpdate?one can't read Input.GetKeyDown reliably in FixedUpdate
Answer by andrew-lukasik · May 24, 2021 at 12:34 PM
Step 1:
Fix whatever this is:
transform.localPosition = Vector3.Lerp( transform.localPosition , newPosition , smooth );
because smooth
is at fault here and should be understood as some kind of local_move_speed
implemented as local_move_speed * Time.deltaTime
here. Otherwise Lerp
alone makes this frame-dependent.
Step 2:
Simplify your code
PlayerMovement.cs:
public class PlayerMovement : MonoBehaviour
{
[SerializeField] float
runSpeed = 140f ,
jumpSpeed = 9f ,
gravity = 9.81f,
laneWidth = 2.5f,
localMoveSpeed = 1f,
minRunSpeed = 130f,
maxRunSpeed = 250f,
accelerationTime = 60f;
CharacterController _controller = null;
TileManager _tileManager = null;
Vector3 _velocity = Vector3.zero;
bool _isMoveStarted = false;
float _moveStartedTime = 0f;
Lane _lane = Lane.Middle;
void Awake ()
{
_controller = GetComponent<CharacterController>();
_tileManager = FindObjectOfType<TileManager>();
}
void Update ()
{
if( _isMoveStarted )
{
float timeSinceMoveStart = Time.time - _moveStartedTime;
runSpeed = Mathf.SmoothStep( minRunSpeed , maxRunSpeed , timeSinceMoveStart / accelerationTime );
// lane change:
if( (Input.GetKeyDown(KeyCode.LeftArrow) || Input.GetKeyDown(KeyCode.A)) && (int)_lane > (int)Lane.Left )
_lane--;
if( (Input.GetKeyDown(KeyCode.RightArrow) || Input.GetKeyDown(KeyCode.D)) && (int)_lane < (int)Lane.Right )
_lane++;
_velocity.z = runSpeed;
// sliding:
if( Input.GetKeyDown(KeyCode.S) || Input.GetKeyDown(KeyCode.DownArrow) )
{
_controller.height = 1.5f;
Vector3 tempCenter = _controller.center;
tempCenter.y = _controller.height / 2;
_controller.center = tempCenter;
Invoke( nameof(SetCenter) , 2f );
}
// jumping and falling:
if( Input.GetKeyDown(KeyCode.Space) && _controller.isGrounded )
_velocity.y = jumpSpeed;
else
_velocity.y = -gravity;
// run:
_controller.Move( _velocity * Time.deltaTime );
}
// ( im not sure what this is )
if( _controller.isGrounded )
{
// update x position:
Vector3 src = transform.localPosition;
Vector3 dst = transform.localPosition.z * transform.forward + transform.localPosition.y * transform.up;
if( _lane==Lane.Left ) dst += Vector3.left * laneWidth;
else if( _lane==Lane.Right ) dst += Vector3.right * laneWidth;
transform.localPosition = Vector3.Lerp( src , dst , localMoveSpeed * Time.deltaTime );
}
}
void StartMoving ()
{
_isMoveStarted = true;
_moveStartedTime = Time.time;
_tileManager.PlayEnvironmentSound();
}
void SetCenter ()
{
_controller.height = 3.64f;
Vector3 oldCenter = _controller.center;
_controller.center = new Vector3( oldCenter.x , 1.91f , oldCenter.z );
}
void UpdateRunSpeed ()
{
runSpeed += 1;
}
public enum Lane
{
Left = 0 ,
Middle = 1 ,
Right = 2
}
}
TileManager.cs
public class TileManager : MonoBehaviour
{
public void PlayEnvironmentSound ()
{
Debug.Log("(play a sound here)");
}
}
Thank you so much. I'll try your code when I get to home.