разбить пространство на прямоугольники, назовём их нодами. В каждом ноде есть некоторое количество ваших прямоугольников. Если один ваш прямоугольников лежит в нескольких нодах, то его можно продублировать или разделить, если это возможно. При каждом игровом фрейме круг может максимально двигаться на v*t, где v - скорость круга, t - время одного игрового фрейма. Вот выбираем все ноды, пересекаемые лучом движения (толк будет, если перебирать не все ноды, а начинать с самых ближайших к кругу, а потом идти вдоль луча) и находящиеся на расстоянии не более v*t. А далее уже в порядке пересечения прямой движения нодов перебирать в этих нодах ваши прямоугольники. Подсчитываем новый вектор скорости после столкновения. Далее записать траекторию, вычислить время столкновения и отнять от t его. И повторить до t = 0. После отрисовать всё это. Изначальное t равно длине фрейма и равно времени прорисовки кадра. Т. е. так можно избежать всяких заторможений физики при низкой скорости отрисовки. И делать физику надо в отдельном потоке от рендера, при чём рендер рисует "сейчашний" кадр, а физик считает следующий.