2011-01-06 4 views
7

Я делаю игру, в которой шары отскакивают вокруг внутри гораздо большего круга. Большой круг не перемещается.Python & Pygame: столкновение шара с внутренностью круга

Вот код, который я сейчас использую для этих столкновений:

def collideCircle(circle, ball): 
    """Check for collision between a ball and a circle""" 
    dx = circle.x - ball.x 
    dy = circle.y - ball.y 

    distance = math.hypot(dx, dy) 

    if distance >= circle.size + ball.size: 
     # We don't need to change anything about the circle, just the ball 
     tangent = math.atan2(dy, dx) 
     ball.angle = 2 * tangent - ball.angle 
     ball.speed *= elasticity + 0.251 

     angle = 0.5 * math.pi + tangent 
     ball.x -= math.sin(angle) 
     ball.y += math.cos(angle) 

Он основан на прекрасном учебнике Питера Collingridge over here.

Объекты окружности и шара - оба класса, с (x, y), радиусом, углом и скоростью.

У меня возникают две проблемы, связанные с этим методом, однако:

  1. мяч отскакивает от (я подозреваю) является его «узловая точка», которая, как представляется, в верхнем правом углу круга.
  2. При столкновении с нижним 5% круга не удается отскочить достаточно высоко и, следовательно, «опускается» из экрана. Я предполагаю, что это происходит потому, что сильный удар не достаточно высок, чтобы переместить мяч выше его (неправильно расположен) «узловой точки»?

Просмотрев возможные решения, уже присутствующие здесь, в частности «Быстрое обнаружение столкновений по кругу» [Ссылка удалена из-за ограничения на спам-ссылки], которые, хотя в Java используют один и тот же метод, все они имеют дело с внешними столкновениями, в то время как я смотрю на подпрыгивание шара вокруг внутреннего круга.

Здесь также определение классов Бала() и Circle():

class Ball(): 
    def __init__(self, (x,y), size): 
     """Setting up the new instance""" 
     self.x = x 
     self.y = y 
     self.size = size 
     self.colour = (0,128,255) 
     self.thickness = 0 
     self.speed = 0.01 
     self.angle = math.pi/2 

    def display(self): 
     """Draw the ball""" 
     pygame.draw.circle(screen, self.colour, (int(self.x), int(self.y)), self.size, self.thickness) 

    def move(self): 
     """Move the ball according to angle and speed""" 
     self.x += math.sin(self.angle) * self.speed 
     self.y -= math.cos(self.angle) * self.speed 
     (self.angle, self.speed) = addVectors((self.angle, self.speed), gravity) 
     self.speed *= drag 

class Circle(): 
    def __init__(self, (x,y), size): 
     """Set up the new instance of the Circle class""" 
     self.x = x 
     self.y = y 
     self.size = size 
     self.colour = (236, 236, 236) 
     self.thickness = 0 
     self.angle = 0 # Needed for collision... 
     self.speed = 0 # detection against balls 

    def display(self): 
     """Draw the circle""" 
     pygame.draw.circle(screen, self.colour, (int(self.x), int(self.y)), self.size, self.thickness 

Спасибо заранее, Натан

ответ

12

Я рад, что вам понравился мой учебник. Мне нравится ваша вариация, она должна быть проще.

Во-первых, я думаю, что вам нужно изменить тест на столкновение с:

if distance >= circle.size - ball.size: 

Поскольку больше размер шара, тем меньше расстояние между его центром и центром круга может быть. Это должно заставить шарики отскакивать в нужном месте (внутри круга).

Тогда я думаю, вам просто нужно поменять знаки для x и y, и все должно работать.

ball.x += math.sin(angle) 
ball.y -= math.cos(angle) 

Чтобы переместить мяч на правильном расстоянии вы можете рассчитать перекрытие:

overlap = math.hypot(dx, dy) - (circle.size - ball.size) 

if overlap >= 0: 
    tangent = math.atan2(dy, dx) 
    ball.angle = 2 * tangent - ball.angle 
    ball.speed *= elasticity 

    angle = 0.5 * math.pi + tangent 
    ball.x += math.sin(angle)*overlap 
    ball.y -= math.cos(angle)*overlap 

Успехов

+0

Ось X не будет определяться косинусом угла и осью Y синусом? Или я ошиблась? : X – JCM

+0

Я думаю, вы правы, что это стандартный способ определения вещей, но пока вы согласны, на самом деле это не имеет никакого значения. Ясно, что если вы вращаете все на 90 градусов, это не должно влиять на то, как вещи отскакивают (при условии, что гравитация также изменяется). –

+0

@PeterCollingridge, ваши статьи * удивительные *!Элегантные решения, пифонический стиль, поздравления .. и спасибо! – MestreLion

2

Большинство графических пакетов используют верхние левый, как старт для рисования кода. Вы, скорее всего, хотите 2 набора координат, один из которых вы сталкиваетесь/перемещаете/etc с, а другой - для рисования (радиус x, y-радиус).

Кроме того, не задумываясь об этом, следует ли проверить пересечение distance + ball.size >= circle.size? Расстояние шаров от центра плюс его радиус должно быть меньше радиуса круга, если я правильно понял установку.

12

Не отвечая на ваш вопрос, я хотел бы прокомментировать вашу стратегию реализации и рекомендовать новый подход. Вы представляете скорость шара в полярной координатной форме, как ball.angle и ball.speed.

Я думаю, что это будет в целом неудобно для вас. Например, в вашем коллизионном коде вы вызываете atan2, чтобы превратить вектор (dx, dy) в угол, а затем вы вызываете sin и cos, чтобы снова вернуть угол обратно в вектор. (Также, если вы когда-нибудь попытаетесь обобщить свой код на три измерения, вы окажетесь в мире боли.) Итак, если у вас нет особых требований, требующих полярных координат, я рекомендую вам делать то, что делают все остальные, а именно представлять скорость шара в декартовых координатах как вектор (vx, vy).

Я также рекомендую изменить физический подход от static one ("является объектом A, который в настоящее время сталкивается с объектом B?") до динамический один (« будет препятствовать столкновению с объектом B во время следующего шага перемещения? »). В системе статической физики вы часто оказываетесь в объектах, пересекающихся друг с другом в конце шага движения, а затем вы должны выяснить, лучший способ, чтобы заставить их снова разделить, которое трудно получить право.

Если вы оба из них, это просто отскакивать мяч без тригонометрии.

Шаг 1 . Преобразование кругового/окружного столкновения в столкновение с точкой/окружением с использованием Minkowski addition:

Original problem at left shows small circle moving inside a large circle. Transformed problem at right shows point moving in circle whose radius is the difference between the radii of the circles in the original problem.

Шаг 2. Рассмотрим сегмент времени, в котором мяч начинается с p = (px, py) и перемещается на v = (vx, vy). Он пересекается с кругом? Вы можете использовать для этого standard line segment/circle test, за исключением того, что смысл теста отменяется.

Шаг 3. Найдите точку столкновения c = (cx, cy). Мяч отскакивает от круга так же, как он отскакивает от линии t касательной к кругу на этом этапе. Для круга, центрированного в начале координат, касательный вектор является просто (-cy, cx), и я уверен, что вы можете решить, как вычислить его для других кругов.

Figure described above

See this answer о том, как рассчитать новый путь шара на основе коэффициентов трения и реституции.

Шаг 4. Не забывайте, что мяч может все еще иметь некоторое расстояние для перемещения по новому вектору w. Если временной шаг достаточно большой или скорость достаточно высокая, он может снова столкнуться в течение одного и того же сегмента времени.

+0

Хотя это было не то, что я искал, спасибо отвалов! Я изучаю это. – nchpmn