Distance-based collision detection not accurate

I’m making a pretty simple mobile 2D puzzle game that requires a lot of collision detection between perfect circles. I had figured this would let me skip physics altogether, as they tend to bog down mobile projects, and detecting collisions between circles shouldn’t be difficult to accomplish without it.

Basically the user is able drag a circle around the screen via touch. At certain time intervals, a disc (also perfectly circular) is spawned and shot across the screen; when the user-controlled circle comes in contact with the disc, the disc sticks to the circle and becomes a part of it (basically, the disc is parented to the circle). Not complicated.

In my update function I’m checking the distance between the spawned disc and the closest child of the user-controlled object, all of which are circles. Here’s what it looks like:

var closest : float = 999;
	var circ : Transform;
	
	// Check for collision with main or children of main
	for (var circle : Transform in main)
	{
		// Get the distance from this child object to the child
		var nextDist = Vector3.Distance (circle.position, transform.position);
		
		// If it is closer than the previous one, update closest circle
		if (nextDist < closest)
		{
			closest = nextDist;
			circ = circle;
		}
		
		// If the circle and the closest transform overlap...
		if (closest <= ((transform.localScale.x / 2) + (circ.localScale.x / 2)))
		{
			// Stop movement and parent the circle to the main
			EndLife ();
		}
	}

It works pretty well, but there’s a problem - when the user moves the main object too fast, the collision detection seems to “lag”, meaning discs won’t detect a collision until they’re partly inside an object. If the player moves the main object REALLY fast, sometimes discs won’t detect anything at all and will just phase through the object, as if they never collided.

Is there a way around this? How is it possible that a distance check in the Update loop can be “slower” than the movement of an object that is also controlled through the Update loop? I feel like I’m misunderstanding something pretty fundamental here.

Any help would be greatly appreciated!

Well, keep in mind objects don’t really “move”. They always jump to their next position. The movement “speed” and the framerate determine how much the object will “jump” each frame.

For example, when you have a monitor 1600 pixel wide and you move your mouse quickly from the left side to the right side, the cursor won’t pass each of the 1600 pixels. It will probably stop / e visible at 5 to 30 positions in between even when it’s 30 that means each “update” the cursor moved ~50 pixels.

To prevent those big jumps you can increase the sample rate, so you would move the objects several times in one Update step to reach the new position. This will decrease the move step size. This is sometimes called oversampling or interpolation. However this requires a lot more cpu power.

The second solution is to calculate the trajectory of the two objects and calculate if and where they meet. It’s the search for the closest point of aproach. All those solutions are quite heavy to calculate.

The best fix is probably to do a dynamic oversampling so no movement gets larger then half of the smalles circle radius. That means when the user moved the mouse / touch very quickly the difference between the old position and the new position would be greater than the max allowed movement step. In this case you would run a while loop and interpolate between the old and the new position in smaller steps (our max allowed step size) until we reach the new postion.

There’s still a problem when you need oversampling for both objects. In this case you have to interpolate both in the same loop or it’s quite pointless.

edit
An example. If you have a movement code like this:

// C#
public float speed = 2000;
void Update()
{
    transform.position += transform.forward * speed * Time.deltaTime;
    CheckCollisions();
}

You would do something like this instead
:

// C#
public float speed = 2000.0f;
public float maxMovementStep = 2.0f;
void Update()
{
    float amount = speed * Time.deltaTime;
    while (amount > 0)
    {
        float step = Mathf.Min(maxMovementStep, amount);
        amount -= step;
        transform.position += transform.forward * step;
        CheckCollisions();
    }
}