I have coded an animation (in python) for three beach balls to bounce around a screen. I now wish to have them all collide and be able to bounce off each other. I would really appreciate any help that can be offered.
import pygame
import random
import sys
class Ball:
def __init__(self,X,Y):
self.velocity = [1,1]
self.ball_image = pygame.image.load ('Beachball.jpg'). convert()
self.ball_boundary = self.ball_image.get_rect (center=(X,Y))
self.sound = pygame.mixer.Sound ('Thump.wav')
self.rect = self.ball_image.get_rect (center=(X,Y))
if __name__ =='__main__':
width = 800
height = 600
background_colour = 0,0,0
pygame.init()
window = pygame.display.set_mode((width, height))
pygame.display.set_caption("Bouncing Ball animation")
num_balls = 3
ball_list = []
for number in range(num_balls):
ball_list.append( Ball(random.randint(10, (width - 10)),random.randint(10, (height - 10))) )
while True:
for event in pygame.event.get():
print event
if event.type == pygame.QUIT:
sys.exit(0)
window.fill (background_colour)
for ball in ball_list:
if ball.ball_boundary.left < 0 or ball.ball_boundary.right > width:
ball.sound.play()
ball.velocity[0] = -1 * ball.velocity[0]
if ball.ball_boundary.top < 0 or ball.ball_boundary.bottom > height:
ball.sound.play()
ball.velocity[1] = -1 * ball.velocity[1]
ball.ball_boundary = ball.ball_boundary.move (ball.velocity)
window.blit (ball.ball_image, ball.ball_boundary)
pygame.display.flip()
Collision detection for arbitrary shapes is usually quite tricky since you have to figure out if any pixel collides.
This is actually easier with circles. If you have two circles of radius r1 and r2, a collision has occurred if the distance between the centers is less than r1+r2.
The distance between the two centers (x1,y1) and (x2,y2) can be calculated and compared as:
d = sqrt((y2-y1) * (y2-y1) + (x2-x1) * (x2-x1));
if (d < r1 + r2) { ... bang ... }
Or, as jfclavette points out, square roots are expensive so it may be better to calculate using just simple operations:
dsqrd = (y2-y1) * (y2-y1) + (x2-x1) * (x2-x1);
if (dsqrd < (r1+r2)*(r1+r2)) { ... bang ... }
The tricky bit comes in calculating the new movement vectors (the rate at which (x,y) changes over time for a given object) since you need to take into account the current movement vectors and the point of contact.
I think as a first cut, you should just reverse the movement vectors to test if the collision detection works first.
Then ask another question - it's better to keep individual questions specific so answers can be targeted.
Detecting a collision is only the first step. Let's break that down.
The fastest thing to do is calculate their square bounding boxes and see if those collide. Two of the sides need to cross (top of 1 and bottom or 2, and left of 1 and right of 2, or vice versa) in order for the bounding boxes to overlap. No overlap, no collision.
Now, when they do overlap, you need to calculate the distance between them. If this distance is more than the sums of the radii of the balls, then no collision.
Okay! We have two balls colliding. Now what? Well, they have to bounce off each other. Which way they bounce depends on a few factors.
The first is their elasticity. Two rubber balls bouncing off each other rebound differently than two glass balls.
The second is their initial velocity. Inertia states that they'll want to keep going in mostly the same direction they started in.
The third is the mass of the balls. A ball with smaller mass will rebound off a much larger mass with a higher velocity.
Let's deal with the second and third factors first, since they are intertwined.
Two balls will rarely hit exactly dead on. Glancing blows are far more likely. In any case, the impact will happen along the normal of the tangent where the balls collide. You need to calculate the vector component of both along this normal given their initial velocities. This will result in a pair of normal velocities that both balls will bring to the collision. Add up the sum and store it somewhere handy.
Now we have to figure out what each ball will take away from it. The resulting normal velocity of each ball is inversely proportional to the given ball's mass. That is to say, take the reciprocal of each ball's mass, add both masses together, and then parcel out the resultant normal velocity away from the collision based on the ratio of the ball's mass to the sum of the reciprocal of both ball's masses. Then add the tangential velocity to this, and you get the resultant velocity of the ball.
Elasticity is mostly the same, except it requires some basic calculus due to the fact that the balls are still moving even as they compress. I'll leave it to you to find the relevant math.
Detecting collisions was covered well by Pax's answer. With respect to having objects bounce off one another, I suggest checking out the following links concerning elastic collisions, inelastic collisions, and coefficients of restitution.
EDIT: I just noticed that this was covered in another SO question, albeit not specifically for Python. You should also check there for some good links.
I think there is somehthing simpler that you guys are missing espeically considering he's using pygame.
Calling the get_rect function can set probably boundraies for the images and Rect that is created, is used for calculating the position of the image and if there are more than one object in the animation, it can be used for detecting collisions.
colliderect & rect can be used, problem is i have no idea how you would implement it especially for an unkown number of balls.
Keeping in mind it's python.
Back in the good old times when CPU cycles were a premium coders used a simple trick to detect collision: they used such colours that they could tell from the pixel colour if it was background or an object. This was done on at least some C64 games.
Don't know if you are willing to go this route, though..
First you need to check collision with rect.colliderect(other_rect)
after that if they are colliding, you can check pixel perfect collision. So you don't mess with object's radius or shape.
For pixel perfect collision checking, I use Masks:
Make both mask objects with mask.from_surface, then put them to Mask.overlap function.
I made a python collision detection if statement, here it is:
if beach ball 1 x < beach ball 2 x + beach ball 1 width and beach ball 1 x + beach ball 2 width > beach ball 2 x and beach ball 1 y < beach ball 2 y + beach ball 1 height and beach ball 2 height + beach ball 1 y > beach ball 2 y:
#put needed code here
In your case, with 3 bouncing balls, you will have to make 2 if statements of this format for each ball to make sure that the collision detection is flawless. I hope this helps.
Related
So I'm making a game and I've got some help from another post to make bullets fly towards the mouse cursor. The original person who showed me this did explain it to me and I have a rough idea what it does but I didn't quite understand it. So I'm posting here for further explanation.
def Shoot(self):
pos = self.rect.centerx, self.rect.centery
mpos = py.mouse.get_pos()
direction = py.math.Vector2(mpos[0] - pos[0], mpos[1] - pos[1])
direction.scale_to_length(10)
return Bullet(pos[0], pos[1], round(direction[0]), round(direction[1]))
Edit: well I know what it does I just don't how I do it. I know It allows for projectiles to a fly towards the mouse even on diagonals but I don't know how it does it.
Whats happening is your getting the position of the cube/player with pos.
mpos is the mouse position on the screen
direction gets the direction between the player and the mouse. for example it the direction could be 10 pixels down and 100 pixels to the right.
The next line scales the direction down to 10, so instead of moving 100 pixels right and 10 down, its close to about 1 down and 10 right (not exactly but pretty close)
The last line creates the bullet with the x position, y position, x speed, y speed. rounding the speed as i said above, its not exactly 1 down and 10 right, it will be some decimal so to make it a nice number, you round it
I've tried to explain that in the answer to your previous question (Im currently making a game with pygame and Ive run into an Issue.), but I'll try it again.
The instruction
direction = py.math.Vector2(mpos[0] - pos[0], mpos[1] - pos[1])
Computes the distance from the point pos (A) to the point mpos (B) along the x-axis and y-axis. Such a tuple of axis aligned distances is called Vector:
At this point the Euclidean distance from point A to point B is unknown.
In the following the vector is scaled to a length of 10, by the operation pygame.math.Vector2.scale_to_length:
direction.scale_to_length(10)
That means that the x and y component of the vector is changed in that way (xd, yd), that the Euclidean length of the vector is 10 (d = 10):
If the components of the vector are added to the components of the point A, once per frame, then the point A steps towards the point B (A1, A2, ...):
I am working on a neural network based on the NEAT algorithm that learns to play an Atari Breakout clone in Python 2.7, and I have all of the pieces working, but I think the evolution could be greatly improved with a better algorithm for calculating species fitness.
The inputs to the neural network are:
X coordinate of the center of the paddle
X coordinate of the center of the ball
Y coordinate of the center of the ball
ball's dx (velocity in X)
ball's dy (velocity in Y)
The outputs are:
Move paddle left
Move paddle right
Do not move paddle
The parameters I have available to the species fitness calculation are:
breakout_model.score - int: the final score of the game played by the species
breakout_model.num_times_hit_paddle - int: the number of times the paddle hit the ball
breakout_model.hits_per_life - int: the number of times the paddle hit the ball per life, in the form of a list; e.g. first element is the value for the first life, 2nd element is the value for the 2nd life, and so on up to 4
breakout_model.avg_paddle_offset_from_ball - decimal: the average linear distance in the X direction between the ball and the center of the paddle
breakout_model.avg_paddle_offset_from_center - decimal: the average linear distance in the X direction between the center of the frame and the center of the paddle
breakout_model.time - int: the total duration of the game, measured in frames
breakout_model.stale - boolean: whether or not the game was artificially terminated due to staleness (e.g. ball gets stuck bouncing directly vertical and paddle not moving)
If you think I need more data about the final state of the game than just these, I can likely implement a way to get it very easily.
Here is my current fitness calculation, which I don't think is very good:
def calculate_fitness(self):
self.fitness = self.breakout_model.score
if self.breakout_model.num_times_hit_paddle != 0:
self.fitness += self.breakout_model.num_times_hit_paddle / 10
else:
self.fitness -= 0.5
if self.breakout_model.avg_paddle_offset_from_ball != 0:
self.fitness -= (1 / self.breakout_model.avg_paddle_offset_from_ball) * 100
for hits in self.breakout_model.hits_per_life:
if hits == 0:
self.fitness -= 0.2
if self.breakout_model.stale:
self.fitness = 0 - self.fitness
return self.fitness
Here is what I think the fitness calculation should do, semantically:
The score, obviously, should have the most significant impact on the overall fitness. Maybe a score of 0 should slightly negatively affect the fitness?
The number of times that the paddle hit the ball per life should have some effect, but not as significant of a contribution/weight. e.g. if that number is 0, it didn't even really try to hit the ball at all during that life, so it should have a negative effect
The number of times that the paddle hit the ball total should also have some effect, and its contribution should be based on the score. e.g. if it didn't hit the ball many times and also didn't score many points, that should have a significant negative effect; if it didn't hit the ball many times but scored a high number of points, that should have a significant positive effect. Overall, (I think) the closer to equal this value is to the game score, the less contribution/weight this value should have on fitness
The average distance in the X direction between the center of the frame and the center of the paddle should basically encourage a central "resting" position for paddle
If the game was ended artificially due to staleness, either this should have a significant negative effect, or it should automatically force the fitness to be 0.0; I'm not sure which case would be better
I'm not sure how to operate on all these values to make them affect the overall fitness appropriately.
Thanks in advance for any help you can provide.
I would minimize the conditional logic in your fitness function, using it only in those cases where you want to force the fitness score to 0 or a major penalty. I would just decide how much weight each component of the score should have and multiply. Negative components just add complexity to understanding the fitness function, with no real benefit; the model learns from the relative difference in scores. So my version of the function would look something like this:
def fitness(...):
if total_hits == 0:
return 0
return (game_score/max_score) * .7 \
+ game_score/total_hits * .2 \
+ game_score_per_life/hits_per_life * .1
(Aside: I didn't include "distance from center of frame" because I think that's cheating; if staying near the center is a good thing to do to maximize play efficiency, then the agent should learn that on it's own. If you sneak all the intelligence into the fitness function, then your agent isn't intelligent at all.)
I want to add friction in pong. When the ball hits the paddle and paddle is moving, the ball's speed can be changed and the ball's direction can be also changed. This is my idea. However, I don't know how to specifically do this. I hope my explanation is clear. Here is my code snippet:
def moveDot(surface,center, ball_speed,ball_radius,right_paddle,left_paddle):
size = surface.get_size()
for coord in range(0, 2):
center[coord] = center[coord] + ball_speed[coord]
# Left edge or the top edge
if center[coord] < ball_radius:
ball_speed[coord] = -ball_speed[coord]
# Right edge or the bottom edge
if center[coord] + ball_radius > size[coord]:
ball_speed[coord] = -ball_speed[coord]
# Left paddle bounce and go through
if left_paddle.collidepoint(center) and ball_speed[0] < 0:
ball_speed[0] = -ball_speed[0]
# Right paddle bounce and go through
if right_paddle.collidepoint(center) and ball_speed[0] > 0:
ball_speed[0] = -ball_speed[0]
The original pong game use a system of detection of the collision point. If the ball collides the paddle, it will be moved toward 45° and it will be less if the ball hits the side of the paddle.
So, the relation between the input and the output incidence is a function of the collision point (you can choose any function you want such as splitting the paddle in two parts or setting a linear factor).
Here, you can see that the green ray hits the center of the paddle, so, the output angle = the input angle. The blue ray shows output angle > input angle.
However that requires some tweaking for the coefficient between input and output angle depending of the game speed, the size of the paddle, the wanted behaviour, ...
Some other version implement a paddle that can change the reflection angle depending on the speed of the paddle.
If you want a real friction system, you can also use a physic engine and tune the different parameters (drag, ...). But, generally, simple implementations are sufficient and more fun.
I suggest you to try different versions and to choose that one which feels the best for your game.
You'll want to convey some portion of the paddle's velocity to the ball's y velocity, the portion being expressed as a coefficient of friction.
Example:
if left_paddle.collidepoint(center) and ball_speed[0] < 0:
ball_speed[0] = -ball_speed[0]
ball_speed[1] += 0.5 * left_paddle.speed
When the ball bounces against the left paddle, half of the paddle's velocity is applied to the ball.
(Note that I'm mentioning velocity, not speed, which has a direction, positive or negative along the respective axis, whereas speed is an absolute scalar. I've used speed in the example, as that fits with the naming you have used, but your implementation is really a velocity; you might want to consider renaming the variables for the sake of consistency.)
I have a chain of squares represented in pygame. I have some code that lets me rotate parts of the chain, as follows.
#!/usr/bin/python
import pygame
def draw(square):
(x,y) = square
pygame.draw.rect(screen, black, (100+x*20,100+y*20,20,20), 1)
def rotate(chain, index, direction):
(pivotx, pivoty) = chain[index]
if (direction == 1):
newchain = chain[:index]+[(y-pivoty+pivotx, (x-pivotx)+pivoty) for (x,y) in chain[index:]]
else:
newchain = chain[:index]+[(y-pivoty+pivotx, -(x-pivotx)+pivoty) for (x,y) in chain[index:]]
return newchain
pygame.init()
size = [600, 600]
screen = pygame.display.set_mode(size)
white = (255,255,255)
black = (0,0,0)
n = 20
chain = [(i,0) for i in xrange(n)]
screen.fill(white)
for square in chain:
draw(square)
pygame.display.flip()
raw_input("Press Enter to continue...")
newchain = rotate(chain, 5, 1)
print chain
print newchain
screen.fill(white)
for square in newchain:
draw(square)
pygame.display.flip()
raw_input("Press Enter to continue...")
screen.fill(white)
newchain = rotate(newchain, 10,0)
for square in newchain:
draw(square)
pygame.display.flip()
raw_input("Press Enter to continue...")
pygame.quit()
The function rotate takes an index of a square in the chain and rotates the whole chain after that square by 90 degrees, pivoting around the initial square. The problem is that this is meant to mimic a physical toy so it is not allowed to collide with itself. I can check to see if two squares are on top of each other after a rotation but how can I make sure they wouldn't collide temporarily during a rotation?
It sounds like you already know how to know if they're overlapping once you do the rotation, unless I am misunderstanding. If that's the case, then it would be relatively easy to define a function to answer that question given a potential rotation in the chain by adding a check to the end of your comprehension:
if (direction == 1):
newchain = chain[:index]+[(y-pivoty+pivotx, (x-pivotx)+pivoty) for (x,y) in chain[index:] if not overlapping(x, y, pivotx, pivoty)]
else:
newchain = chain[:index]+[(y-pivoty+pivotx, -(x-pivotx)+pivoty) for (x,y) in chain[index:] if not overlapping(x, y, pivotx, pivoty)]
And of course relying on some kind of:
def overlapping(x, y, px, py):
if (some logic that determins if this is bad):
raise Exception('Overlapping')
return True
You would need to do something useful with the exception, but at least this would check each square as you process it, and break out immediately instead of waiting until after the whole rotation to verify that it's good.
I would use the pygame functions to do that.
1. make your surfaces to sprites.
2. rotate them with pygame.transform.rotate.
3. check collision with pygame functions.
4. this function works perfect for me ;).
def collision(sprite, group):
rectCol = pygame.sprite.spritecollide(sprite, group, False)
return [s for s in rectCol if pygame.sprite.collide_mask(sprite, s)]
sprite is one of your squares.
group is all the other squares.
the function returns a list with all squares witch collide with your square.
What you have to do is check a collision between the two quarter-circles that two sides of the rotating rect inscribe. To check collisions between a quarter-circle and a rectangle you can try adapting this code.
The 2 squares will overlap in transit if:
one is stationary, the other is moving
the center of one starts to the left of the other, and ends up to the right (cross product will be of use here)
their distances to the pivot square are within a range (determined by the corners furthest and closest to the pivot square).
Above I gave an idea how to quickly check 2 given squares.
If you sort the squares by their distance to the pivot square, you will not have to check all pairs, only the pairs that are within a distance (thus avoiding O(N^2)).
One way you can do it is to model the squares after circles and use the relationship
d=sqrt((x2-x1)^2+(y2-y1)^2)
(x1,y1), (x2,y2) being the center of the squares.
where d is the minimum distance between their centres. Then you compare it to r1+r2, where r1 and r2 are the radius of the circles residing in the squares. If d is less than r1+r2, reverse their unit velocity vector, or make them rotate the other way.
You can increase the accuracy of the model by testing the vertices of one square, against the diagonals of another square. For example (please take a graph paper to see this), say we have a square A , vertices being [(2,0),(0,2),(2,4),(4,2)], and another (square B) being [(2,2),(5,2),(5,5),(2,5)], now take any one square (we'll take B) and take any one of it's vertices, say, (2,2). Test if the x-coords (2) lies between the x-coordinate of the diagonally aligned vertices of A, say (2,4) and (2,0). Apparently it does! Then we check it against another diagonal, (0,2) and (4,2). It does too! So, square B has collided with square A and the rotation vector or the velocity vector has to be reversed. You may also check with the y-coords.
You'll have to loop through each square to check if they collide with others. However, you don't have to check all squares as you will only need to concern yourself with squares with min distance of d is less than r1+r2 relative to each other, so you will just need one loop to check if their distances are less than the sum of radius, and another loop to check if their vertices has entered the body of the square. I normally do that in my games which stimulate random motion of particles (e.g. brownian motion)
This problem has been generically solved many, many times. The simplest explanation is that you use an increasing level of detail.
For the shapes themselves you must create either bounding boxes or bounding circles which are large enough to contain the outer-most points in the shape. A bounding circle is nice because it is the smallest shape which will always fully cover the object. It also doesn't need to be regenerated after a rotation because it always describes the maximum space possible for your shape.
For a bounding circle the next operation is to measure the distance between each bounding circle and discard the ones that cannot possibly overlap. This is fairly inexpensive. Note too, that you can discard reflections. I.e. if you tested that shape a cannot overlap shape b, don't now test if shape b overlaps shape a.
Once you have the shapes which "may" overlap, you must use a precise algorithm to see if any point in one shape overlaps any point in the other shape. There are a variety of ways to do this. Some of them are geometric (GJK Algorithm) while others use things like z-buffers or pixel masks. For these latter kind you can draw one shape to a test buffer, then start to draw the second shape. If you check the buffer before you plot a pixel and see there is a pixel already there, you know there is an intersection (collision).
I have a program written for simple 2D collision detection and handling on my current billiards simulation, however it is rubbish am looking for a solution that involves proper physics, i.e. newtons laws of motion for coliisions. what I have so far, which does not conserverve momentum and simplifies physics is
def collide(ball1,ball2):
dx = ball1.x - ball2.x
dy = ball1.y - ball2.y
dist = (dx)**2+(dy)**2
if dist < (ball1.radius + ball2.radius)**2:
tangent = atan2(dy, dx)
angle = 0.5 * pi + tangent
angle1 = 2*tangent - ball1.angle
angle2 = 2*tangent - ball2.angle
speed1 = ball2.speed*e
speed2 = ball1.speed*e
(ball1.angle, ball1.speed) = (angle1, speed1)
(ball2.angle, ball2.speed) = (angle2, speed2)
ball1.x += sin(angle)
ball1.y -= cos(angle)
ball2.x -= sin(angle)
ball2.y += cos(angle)
and what i have to run the collisions is this, where the bounce() is for hitting against the wall
running = True
while running:
background()
for i,ball in enumerate(balls,1):
ball.bounce()
ball.move()
for ball2 in balls[i:]:
collide(ball,ball2)
ball.display()
pygame.display.flip()
i'm still pretty new to this so please change whatever is useless/stupid
I recall I did a simple billiard simulator a while back. As you mentioned this is for educational purposes I will spare you from the whole code (and I don't have to dig around for it too :) )
But basically, I kept track of how long time has elapsed since the last frame. I used this time to find out new positions of each ball given a speed vector of each ball. For collisions in a frame I had to figure out at which exact time two balls would collide, and then apply each collision at that exact time. Pseudo code would look something like:
while running:
frame_time_ms = time elapsed since last frame
collisions = all collisions that will happen during this frame, with the exact time of collision. (1)
while collisions:
collision = first collision in collisions
collision_time_ms = time of collision (1)
move all balls to collision_time_ms
collide the two balls in the collision (2)
collisions = all remaining collisions after the time of collision (1)
move all balls to the end time of the frame
So, you will need to bring back your geometry and physics knowledge to find out the key formulas to:
Given the start and end position of two balls in a frame (or part of
a frame), do they collide, and at which point do they collide.
Remember to include the radius of the balls as well here. This will
give you the time of collision.
Given two balls at exact collision position, how will their new speed vectors look like afterwards. Some hints is to use elastic collisions, and experiment with how elastic it actually is:
http://en.wikipedia.org/wiki/Elastic_collision For bonus points you
could also include rotation of the balls :)
Good luck! =)
Circle on circle collision is simple. Take the center point co ordinance, subtract them to determine the distance between each circle.
Then, if the distance is greater than the sum of both circles radii, than they do not touch. If it is equal they are touching, if it's less they are overlapping.
Simply, if they are touching, have them repel each other. You can do this in a few ways. If you keep track of the directions they move in, have them move in the opposite direction.
As for walls, just use each wall with > < statements. So if the pos of a circle has x coordinates less than the west wall, than it has passed that wall. Again, just have them repel the walls.
Circle Collison is very simple if however you want to do other shapes it will be unbelievably difficult. Unless you just pit circles around those shapes or use pixel perfect collision (this is very high performance demand.)
If you want highly accurate collision of non circles, get a physics engine.