I've been confusing myself about Update() vs FixedUpdate() and time.deltaTime vs time.fixedDeltaTime for this situation.
I want frame-rate independent movement for the camera mouse-look but at the same time I want it to be as smooth as possible for 60 fps. For some reason, 60 fps seems slightly jittery.
My question is, do I put the function below inside of Update() or FixedUpdate()? Should the deltaTime variable be time.deltaTime or time.fixedDeltaTime to achieve this? Or am I doing this all wrong?
Thanks for any input.
BTW the SmoothDamp function below is a smoothing function based on lerp that allows for the feedback
void MouseLook()
{
if (_isEscaped)
return;
if (!_isAllowedToLook)
return;
var deltaTime = Time.fixedDeltaTime;
var adjustedSensitivity = _lookSensitivity * 100f; //Keeps sensitivity between 0.1 and 10f but makes the sensitivity in math actually work
_lookInput.x = SmoothDamp.Float(_lookInput.x, Input.GetAxis("Mouse X"), _lookSmoothSpeed, deltaTime);
_lookInput.y = SmoothDamp.Float(_lookInput.y, Input.GetAxis("Mouse Y"), _lookSmoothSpeed, deltaTime);
// Mouse look
var newVerticalPitch = _verticalPitch + _lookInput.y * -adjustedSensitivity * deltaTime;
_verticalPitch = SmoothDamp.Float(_verticalPitch, newVerticalPitch, 15f, deltaTime);
_verticalPitch = Mathf.Clamp(_verticalPitch, -_maxVerticalLookAngle, _maxVerticalLookAngle); //Clamp vertical look
// Camera vertical rotation
var newCameraRotation = Quaternion.Euler(Vector3.right * _verticalPitch);
_camTransform.localRotation =
SmoothDamp.Quaternion(_camTransform.localRotation, newCameraRotation, 15f, deltaTime);
// Player horizontal rotation
var newPlayerRotation = _transform.rotation * Quaternion.Euler(_lookInput.x * adjustedSensitivity * deltaTime * Vector3.up);
_transform.rotation = SmoothDamp.Quaternion(_transform.rotation, newPlayerRotation, 15f, deltaTime); //Player rotation
}
Update and FixedUpdate are both callback functions that Unity invokes on your Monobehaviours at certain times during their lifetime. More specifically, Update is called every frame (assuming the Monobehaviour is enabled) and FixedUpdate is called every physics frame.
Physics frames are independent of graphical frame-rate and are set from within Unity to occur at a specific interval. The default is 50 times per second (in other words FixedUpdate runs at 50fps by default).
FixedUpdate is generally a function that you should reserve for your physics-based needs (such as using RigidBodies or ray-casting etc.) and most certainly not one to use for things such as getting user input.
Now that that is hopefully cleared up, let's address Time.deltaTime and Time.fixedDeltaTime:
Time.fixedDeltaTime returns the amount of time that has passed since the previous FixedUpdate() frame ran. Now, since we know from above that FixedUpdate() runs at a constant frame-rate (50 by default) this value will more or less always be the same between frames
Time.deltaTime similarly returns the amount of time that has passed since the previous Update() frame ran. Now, since Update() does not run at a constant frame-rate, but is dependent on the user's machine, this value is almost always going to be a little different each frame.
So with all of this in mind, should this go in Update() or FixedUpdate()? And should it use Time.deltaTime or Time.fixedDeltaTime?
If I've done a decent job at explaining the differences between these things, then I would expect you to now know that it ought to go in Update() and use Time.deltaTime.
I do not know what that SmoothDamp.Float() method is doing exactly, but by lerping between two positions using Time.deltaTime as the t parameter, you will ensure that no matter what the frame-rate of the machine running this is, that it will be smooth and consistent.
Related
So my rigidbody controller has a speed limiter with the following code:
Vector3 flatVel = new Vector3(rb.velocity.x, 0f, rb.velocity.z);
// limit velocity if needed
if (flatVel.magnitude > moveSpeed)
{
Vector3 limitedVel = flatVel.normalized * moveSpeed;
rb.velocity = new Vector3(limitedVel.x, rb.velocity.y, limitedVel.z);
}
but for some reason that last line of code:
rb.velocity = new Vector3(limitedVel.x, rb.velocity.y, limitedVel.z);
causes my character's falling speed to reset when moving in midair, when it reaches about -50 units of speed. I've tried modifiying rb.velocity.x and rb.velocity.z seperately, but Unity doesn't allow that. Is there any other way to cap rigidbody speeds without using rb.velocity?
According to the Unity documentation here.
In most cases you should not modify the velocity directly, as this can result in unrealistic behaviour - use AddForce instead Do not set the velocity of an object every physics step, this will lead to unrealistic physics simulation. A typical usage is where you would change the velocity is when jumping in a first person shooter, because you want an immediate change in velocity.
I'd say this is the reason you are encountering the behaviour that you are.
A Unity forum post, found here, suggests the correct way to do this is:
The 'proper' way would be to apply a force in the opposite direction of the rigidbody's velocity. The amount of force should be proportional to the extent to which the rigidbody is exceeding its speed limit.
Another Unity forum post suggests to make use of the ClampMagnitude method, the post can be found here.
private void FixedUpdate()
{
var force = new Vector3(input.x, 0f, input.y) * acceleration;
var velocity = playerRigidbody.velocity + force;
playerRigidbody.velocity = Vector3.ClampMagnitude(velocity, maxSpeed);
}
Hope this helps.
My camera jitters on rotation, but not movement. This script is the only asset in a new project, and it's attached to the camera - it's supposed to be a free-flying camera. I added a few cubes into the environment so I could check it was moving correctly, and it stutters several times a second whenever it's rotated. Movement works just fine.
I've tried changing camera clipping planes, I've tried moving either movement or rotation or both into Update() instead, removing the default camera and adding my own, and since it's an otherwise empty project I'm showing over 500fps so don't have any performance issues - nothing has had any effect, for better or worse.
Any suggestions welcome! If someone has a minute to open up a default project and slap this script on the camera and make sure it's not something on my end, that would at least rule that out and I'd really appreciate it. Thanks in advance.
public class CameraController : MonoBehaviour
{
public Vector2 mouseSensitivity = Vector2.one;
public Vector3 moveSensitivity = Vector2.one;
public Transform camTransform;
Vector3 camEulers = Vector3.zero;
void Start()
{
camTransform = FindObjectOfType<Camera>().transform;
}
void LateUpdate()
{
KeyboardMovement();
LockedMouseRotation();
}
void KeyboardMovement()
{
camTransform.position += Input.GetAxis( "Vertical" ) * camTransform.forward * Time.deltaTime * moveSensitivity.y;
camTransform.position += Input.GetAxis( "Horizontal" ) * camTransform.right * Time.deltaTime * moveSensitivity.x;
}
void LockedMouseRotation()
{
camEulers.x -= Input.GetAxis( "Mouse Y" ) * mouseSensitivity.y * Time.deltaTime;
camEulers.y += Input.GetAxis( "Mouse X" ) * mouseSensitivity.x * Time.deltaTime;
camEulers.x = Mathf.Clamp( camEulers.x, -90.0f, 90.0f );
camTransform.rotation = Quaternion.Euler( camEulers );
}
}
Don't use Time.deltaTime here.
Your goal here is to convert measurements of linear distance (GetAxis("Mouse X") and GetAxis("Mouse Y")) into a measurement of angular distance (the angular distance you are using to adjust the camera's Euler angles). For that you only need a constant factor, which can be your mouseSensitivity components.
But GetAxis gets multiplied by Time.deltaTime in other places and works perfectly fine. Why do those work like that?
Your goal is not converting to distance from "throttle position" like one might from GetAxis("Horizontal") or GetAxis("Vertical") or GetAxis("customKeyButtonOrThrottleAxis") . That is a situation where you would want to multiply by a time (Time.deltaTime) to calculate a distance if you are using the position of the button or throttle as velocity. This is because distance = speed * time.
In some cases you might multiply by Time.deltaTime to calculate a velocity or speed if the button or throttle is to represent acceleration, because speed = acceleration * time.
Okay, so why doesn't Time.deltaTime work here?
When you use GetAxis("Mouse X") or GetAxis("Mouse Y"), you get a measurement of pixel difference in position since the last frame.
The typical way a mouse driven camera works in games is that moving the mouse the same distance in the same direction should move the camera the same way (assuming you don't move the camera all they way up or down of course).
So what that means is that multiplying by Time.deltaTime does here is often actually the opposite of what you might expect - moving the mouse the same distance moves the camera's rotation differently depending on how many frames that movement occurred over and how quickly those frames were rendered.
The faster the frames were rendered (that means, a smaller Time.deltaTime), the less camera rotation the same mouse distance becomes. This results in unpredictable jitter especially when the very act of moving the camera can drastically change the framerate.
So how do I fix it then?
So, the only thing you need to do here is to remove the multiplication by Time.deltaTime:
camEulers.x -= Input.GetAxis( "Mouse Y" ) * mouseSensitivity.y;
camEulers.y += Input.GetAxis( "Mouse X" ) * mouseSensitivity.x;
A similar situation where you might want to include Time.deltaTime is if you want to calculate the speed or velocity of the mouse movement.
But, in that case, you would want to divide by Time.deltaTime. This is because speed = distance / time.
Vector2 mouseVelocity = Vector2.Scale(mouseSensitivity,
new Vector2(Input.GetAxis("Mouse X"),
Input.GetAxis("Mouse Y"))
) / Time.deltaTime;
In most cases, you probably don't want to be concerned with the speed/velocity of the mouse movement. But, for example, those interested in a super monkey ball type game using a track ball input might find this side bit useful!
I'm learning game development in Unity. I recently got struct to the Time.deltaTime function during the code i was learning from the tutorials. I have searched about it for better understanding but not learned the main purpose of using it as it is explained in a professional way. In short I want some easy explanation. So, I can understand from it.
Rendering and script execution takes time. It differs every frame. If you want ~60 fps, you will not have stable fps - but differing amounts of time passing each frame. You could wait if you are too fast, but you cannot skip rendering when you are slower than expected.
To handle different lenghts of frames, you get the "Time.deltaTime". In Update it will tell you how many seconds have passed (usually a fraction like 0.00132) to finish the last frame.
If you now move an object from A to B by using this code:
object.transform.position += Vector3.forward * 0.05f;
It will move 0.05 Units per frame. After 100 frames it has moved 5 Units.
Some pcs may run this game at 60fps, others at 144 hz. Plus the framerate is not constant.
So your object will take 1-5 seconds to move this distance.
But if you do this:
object.transform.position += Vector3.forward * 5f * Time.deltaTime;
it will move 5 units in 1 second. Independent of the framerate. Because if you have 10000 fps the deltaTime will be very very small so it only moves a very tiny bit each frame.
Note: In FixedUpdate() its technically not 100% the same every frame, but you should act like it's always 0.02f or whatever you set the physics interval to. So independent from the framerate, the Time.deltaTime in FixedUpdate() will return the fixedDeltaTime
edit:
Here is a nice comparison video I made:
please watch the video on imgur
Red Cube: transform.position += Vector3.right * (1f / 60f);
Green Cube: transform.position += Vector3.right * Time.deltaTime;
framerate is locked to 60 fps! (using Application.targetFrameRate = 60;
You can see, the green cube is hitting the right wall always at 5, 10, 15, 20, 25 ...
At first they look synchronized, but once I jiggle the window, some frames are dropped (may be an editor thing, but there are tons of reasons for frame-drops in a real game). The red cube never catches up to the green one, the dropped frames are never repeated.
That shows using deltaTime is a lot more consistent + it gives you a nice bonus: You can adjust the timescale using Time.timeScale = .5f; to make a slowmotion!
Time.deltaTime is simply the time in seconds between the last frame and the current frame.
Since Update is called once per frame, Time.deltaTime can be used to make something happen at a constant rate regardless of the (possibly wildly fluctuating) framerate.
So if we just say obj.transform.position += new Vector3(offset, 0, 0); in our update function then obj will move by offset units in the x direction every frame, regardless of FPS.
However if we instead say obj.transform.position += new Vector3(offset * Time.deltaTime, 0, 0); then we know that obj will move offset units every second as every frame it will move the fraction of offset corresponding to how much time that frame took.
As said here:
This property provides the time between the current and previous frame.
You can use this to check your framerate, for example. Also, as said here
If you want to move something at a constant velocity speed, for instance, multiply speed by Time.deltaTime and you will get exactly the distance moved by the object since last Update (remember: distance = velocity * time)
In my game I have targets that spawn and move towards the player in a straight line. I use this code:
transform.LookAt(target.transform);
transform.position = Vector3.MoveTowards(transform.position, target.transform.position, speed);
Which makes the target home in on the player and move towards them.
I decided to try and make them move in a sin wave, so it looks less boring than going in a straight line, and I added a line of code, which now becomes:
transform.LookAt(target.transform);
transform.position = Vector3.MoveTowards(transform.position, target.transform.position, speed);
transform.position += transform.right * Mathf.Sin (Time.time * 3f) * 1f;
Now this works great, my enemies move left and right as they travel towards the player, except, they all follow the same path at the same time, resulting in a large group of targets moving left and right in a sort of choreographed way.
I believe it is because I use Time.time in the Mathf.Sin piece of code, which results in all the targets using the same value to generate a sine wave.
I've tried changing to Time.deltaTime and changing the Time.time variable to a Random.Range variable between 2 numbers, but they either don't work, stop my targets from moving, or make them look like they are vibrating. I've also tried using animations as well, but because of my MoveTowards code, this doesn't work either.
So, is there a way to make a GameObject move left and right in a sine wave and move forwards at the same time, but independently so they do not look choreographed? :-)
In order to offset the phase of the sine function (while keeping the frequency and amplitude) you have to add a constant to x in sin(x).
So you could try transform.position += transform.right * Mathf.Sin (Time.time * 3f + i) * 1f; where i is unique to the target moving towards the player.
See also: https://en.wikipedia.org/wiki/Sine_wave
I've got an empty game object filled with all my player components, within it is a model which animates itself when the controls are pressed which are instantiated by GetButtonDown. Currently everything in the player game object moves instantly to the next point (basically teleports there with no transition). Is there a way I can get it all to move over say 1 second rather than going there instantly? Here's what happens on GetButtonDown
if (Input.GetButtonDown ("VerticalFwd"))
{
amountToMove.y = 1f;
transform.Translate (amountToMove);
}
I've tried transform.Translate (amountToMove * Time.deltaTime * randomint);
and all sorts of ways to use Time
however that doesn't seem to work even though it seems the most logical way. I'm guessing because the GetButtonDown only "runs" when it is pressed and has no update function to "time" the move every frame? Any ideas?
amountToMove is saved as a Vector3 aswell.
The first formula is wrong. The correct formula would be:
this.transform.position += (Distance/MoveTime) * time.deltatime
Try .Lerp, it runs as a coroutine interpolating a value over some amount of time Vector3.Lerp(Vector3 start, Vector3 end, float time)
See documentation here
This should give you a rough idea of whats going on
Vector3 Distance = End - Start;
// this will return the difference
this.transform.position += Distance/time.deltatime * Movetime
// this divides the Distance equal to the time.deltatime.. (Use Movetime to make the movement take more or less then 1 second
IE: If the Distance is 1x.1y.1z, and time.deltatime is .1 and Movetime is 1.... you get a movement of .1xyz per tick, taking 1 second to reach the end point
FYI: transform.Translate works as a fancy .position +=, for this purpose you can use them interchangeably
EDIT
Here are a few solutions
Easy ::
Vector3 amountToMove = new Vector3(0,1,0); // Vector3.up is short hand for (0,1,0) if you want to use that instead
if (Input.GetButtonDown ("VerticalFwd"))
{
transform.position = Vector3.Lerp(transform.position ,transform.position + amountToMove, 1);
}
Hard and probably unnecessary ::
Write a Coroutine that does a kind of Linear interpolation for your specific application See documentation here