0
\$\begingroup\$

In my simulation, if an object is inside another object, it imparts a force proportional to the distance inside the object (basically a separation force).

I am having an issue though where the simulation runs differently given different deltaTime values.

I think the issue is due to collisions. For example, Let's say the physics loop updated very often and the deltaTime as a result, was very small: if a collision occurred, then it will impart a force. And then next frame the objects will still be colliding (since not much time has passed), so it will impart another force to separate them. Then finally on frame 3 the objects will be separated.

This is in contrast to the game running with a very low update count (and as a result a high deltaTime). If two objects are colliding, a force will be imparted, but because the deltaTime is so high, they'll already be separated by the next frame, so another collision force won't be imparted.

As a result, different total force values will be imparted onto the intersecting objects, due to the differences in deltaTime. I'm struggling to think of how to resolve this. Is this indeed the issue, and if so, how does one begin to resolve this? The only thing I could think of is to enforce collision is checked, say, 60 times per second. No more, no less. But then doesn't that defeat the entire purpose of using deltaTime? Because then the game is locked at a 60 hz update rate.

\$\endgroup\$
4

2 Answers 2

1
\$\begingroup\$

The solution

The gist of it is: in addition to setting the velocity of the objects, you also want to move them so the next tick they are not intercepting. You can now stop reading this answer.


Why is that the solution?

What happens is that the objects collided at some time between the last tick to the current one. That is, the instant of collision is a fraction of your delta time. So to simulate the full span of the delta time, you can imagine that the objects were moving, collided, and then began moving according to their bounce. So at the end of the delta time the position is also wrong and you should correct it.

Now, you need to make a trade-off between precision and performance. Using realistic logic to figure out where the object ends up will have an impact in performance that ultimately will mean you won't be able to handle as many objects.

Thankfully, we are making games, and games don't need to be realistic. So go ahead and simply move the objects so they are no longer intercepting.

The cheap approach is to figure out the distance between the object, and at what distance they should be to not intercept, and move them accordingly. However, this approach is infamous for causing troubles by pushing objects out the wrong direction.

An slightly less cheap approach - but also one that causes less troubles - considers their velocity, and moves them in the opposite direction they came from.


Other related problems

Too many bounces

Another issue related to delta time is too many bounces in the same tick. The issue is that when you push the object out so it is no longer intercepting, they end up intercepting something else. And then you solve that other collision, and it results in yet another collision and so on.

Have a hard cap on how many you will solve per object. It is better that at some point the physics engine gives up and leaves some objects intercepting than to kill the performance of the game.


Tunneling

Yet another issue related to delta time is tunneling. It happens when your objects are too fast (or you ticks are too long), so that they can leap from one side to the other without registering collisions.

The cheap approach for these is to enforce a maximum/terminal velocity. So that objects don't get to move so fast that they can tunnel through each other (having a maximum velocity will also help with the prior issue).

And, of course, you can increase the tick frequency.


About the expensive approaches

You want to find out what is the instant when the objects would have collided. Which you can do since you have the motion equation for them. To make it easier, consider the frame of reference of one of the objects and work there. In the frame of reference of an object, itself is static, and the other object is making the combined motion of both of them… And you want to find out when the distance between their surfaces is zero. Setup the equation and solve for time (which is trivial if they just have position and velocity. No so much if they have acceleration, friction, drag, dampening, etc…).

Once you have the instant they collided, you can compute where they would have been at that time, and place them there. Then compute the speed after the bounce. And if you are so inclined, compute how much they move for the remainder of the delta time. And that could lead to another collision…


And about tunneling, you could collide the shape the objects sweep during their motion (e.g instead of colliding circles, you collide a capsule that goes from the prior position of the circle to the current one). You would have to change your approach to discard possible collision early, because, as you can imagine, you cannot simply check for collision with objects in the same area where the object ended… You need to consider their velocity, and check for collision with objects in the areas along the motion. Search for continuous collision detection and continuous physics simulations for more details.

\$\endgroup\$
2
  • \$\begingroup\$ Hello! Thank you again for the long and insightful answer, but I'm not sure if I can do this approach. I have many, many objects. Like, potentially up to 500 circular shaped objects all clumped up right on top of each other. So they're often overlapping slightly. And one getting hit may cause a chain reaction of forces and collision resolution on many different objects, which in turn affect other objects, etc. Hmm, I'll have to think about it some more. But I'm going to keep studying your answer in the interim. \$\endgroup\$ Apr 20, 2022 at 11:46
  • \$\begingroup\$ @Ryan "...all clumped up right on top of each other. So they're often overlapping slightly. And one getting hit may cause a chain reaction of force..." — getting that kind of system to behave well is a hard problem. If you're not doing this to learn about that problem, you should try using existing physics (and collision) engines instead of writing your own. \$\endgroup\$
    – Kevin Reid
    Apr 20, 2022 at 18:20
1
\$\begingroup\$

Since I realized that physics are very hard and I wanted it to be perfectly identical on everyone's machine, I just went with a fixed physics update loop (runs at 60 hz), but with lerping every frame so the movement is smooth on high refresh rate monitors.

I based the implementation off this article and also used the alpha value at the bottom to interpolate movement.

That is, for each of the objects that moves, I have their current position and previous position as member variables for their class. Then when I set their new position, I set their previous position right before updating the new position.

Then in my rendering function I use the alpha value from the article:

const alpha = accumulator / dt;

And then render the position of entities based on that:

function lerp(a: number, b: number, t: number) {
  return (1 - t) * a + b * t;
}

const renderX = lerp(e.prevPos.x, e.pos.x, alpha);
const renderY = lerp(e.prevPos.y, e.pos.y, alpha);

renderEntity(e, renderX, renderY);
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .