Coroutine is causing uneven spacing between objects - c#

I have a Coroutine, and I have tried using WaitForSecondsRealtime and WaitForSeconds. When I run the Coroutine, the delay is the same for a few iterations, but then there is a gap where the delay is a little longer, then it goes back, then there is a delay again.
Here is an image of what is happening, as you can see there are a few gaps between circles, and I would like the gaps to be a bit more constant.
Here is the Coroutine that I am using for this:
public float shootDelay = 0.03f;
IEnumerator Shoot(Vector3 direction) {
direction.z = 0f;
foreach (GameObject ball in balls) {
float speed = ball.GetComponent<Ball>().speed;
ball.GetComponent<Ball>().rb.velocity = direction.normalized * speed;
yield return new WaitForSecondsRealtime(shootDelay);
}
}

It is hard to tell from your tiny bit of code exactly what your question is. A good Minimal, Complete, and Verifiable code example would help a lot (perhaps minus the standard Unity3d boilerplate…there's an art to providing just the right amount of code for a Unity3d question).
But is seems like you are trying to implement a delay between "balls" being "shot" from something. As such, you want for each ball to be initialized at precise intervals. They are not, which results in irregular intervals.
Unfortunately, I don't think you can count on the WaitForSecondsRealtime() (or WaitForSeconds(), for that matter…which is probably a better choice here, as it would automatically adjust if you change the game simulation speed). These functions have significant sources of imprecision that you will need to take into account, particularly that the time that's waited is counted only after the current frame is entirely complete and your coroutine is only resumed at the first frame after the specified time has elapsed.
One way to take into account this imprecision is by using the Time.time value to track how much simulation time has actually passed since you started firing the bullets. Initialize a variable in the coroutine just before the loop, to the current value. Then when you are setting the velocity of the bullet object, also set its position based on the velocity and the difference between the current time value and that bullet's intended time value. Then wait for a period of time not the exact interval you want, but an interval that represents the difference between the current time and the next bullet time.
Without a good MCVE it's not practical for me to try to test this (i.e., I haven't), but something like this should work:
IEnumerator Shoot(Vector3 direction) {
direction.z = 0f;
float nextBulletTime = Time.time;
foreach (GameObject ball in balls) {
Ball ballComponent = ball.GetComponent<Ball>();
float speed = ballComponent.speed;
Rigidbody rb = ballComponent.rb;
rb.velocity = direction.normalized * speed;
rb.position += rb.velocity * (Time.time - nextBulletTime);
nextBulletTime += shootDelay;
yield return new WaitForSeconds(nextBulletTime - Time.time);
}
}

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.

Rigidbodies going inside each other in Unity

How do I solve this problem?
Details:As you can see from the game view, the gems are dropping from the right-top of the screen. When they fall down fast and hit each other with the similar rotation, they go into each other.
That gem doesnt look too bad to me. collissions can also go through objects if they are very fast.
Interpolation and continous mesh detection both increase the physics performance.
I'd recommend that you write 3 lines that increase physics performance when an object is going fast and switches it to normal when it is going slow. you can do a proximity test every frame to check for fast objects (physics velocity variable) to know what objects they approach, and make physics maximum for both fast and approached objects.
I'd use the same as you wrote, using the rigidbody velocity is perfect, except:
private void Update ()
{
var vel = rgd.velocity;
speed = vel.sqrMagnitude;//uses 5-20 times less processor power
if (speed >= 4) // it's the same
Magnitude uses square-root maths which is one of the most expensive ops on a processor. Being a hawk about instances of every frame square root and divisions is what all advanced programmers do to streamline their code.
If you had to find the velocity of objects which aren't using physics, then you'd have to delete their current position from the last frame position every frame, to find their velocity vector which would be the same as rigidbody.velocity using posA posB variables to recall the last frame... i.e. for objects without a rigidbody.
Here is the code I wrote, and I used this on my object.
bool fast;
Rigidbody rgd;
float speed;
private void Start ()
{
rgd = gameObject.GetComponent<Rigidbody>();
}
private void Update ()
{
var vel = rgd.velocity;
speed = vel.magnitude;
if (speed >= 2)
{
rgd.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
}
else
{
rgd.collisionDetectionMode = CollisionDetectionMode.Discrete;
}
}

XNA 2D game - behaves differently on friend's computer

I made a simple networking xna game using c# and lidgren. But velocity of objects are different on friend's computer - resolution of window is the same, fps are the same (both 60), application is the same, but he's character is just slower for some reason. For some time his velocity is equal to mine, but then it slows down again.
Other friends do have equal velocities as I do. What could be a problem? The program adjusts movement according to fps, so there is probably no problem in fps.
Without seeing your code its hard to say for sure.
The program adjusts movement according to fps
Well it's generally better to think of animation as a function elapsed since the last update rather than frames per second. That way, the animation will take the same amount of real world time to complete regardless of FPS whether it be fixed or variable update.
Movement deltas should take into account time elapsed since last game update not last/current FPS.
e.g.
public Vector3 Velocity;
public Vector3 Position;
public void Update(GameTime gameTime)
{
float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
// Apply acceleration
Vector3 acceleration = force / Mass;
Velocity += acceleration * elapsed;
// Apply psuedo drag
Velocity *= DragFactor;
// Apply velocity
Position += Velocity * elapsed;
.
.
.
You said you're using lindgren to make it multiplayer? Maybe it's rubberbanding due to latency. Try checking it on 2 computers on lan to see if it resolves the issue.
Possible there's different settings to lindgren to specify how to handle syncing or prediction.

Unity 3D Move object over specific time on GetButtonDown

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

Categories