sahaun's blog

By sahaun, history, 2 months ago, In English

Hello Codeforces!

As promised almost three years ago, I have finally found the time and scope to write about using CP techniques outside CP itself.

My first post is about a use case of binary search in video games. Unfortunately, the post is written in MDX (with videos and custom elements that Codeforces doesn't support), so I have to post a link if you want to read the whole post.

You can find it here: Continuous Collision Detection in Video Games using Binary Search

Here is a summary.


The Problem

In a standard game loop, objects "teleport" from position $$$(x, y)$$$ to $$$(x + \Delta x, y + \Delta y)$$$ in a single frame.

If a wall is thin enough, a fast-moving bullet can start on one side and end on the other in a single frame, completely skipping the collision check. This well-known bug is called tunneling.

w w2

To fix this, we need to check the swept volume — the continuous volume the object occupies during its movement from time $$$t_0$$$ to $$$t_1$$$.

e2

The problem is that we need to find the exact time of impact $$$t \in [t_0, t_1]$$$ where the object first touches the wall.

The Solution: Binary Search on Time

Let's define a function $$$f(t)$$$ such that:

  • $$$f(t) = 1$$$ if the object's swept volume from $$$t_0$$$ to $$$t$$$ intersects the obstacle.
  • $$$f(t) = 0$$$ otherwise.

Claim: If there is a collision in the swept volume during $$$[t_0, t]$$$, there will also be a collision in any later time interval $$$[t_0, t']$$$ where $$$t' \geq t$$$. This can be proven formally, but the intuition is simple.

Proof

Since the function is monotonic, we can Binary Search on the time $$$t$$$ to find the first moment of collision.

double find_collision_time(double t0, double t1, Object obstacle) {
  // Handle t0 separately - in case we are already colliding
  if (check_collision(t0, t0, obstacle)) return t0;

  // Handle (t0, t1]
  const double eps = clamp((t1 - t0) / this->speed, 1e-6, 1e-3);
  const int maxIterations = 1 + ceil(log2((t1 - t0) / eps));
  for (int i = 0; (i < maxIterations) && ((t1 - t0) > eps); ++i) {
    double mid = (t0 + t1) / 2.;
    if (check_collision(t0, mid, obstacle)) {
      t1 = mid;
    } else {
      t0 = mid;
    }
  }
  return t1;
}

The time complexity is $$$O(\log ((t_1 - t_0) / \epsilon))$$$ which becomes $$$O(\log S)$$$ when $$$\epsilon \approx 1 / S$$$.


Thanks for reading! Let me know if you use binary search for anything else.

  • Vote: I like it
  • +8
  • Vote: I do not like it