Background jitter with v-sync off in standalone build (high fps/300+ fps) - c#

So the last 2 months I had been reworking my PlayerController to be rigidbody based for the sake of having more interactions with rigidbodies i.e. pushing objects. This went all pretty well, until I started testing my game in a standalone build. I immediately noticed strange jitter/stutter in the background (the player runs smooth) when v-sync is turned off (with v-sync is turned off my computer runs the game at 300fps+). At first I thought it had something to do with Cinemachine, I'm using CinemachineFreeLook, but to be honest as of now I'm unsure if Cinemachine is what's causing the jitter/stutter. Something worth noting is that the jitter/stutter is not constant, while the fps seems constant the jitter/stutter might go away for like 20 seconds and come back after. I have obviously done alot of research the past week and have been reading plenty of posts about FixedUpdate vs. Update, but unfortunately without any luck.
I tried to add as much information as possible to this post to give a good understanding of my project, but first the stuff I already tried;
Changing the Fixed Timestep from 0,02 to 0,01. (This kind of works,
but I would like to prevent changing the Fixed Timestep)
Turning on interpolate/extrapolate. (Now there is jitter with both
v-sync turned on and off)
Changing the CinemachineBrain Update Method to FixedUpdate/LateUpdate
instead of SmartUpdate.
Deconstructing the scene and removing some heavy on performance stuff
like Volumetric Lighting and Planar Reflections.
Creating a really barebones PlayerController with only the code to
move the player.
Removing the library folder of my project. (Read this somewhere
online, but no luck)
Turning of my Xbox One controller, to make sure there is only one
input in use.
Swithed the building from Windowed Fullscreen to Exclusive
Fullscreen.
Turned off one monitor while testing.
Move all my code to FixedUpdate.
Added these 3 scripts and changed the Script Execution Order
(Couldn't get this to work)
Put the input logic in FixedUpdate.
Below you can see both my movement scripts, one is part of my actual PlayerController, while the other was the one I wrote for the barebones Player.
void FixedUpdate() {
_isGrounded = Physics.CheckSphere(_groundChecker.position, GroundDistance, Ground, QueryTriggerInteraction.Ignore);
_inputs = Vector3.zero;
_inputs.x = Input.GetAxis("Horizontal");
_inputs.z = Input.GetAxis("Vertical");
_inputs.Normalize();
if (_inputs != Vector3.zero)
transform.forward = _inputs;
_body.MovePosition(_body.position + _inputs * Speed * Time.fixedDeltaTime);
}
This is the script I wrote for the barebones player controller.
void FixedUpdate() {
if (pushControllerScr._pushBlocker) {
input = Vector3.zero;
} else {
input = new Vector3(InputManager.MainHorizontal(), 0, InputManager.MainVertical());
}
RunAxis();
inputDir = input.normalized;
inputDir.Normalize();
// Makes the player able to move, while not landing or being dead
if (!landState && !climbManagerScr.isClimbing) {
Move(inputDir, running);
}
}
void Move(Vector3 inputDir, bool running) {
if (inputDir != Vector3.zero) {
float targetRotation = Mathf.Atan2(inputDir.x, inputDir.z) * Mathf.Rad2Deg + cameraT.eulerAngles.y;
transform.eulerAngles = Vector3.up * Mathf.SmoothDampAngle(transform.eulerAngles.y, targetRotation, ref turnSmoothVelocity, GetModifiedSmoothTime(turnSmoothTime));
}
float targetSpeed = ((running) ? runSpeed : movementSpeed) * inputDir.magnitude;
stateInfoBaseLayer = _anim.GetCurrentAnimatorStateInfo(0);
if (stateInfoBaseLayer.IsTag("Climbing") || stateInfoBaseLayer.IsTag("Standing")) {
currentSpeed = 0f;
} else {
currentSpeed = Mathf.SmoothDamp(currentSpeed, targetSpeed, ref speedSmoothVelocity, GetModifiedSmoothTime(speedSmoothTime));
}
if (stateInfoBaseLayer.IsTag("AirIdle") && currentSpeed > walkSpeed) {
currentSpeed = walkSpeed;
}
velocity = transform.forward * currentSpeed + Vector3.up * _rb.velocity.y;
if (_capsuleCol.enabled) {
_rb.MovePosition(_rb.position + velocity * Time.deltaTime);
}
}
This is what I actually use in my PlayerController Script
Below you can see some footage of the jitter/stutter. It was quite hard to capture it, since using any recording software like OBS dropped the fps in my game to a point where the jitter/stutter was disappearing. Sorry for the phone recording, but this was honestly the only way.
Footage of my game showcasing the jitter/stutter especially noticable around the yellow ship.
And finally here's an album with some relevant settings in the inspector. Imgur Album

Looking at your recording, it looks as if the player is smooth and everything else.
From my experience 99% of the time means the jitter comes from the camera not being synced up correctly to the player.
It seems you have done the right things, and usually combining the following should work fine:
CinemachineBrain should update on FixedUpdate
Use interpolate/exrapolate on your player's Rigidbody (we chose extrapolate)
Apply forces/changes to physics objects solely in FixedUpdate
If you do all 3 together, this should work smoothly regardless of vsync, frame rate, fixed delta time, etc
Obviously your specific project might have something else that's messing it up; but I've seen this issue countless times and usually these combined steps should solve it.

Related

C#/Unity Camera Follow Jitter due to Time.deltaTime

Game: In a simple 2D Portrait Game made in Unity, I have a GameObject (Player) that has a fixed location and which is moving upwards. The Camera follows the Player and animated Obstacles are spawning from time to time moving left to right. The attached Screenshot shows the Scene.
The Problem:
The Movement is not smooth, as it seems like the Player is jittering. I think I already identified one of the causes: Big variation of Time.deltaTime. Average value is 0.0167, but I had variations. Minimum was 0.00177, maximum value was 0.2249519.
Settings:
Target Framerate is 60. I use Unity 2019.4.2f1 and as build target an iPhone X with iOS 14.2.
Scripts
public class Player: MonoBehaviour
{
float speed = 5f;
void Update()
{
transform.Translate(0,speed*Time.deltaTime,0);
}
}
public class CamFollow : MonoBehaviour
{
public Transform Player;
private Vector3 FollowVector;
void LateUpdate()
{
FollowVector = Player.position - new Vector3(0, -4.0f, 10);
transform.position = Vector3.Lerp(transform.position, FollowVector, Time.deltaTime * 4f);
}
}
Note: I need to use Lerp, because the Player may lower or increase the speed for one second, then the camera gently moves to the new position, before changing back. For the Obstacles I don't have a Script. They are moving, by using the Animation Component. For the Obstacles I only loop a change of the x value of the position.
My alternative solutions:
1. Changing the value for Time.deltaTime to a constant value of 0.01666667f:
void Update()
{
transform.Translate(0,speed*0.01666667f,0);
}
This makes the Player Object jitter a lot in the Unity Editor but only a little on the device
2. Using Fixed Update both for the Camera Follow and the Player Movement
This makes the movement and camera follow perfectly smooth, but the animated objects jitter a lot. I know Unity wants to adress the deltaTime issue in one of the next updates. But there should be a solution for my problem, so did anybody have a similiar problem, which could be solved? I prefer the 2nd alternative, because the movement looked really smooth and nice, so can I somehow make the animation part of "fixedUpdate"?
The variation in the 'deltaTime' is to be expected.
The variation is large on the PC because you are running on a complex computer with a complex operating system and lots of other applications running simultaneously, each with a multitude of threads, which every once in a while want to do some work. Thus, the scheduler of the operating system cannot guarantee that you are going to get a time slice at the precise moment that you want it in order to render your next frame.
The variation is smaller on the mobile device because it is a much simpler machine with a lot less going on, so the scheduler is able to give you time slices close to the precise intervals that you are asking.
You are already taking this variation into account when you do
transform.Translate( 0, speed * Time.deltaTime, 0 );
This is a fundamental technique in game development: the frame rate is never constant, so the distance by which you must move an object on each frame depends on the precise amount of time elapsed between this frame and the previous frame.
So, that part is fine.
Your problem lies in
transform.position = Vector3.Lerp( transform.position, FollowVector, Time.deltaTime * 4f );
Here you are passing Time.deltaTime * 4f for parameter t of Vector3.Lerp(). I have no idea what you are trying to accomplish, but the number you need to pass there needs to be a gradual transition between 0 and 1, and instead you are passing a randomly varying number of seconds multiplied by some magic constant 4. This does not look correct.
A couple of options that I can think of:
Always use 0.5 for t so that the camera always rushes to the right position and then slows down as it gets closer to it.
Calculate a separate speed vector for the camera, then move the camera using a translation just as you do for the player.

Unity3D C# - speed issues with `Input.GetMouseButton(0)` [SOLVED]

I've been struggling with the movement of my player since its movement was based on updating its world position with time frames. Using Update or FixedUpdate didn't change anything.
Since, I setted up another movement which involved a lot of changes in my code, this time based on speed (* Time.delaTime) and Update make my player moving faster when the mouse button is kept down, but FixedUpdate fix the problem and whatever if I just clic or keep the mouse down my player keep the same speed.
Code:
void FixedUpdate()
{
if(Input.GetMouseButton(0))
{
Vector3 target = GetMouseWorldPosition();
transform.position = Vector3.MoveTowards(transform.position, target , speed * Time.deltaTime);
Good luck and have fun :)
Issue 1
As you are already using Coroutine, you can use yield return new WaitForSeconds (1) instead of yield return null. you can adjust the number to set the wait duration between steps. Don't purposely slow down your CPU to achieve anything about timing. And don't mess up with Fixed Update time it's for physics simulation.
Issue 2
change it to yield return new WaitForSeconds (Input.GetMouseButton(0)?.5f:1);, so it waits shorter time if mouse button is held.
Issue 3
if(pathfinding==null || grid=null){
...(do random movement)
}else{
...(do pathfinding movement)
}
EDIT 1
if you don't want teleporting, you need to write transition yourself.
Vector3 oldPos = transform.position;
Vector3 newPos = nodeWaypoint;
float time = 0;
while(time<1){
yield return null;
time +=Time.deltaTime;
transform.position = Vector3.Lerp(oldPos,newPos,time);
}
Or just use DoTween
but TBH, your coding is quite problematic. it will keep doing pathfinding and calling UpdatePosition when the player holding click.

Unity - Run a loop like sonic without falling down

I am making this 2D platform game for a freelance, and I have to do a sonic mechanic, I have to make the character run through a full loop without falling down, I realised that to do this first I would have to rotate the character conform he runs through the loop, but the second part is where I am stuck.
So, basically, how can I make the character run the loop without falling down.
private void OnCollisionEnter2D(Collision2D coll)
{
Vector3 collRotation = coll.transform.rotation.eulerAngles;
if (coll.gameObject.tag == "Ground")
{
//in this part i rotate the player as he runs through the loop
transform.eulerAngles = new Vector3(transform.eulerAngles.x, transform.eulerAngles.y, collRotation.z);
//this is the part that i am stuck, trying to figure out how to make the character stay in the loop without falling, i tried collision detection,
//i tried raycasting but nothing seems to work
if (IsGrounded)
{
GetComponent<Rigidbody2D>().AddForce(Vector2.down, ForceMode2D.Impulse);
}
}
}
The trick that is most commonly used for remakes and similar is getting the speed of the player character on entry.
// Feel free to adjust this to whatever works for your project.
const float minimumAttachSpeed = 2f;
// This should be your characters current movement speed
float currentSpeed;
// You need a Rigidbody in this example, or you can just disable
// any custom gravity solution you may have created
Rigidbody2D rb;
If the speed of the character is excelling the minimum attachment speed, you can then allow them to follow a predefined path at that speed.
bool LoopAttachmentCheck()
{
return minimumAttachSpeed <= currentSpeed;
}
And now you can check to see if you're moving fast enough! I assume for this example you are using a Rigidbody2D...
(this check should only be run when you're either entering or currently on the loop)
void Update()
{
if( LoopAttachmentCheck() )
{
// We enable this to prevent the character from falling
rb.isKinematic = true;
// Here you write the code that allows the character to move
// in a circle, e.g. via a bezier curve, at the currentSpeed.
}
else
{
rb.isKinematic = false;
}
}
It's up to you to implement the actual rotational behaviour. If I were you, a good way to do this would (assuming it is a perfect circle) use RotateAround from the center of the circle.
If you have more complex shapes, you can use waypoints for movement, and just iterate through them at your speed.
The moment you fail to keep up speed, your character will fall off (so if a player decided to stop running for example) and the Rigibody2D will become kinematic.
Hope this helps!

Unity Google VR - getting the rotation, yaw, and pitch values from android device

I'm working on converting a simple project of mine into a VR game. Currently, it's just a motorcycle that will zoom forward, and the player can use A or D (or left and right) to steer the cycle.
Here's my code so far:
void Start () {
cycleSphere = GetComponent<Rigidbody>();
}
void Update () {
inputX = Input.GetAxis("Horizontal");
}
void FixedUpdate () {
if (currentSpeed < maxSpeed)
currentSpeed += acceleration;
if (currentSpeed > 0)
cycleSphere.transform.Rotate (0, inputX, 0);
cycleSphere.velocity += transform.forward * currentSpeed;
}
I've imported GoogleVR SDK v1.70.0 and added the prefab, which allows me to view the game on my phone in VR and even look around without touching the code.
I want to make it so that if the player tilts his head left or right, the cycle will turn in the respective direction. How do I do that? Do I use Input.Gyro?
I've done something similar to this before, I made Pacman in Virtual Reality where tilting your head would steer the player (As in, tilting your head to the left so that your ear touches your shoulder, would steer the player, and vice versa).
I did it using Input.acceleration
For example, you can do something as simple as this:
void Update()
{
transform.Rotate(0f, Input.acceleration.x * acceleratorSensitivity, 0f);
}
Basically, you take the X rotation of your device (which would be your head tilting), apply your sensitivity (higher sensitivity means faster turning), and rotate your transform accordingly! :-)
I also think this approach is better than using the gyroscope as not all devices contain a gyroscope, but the majority of devices contain accelerometers (which at its basic level, rotates the screen).
You can use the main camera's transform.eulerAngles like so:
private float angle;
Update() {
angle = transform.eulerAngles.z;
}
This gives you the number of degrees of tilt, where a low value means tilting to the left, and a high (~360) value means tilting to the right.
In my testing just now it seems to give values of up to +-10 depending on where you look, even without any rotation.

How to apply force to an object in a particular direction in Unity

I'm using unity to develop my game. I've made a custom swipe gesture by calculating the startPosition and the end Position of the touch. I got the vector direction by subtracting the two positions and debugged it successfully.
But when it comes to applying force to my game object I'm not getting any success after trying a lot.
here's what I'm doing:
swipeDirection = new Vector3(endPos.x - startPos.x,0f, endPos.z - startPos.z);
swipeDirection.Normalize();
Debug.Log("The direction is "+ swipeDirection);
ball.rigidbody.AddForce(swipeDirection * 5);
where ball is just a GameObject. Whenever I run it on my iPhone, the game just gets stuck giving a EXC_BAD_ACCESS code after the first swipe.
So I just tested your code. And it works. This issue you may be having is that those number are pretty small most likely, and you need a LOT of force to move it noticeably. Try changing your 5 to a 1000 or something big so you can definitely see the change. Here is my code, just added to a ball with rigidbody.
void Start ()
{
Vector3 dir = new Vector3 (100f, 0f, 0f);
dir.Normalize ();
this.gameObject.rigidbody.AddForce (dir * 100);
}
almost identical. you just need to make the multiplier big enough to see.
good luck.

Categories