Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python & Pygame: Ball collision with interior of circle

I'm making a game in which balls bounce around the inside of a much larger circle. The larger circle doesn't move.

Here's the code that I'm currently using for these collisions:

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)

It is based on the wonderful tutorial by Peter Collingridge over here.

The circle and ball objects are both classes, with (x,y), radius, angle and speed.

I am having two problems with this method, however:

  1. The ball bounces from (what I suspect) is its "anchor point", which seems to be in the top right corner of the circle.
  2. When colliding with the bottom 5% of the circle, is fails to bounce high enough and therefore "sinks" out of the screen. I am guessing that this is because the bounce is not high enough to move the ball above its (incorrectly placed) "anchor point"?

Having looked at possible solutions already on here, notably "Fast circle collision detection" [Link deleted due to spam link limit], which, although in Java is using the same method, these all deal with external collisions, whereas I am looking at bouncing a ball around the interior of a circle.

Here is also the class definitions of the Ball() and the 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

Thanks in advance, Nathan

like image 297
nchpmn Avatar asked Jan 06 '11 09:01

nchpmn


People also ask

What is Python used for?

Python is a computer programming language often used to build websites and software, automate tasks, and conduct data analysis. Python is a general-purpose language, meaning it can be used to create a variety of different programs and isn't specialized for any specific problems.

Is Python easy to learn?

Python is widely considered among the easiest programming languages for beginners to learn. If you're interested in learning a programming language, Python is a good place to start. It's also one of the most widely used.

What are basics of Python?

Python has a simple syntax similar to the English language. Python has syntax that allows developers to write programs with fewer lines than some other programming languages. Python runs on an interpreter system, meaning that code can be executed as soon as it is written. This means that prototyping can be very quick.

Which software is used for Python?

PyCharm, a proprietary and Open Source IDE for Python development. PyScripter, Free and open-source software Python IDE for Microsoft Windows. PythonAnywhere, an online IDE and Web hosting service. Python Tools for Visual Studio, Free and open-source plug-in for Visual Studio.


2 Answers

Without answering your question, I'd like to comment on your implementation strategy and recommend a new approach. You represent the velocity of the ball in polar coordinate form, as ball.angle and ball.speed.

I think that this is going to be generally inconvenient for you. For example, in your collision code you end up calling atan2 to turn the vector (dx, dy) into an angle, and then you call sin and cos to turn the angle back into a vector again. (Also, should you ever try to generalize your code to three dimensions, you will find yourself in a world of pain.) So, unless you have particular requirements that necessitate polar coordinates, I recommend that you do what everyone else does, namely represent the velocity of the ball in Cartesian coordinates as the vector (vx, vy).

I also recommend changing your physics approach from a static one ("is object A currently colliding with object B?") to a dynamic one ("will object A collide with object B during its next movement step?"). In a static physics system you often end up with objects intersecting each other at the end of a movement step, and then you have to figure out the best way to get them to separate again, which is hard to get right.

If you do both of these, it is straightforward to bounce the ball without any trigonometry.

Step 1. Transform circle/circle collision into point/circle collision using 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.

Step 2. Consider a time segment in which the ball starts at p = (px,py) and moves by v = (vx,vy). Does it intersect with the circle? You can use a standard line segment/circle test for this except that the sense of the test is reversed.

Step 3. Find the point of collision c = (cx,cy). The ball bounces off the circle in the same way as it would bounce off the line t tangent to the circle at this point. For a circle centred at the origin, the tangent vector is just (−cy,cx) and I'm sure you can work out how to compute it for other circles.

Figure described above

See this answer for how to calculate the new path of the ball based on coefficients of friction and restitution.

Step 4. Don't forget that the ball may still have some distance to move along the new vector w. If the time step is large enough or the velocity high enough it may collide again during the same time segment.

like image 199
Gareth Rees Avatar answered Nov 09 '22 22:11

Gareth Rees


I'm glad you liked my tutorial. I like your variation, it should actually be simpler.

First, I think you need change the test for collision to:

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

Because the larger the ball size, the smaller the distance between its centre and the centre of the circle can be. This should make the balls bounce at the right place (inside the circle).

Then I think you just need to swap the signs for the x and y and everything should work.

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

To move the ball by the correct distance you can calculate the overlap:

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

Good luck

like image 35
Peter Collingridge Avatar answered Nov 09 '22 23:11

Peter Collingridge