Efficient collision detection - python

I am using python and pyglet in a 2d game but I have come across a problem in collision detection. My code to check for collision looks like this:
def distance(self,target):
return math.sqrt((self.x-target.x)**2 + (self.y-target.y)**2)
def check_collision(self):
for i in list_of_mobs:
if self.distance(i) < (self.width/2 + i.width/2):
return True
It checks the distance from each sprite with "target" being another sprite. The thing I am unsure of is "should I be checking for collisions between all the sprites?" I have over 200 mobs (even more I hope in the finished product) and it becomes unplayable when the collisions are checked. Is there someway to only check sprites within a certain distance without losing speed?
EDIT:
I had a read around google and found out that a lot of the speed was spent doing the same collisions. i.e. sprite1 was checked with sprite2 and sprite2 was checked with sprite1. So I made a few changes to the check collision function and it runs quicker now but still less than 20 fps
def check_collision(self):
global mobs_to_collide
if mobs_to_collide == []:
mobs_to_collide = list_of_mobs[:]
if self in mobs_to_collide:
mobs_to_collide.remove(self)
for i in mobs_to_collide:
if self.distance(i) < (self.width/2 + i.width/2):
return True
(it may contain some inefficient code/useless stuff. I was playing around with it a bit)
EDIT2:
I decided to use rabbyt as the sprite library. The collisions are fast and easy. I replaced the code above ^^ with:
rabbyt.collisions.collide(mobs_to_collide)
This returns a list of lists (I'm not sure if that's the correct term) with the objects that collided. I'm still working on how to translate that into an "if collided:" type statement but I am making progress. If anyone is in a similar situation, I would reccomend using rabbyt.

A simple way to improve the speed on this, could be to remove the square root operation.
def distancesq(self,target):
return (self.x-target.x)**2 + (self.y-target.y)**2
def check_collision(self):
for i in list_of_mobs:
# Square this distance to compensate
if self.distancesq(i) < (self.width/2 + i.width/2)**2:
return True

Probably it is already too late, but I had exactly the same problem. I managed to solve it by calculating collisions only for visible objects, like that:
for object_ in objects:
if not object_.visible: # pyglet.sprite.Sprite() provides this flag
continue
# rest of your collision detection

Related

Python2.7 how do I use multiples variables in a loop?

I'm making my own game with Python2.7 through the pygame libraby.
It's a 1v1 combat game where players use the same keyboard.
The game works in a main loop that is repeated 60times per second, every time the loop is executed, it calculates lots of things e.g the position, problem is that I have 2 players, so I have to write the lines two times.
Example here:
if p1direction == 'right' and p1XS < p1Attributes[1]: p1XS +=
p1Attributes[0]
and:
if p2direction == 'right' and p2XS < p2Attributes[1]: p2XS +=
p2Attributes[0]
See the differences p1 and p2, they are variables that belongs to Player 1 and Player 2 respectively.
I just want to find a solution to not write every time the same lines just for p2. I was thinking about the for function so I can even add players easly but I don't know how to do it in this case...
Can someone help me ? :) Please
Create a class player.
Then add the attributes of each player to the class.
Instantiate your class with player 1 and 2.
class Player():
direction = "right"
etc.
def shoot(self):
if self.direction == "right"
shoot_right()
playerOne = Player()
playerTwo = Player()
direction = playerOne.direction
If you haven't used classes yet, I wouldn't recommend using them though. Inheritance can get pretty nasty...
Hope that helped,
Narusan
EDIT:
If you haven't used classes in Python yet, I recommend catching up there first and then continuing your game development. I have programmed several games in pygame as well, and classes come in very hand. In fact, it is almost impossible to create pygame games without using proper classes (or endless if-clauses and for-loops that will make everything super slow).
Wish you all the best of luck
How about storing your variables(for example p1direction and p2direction) in a vector(player_directions) indexed by the player number and using a loop access it, for example:
number_of_players = 2
playersXS = function_that_fills_playersXS() # return a vector containing your p1XS and p2XS variables in a vector
for player_number in xrange(number_of_players):
if player_directions[player_number]=='right' and playersXS[player_number]< Attributes[player_number][1]:
playersXS[player_number]+=Attributes[player_number][0]

Making sprites bounce off of each other

To download the code, follow the link:
Background:
So I've been going through pygame tutorials since I'm new to it and I found Eli Bendersky's well-known tutorial. I was going through part one and was attempting to add my own flair to it by making it "Social Cats". The cats would wander around and if they touched each other then they would graze each other and go their separate ways. In other words the same thing Eli had but with collision detection and new sprites. I figure this would be good practice. I've spent the past few days researching collision detection and how different people do it, but I've yet to see one that would work with my scenario or something similar. I'm beginning to see how small of a community I'm in.
Objective:
Ultimately, I am trying to make it so when a cat runs into another one, the one that collided will go off in a random direction that equals 45 degrees less or more than its current direction.
Problem:
I'm importing vec2d and I have my Cat class and my main. I would like to do the collision detection in the main because later on I will create a GameManager class that watches over what's going on. According to OOP, the cats shouldn't know about each other anyways. I've been having trouble getting the collision detection to work. I've tried a couple of different ways. In both ways nothing happens when they touch each other. I'm getting the impression what I'm wanting to do is way more complex than how I'm perceiving it. How am I screwing this up? I feel as if I've wasted enough time on this one aspect. Of course, that's the learning process. Thoughts?
Way 1:
mainWindow.fill(_white_)
for cat in cats:
cat.update(timePassed)
cat.blitme()
catsCollided = pg.sprite.spritecollide(cat, catGroup, True)
print pg.sprite.spritecollide(cat, catGroup, False)
for cat in catsCollided:
cat.currentDirection.angle = randint(int(cat.currentDirection.angle - 45), int(cat.currentDirection.angle + 45))
Way 2:
mainWindow.fill(_white_)
for cat in cats:
cat.update(timePassed)
cat.blitme()
print pg.sprite.spritecollide(cat, catGroup, False)
tempCatList = list(cats)
for catA in cats:
tempCatList.remove(catA)
for catB in cats:
if catA.rect.colliderect(catB.rect):
cats[cats.index(catA)].currentDirection.angle = randint(int(cat.currentDirection.angle - 45), int(cat.currentDirection.angle + 45))
Your first way is correct, however there are just a few bugs. Sprite collide is the best way to do it. First of all, there are very few circumstances when you want the third argument in sprite collide to be true, and unless I completely misunderstand how your code is doing it, you do not want to use True. When you specify True, it will automatically delete both sprites upon collision. The other thing is that you want to make sure to filter out self collisions. Basically, if a sprite runs sprite collide on itself it registers a collision with itself. One final thing about your code is that although your randint chooser might work (you may want to test what it is returning though), random.choice() would be a better fit for what you are looking for. When these changes are implemented, it looks something like this:
mainWindow.fill(_white_)
for cat in cats:
cat.update(timePassed)
cat.blitme()
catsCollided = pg.sprite.spritecollide(cat, catGroup, False) #False makes it so colliding sprites are not deleted
print pg.sprite.spritecollide(cat, catGroup, False)
for cat in catsCollided: #by the way although this is perfectly fine code, the repetition of the cat variable could be confusing
if cat != self: #checks that this is not a self collision
cat.currentDirection.angle = random.choice([int(cat.currentDirection.angle - 45), int(cat.currentDirection.angle + 45])

python pygame physics rectange - circle collision

Again this question is on PyParticles4.
Link to last question for reference
Comment if unclear...
I am working on a Shooter game, much like this, but on a flat land with a wall that varies it's height on every turn(something for fun in the game) and with 2 players,each with a cannon that can move some distance (there's a limit, and they can't move beyond a certain amount from their start position) on each turn(the player decides if he wishes to move).
My code so far(for the Bullet and Shooter)
class Bullet(PyParticles.Particle):
def hit(self,shooterlist):
for shoot in shooterlist:
#confusion
dist = math.hypot(dx,dy)
# other funcs to added
class Shooter:
def __init__(self,pos,size):
self.rect = pygame.Rect(pos,size)
# other funcs to added
My Problems
Collision of the bullet with the Shooter. Any Ideas on how to know when the bullet collides with the rect?
I have been advised by someone to look at all the points on the edge of the rect, and see if it is within the circle but it seems to be very slow.
I think something faster would be better..
..
..
Update:
The circle can have a rect around it, which if collides with the rect, I now know when the rect is close to the circle, maybe even touching it.. How do i move forward??(Thx to PygameNerd)
I am not sure what you mean by your question, but I thingk that you need the colliderect function.
rect.colliderect(rect): Return bool
Put this in the code somewhere, and if it returns true, have the ball explode.
You can run another test every time colliderect returns true. There are a few options for what that test can be. One is similar to the advice you already received but with the wall and circle switched. Check the points on the circumference of the circle to see if one of them collides with the wall using Rect.collidepoint. In pygame, you can get a list of circumference points by creating a Mask from the circle surface and running Mask.outline. You probably won't need every point, so you could get just every Nth point. In fact, you may only need the midpoints of the edges of the circle's rect, which you can get from the Rect object itself.
You're asking for precise collision, and that would give you pixel perfect collision detection between a circle and a rectangle. You may not need it in your game, though, so I suggest trying with just colliderect first. If you're interested in collision detection in pygame in general, there are a lot of options that are directly implemented in or based on functions described in the Mask and Sprite documentation.
You can use Rect.collidepoint with the center of the circle but make rect bigger
collide_rect = Rect(x - p.radius,y - p.radius,w + 2 * p.radius,h + 2 * p.radius)
if collide_rect.collide_point(p.pos):
# Collision Resolution

Colliding Rects

I have been working on this game, but I have a question when it comes to collision detection. Can I check for collision and have it return True:
example:
def collide(self, EnemyTank):
tank_collision = pygame.sprite.collide_rect(self.rect, EnemyTank.rect)
if tank_collision == True:
return True
And then make it perform an action like this:
if player.collide == True:
e_tank_x += 0
I am new to programming so please bear with me, I am trying as hard as I can and any comments or suggestions would also be very appreciated.
I'm not an expert of pygame, but it sound perfectly legitimate. I would just take away the middle man in your initial function:
def collide(self, EnemyTank):
return pygame.sprite.collide_rect(self.rect, EnemyTank.rect)
And you need to adjust the test you use, as you want to actually check the collision, while as you wrote it it just test if the function collide exist ;)
You can also use the implicit testing, removing some unnecessary character (best abith are best learned early)
if player.collide(EnemyTank):
do your action here
good luck with your game!

top down friction in pymunk

Been struggling with this for a couple of days, hard to find code examples on the net.
I'm making a topdown game and having trouble getting the player to move on key press. At the moment i'm using add_force or add_impulse to move the player in a direction, but the player doesn't stop.
I've read about using surface friction between the space and the player to simulate friction and here is how it's done in the tank.c demo.
However I don't understand the API enough to port this code from chipmunk into pymunk.
cpConstraint *pivot = cpSpaceAddConstraint(space, cpPivotJointNew2(tankControlBody, tankBody, cpvzero, cpvzero));
So far, I have something that looks like this:
class Player(PhysicalObject):
BASE_SPEED = 5
VISIBLE_RANGE = 400
def __init__(self, image, position, world, movementType=None):
PhysicalObject.__init__(self, image, position, world)
self.mass = 100
self.CreateBody()
self.controlBody = pymunk.Body(pymunk.inf, pymunk.inf)
self.joint = pymunk.PivotJoint(self.body, self.controlBody, (0,0))
self.joint.max_force = 100
self.joint.bias_coef = 0
world.space.add(self.joint)
I don't know how to add the constraint of the space/player to the space.
(Need someone with 1500+ rep to create a pymunk tag for this question).
Joe crossposted the question to the Chipmunk/pymunk forum, and it got a couple of more answers there. http://www.slembcke.net/forums/viewtopic.php?f=1&t=1450&start=0&st=0&sk=t&sd=a
Ive pasted/edited in parts of my answer from the forum below:
#As pymunk is python and not C, the constructor to PivotJoint is defined as
def __init__(self, a, b, *args):
pass
#and the straight conversion from c to python is
pivot1 = PivotJoint(tankControlBody, tankBody, Vec2d.zero(), Vec2d.zero())
# but ofc it works equally well with 0 tuples instead of the zero() methods:
pivot2 = PivotJoint(tankControlBody, tankBody, (0,0), (0,0))
mySpace.add(pivot1, pivot2)
Depending on if you send in one or two arguments to args, it will either use the cpPivotJointNew or cpPivotJointNew2 method in the C interface to create the joint. The difference between these two methods is that cpPivotJointNew want one pivot point as argument, and the cpPivotJointNew2 want two anchor points. So, if you send in one Vec2d pymunk will use cpPivotJointNew, but if you send in two Vec2d it will use cpPivotJointNew2.
Full PivotJoint constructor documentation is here: PivotJoint constructor docs
I'm not familiar with either system you've mentioned, but some basic game ideas that may relate are:
If you add a force (or impulse) which affects movement, for the entity to stop, you must also take it away. In my games if I had a function AddImpulse()/AddForce() I would have a corresponding one such as Apply_Friction() which would reverse the effect by however much you want (based on terrain?) until movespeed is zero or less. I personally wouldn't bother with this method for movement unless needed for gameplay since it can add more computations that its worth each update.
There should be some way to track KeyPressed and/or KeyPosition and then using those x/y coordinates are incrememnted based on player speed. Without knowing what you've tried or how much the API does for you, it's hard to really say more.
Hope this helps. If this is stuff you already knew kindly ignore it.

Categories