I am totally new to Box2D and I have been trying it on the Python platform combined with pygame.
I have a bullet that bounces around and my goal is that when it has hit a wall up to around 5 times or hit an enemy it will get destroyed.
Now I was looking at this getting started guide at the Accessing Contacts section, and it says that all the recent contacts can be found at body.contacts.
And so it seems, but when my bullet just collides once with a wall it gives me 6 contact edges.
My bullet is a circle and the wall is an edge shape.
# Bullet body, taken from the inside of the constructor
self.body = world.CreateDynamicBody(position=pos_m, bullet=True)
self.body.CreateCircleFixture(radius=radius, density=1, friction=0.5, restitution=0.8)
# 4 walls covering the screen. top_left, top_right etc. is vectors positions
border = world.CreateStaticBody(
position=(0, 0),
shapes=(
b2EdgeShape(vertices=[top_left, top_right], friction=0.3),
b2EdgeShape(vertices=[top_right, bottom_right], friction=0.3),
b2EdgeShape(vertices=[bottom_right, bottom_left], friction=0.3),
b2EdgeShape(vertices=[bottom_left, top_left], friction=0.3),
),
)
So for the bullet I put this in it's update method (that runs once per step, outside world.Step())
# Check contacts
for contact_edge in self.body.contacts:
print("contact!")
And when the bullet just collides once it prints out
contact!
contact!
contact!
contact!
contact!
contact!
Why does it do this? And how do I limit it to one contact per collision?
Edit 1:
It seems like the amount of contacts are semi-random. Varies from 6 up to 10 contacts per impact
Edit 2:
Here's a small test sample: https://dl.dropboxusercontent.com/u/16314677/game.zip
I am using Python 2.7.8 with pygame 1.9.2a0 and pyBox2D 2.1b1
Edit 3:
It seems like when it collides once the self.body.contacts just contains one contact edge, but it keeps that edge in that list for around 6 steps. How do work around this? Doesn't seem like I can even remove it from the list, the del function does nothing. Help much appreciated!
I find the reason! (whoohoo!)
Each contact is part of the whole contact event. It's like small contacts that happen before and after the collision, but only one of the contact_edge values are the actual collision.
When I mean contact_edge I mean of course the values from the list body.contacts as mentioned in the GettingStartedManual.
So if you look at the contact object in this documentation you can see one of the properties are touching.
When I applied that to the loop I saw that only one of the 6+ contacts are touching and therefore I can calculate when it hits a wall, which is what I wanted!
Example of the loop:
for contact_edge in body.contacts:
if contact_edge.contact.touching:
print('ONE collision!')
Related
I am trying to create a game that places a cat which I can move left and right at the bottom of the screen.
Then I make a ball appear at a random position on the top of the screen and fall down the screen at a steady rate.
If the cat catches the ball by colliding with it, I make the ball disappear.
Meanwhile, I make a new ball each time the cat catches the ball or whenever the ball disappears off the bottom of the screen.
My question is: how do I make the ball disappear after being caught by the cat and then create a new ball over and over?
Currently I can think of two approaches to this problem:
First, I create a single ball. Once the cat catches the ball, I write some codes to make THAT ball disappear. Then, write some codes to create a new ball. Put these codes in a loop somehow to keep a ball disappear and reappear over and over. The problem is, I am not sure which codes to write to make a ball disappear and then create a new one.
Second approach is, I create a group of balls balls = Group() using pygame.sprite. I let those balls drop one after another. When a cat catches a ball, I remove that ball from the group of balls. Then let another ball in the group drop.
for ball in balls.copy():
if cat.rect.colliderect(ball.rect):
balls.remove(ball)
This second approach has somehow created another problem. When I wanted to specify the collision between the cat and each ball, I sometimes received the error message saying that either the name "ball" is not define or the name "balls" is not defined. Plus, I don't know whether it is really necessary to use a for loop whenever I want to specify a cat-ball collision. I feel that there has to be a more straight-forward and efficient way than this.
Would the first approach be better? If yes, please let me know how to make a ball disappear and to create a new ball immediately afterwards. THANK YOU SO MUCH!!
Probably the easiest way is to create a sprite with the following template:
class Ball(pygame.sprite.Sprite):
""" falls and restarts the ball position when ball hits the cat or the ground"""
def __init__(self):
""" setting up the ball image, rect, caught_by_cat flag etc. """
def update(self):
if self.hits_the_ground():
self.reset()
elif self.caught_by_cat:
self.reset()
else:
self.fall()
def reset(self):
""" randomize the ball position and set the ball rect to this position and resets the caught_by_cat flag """
def hits_the_ground(self):
""" checks whether ball hits the bottom of the board """
def fall(self):
""" move the ball rectangle with a steady speed """
and then after the initialization and adding it to the pygame.sprite.Group, in the game event loop simply:
sprite_group.update()
sprite_group.draw(screen)
Check for collision
if cat_collides_with_ball:
ball.caught_by_cat = True
This way you don't need to create many ball objects, you just need to restart the ball rect attribute to draw it in other place.
This question already has answers here:
rect collision with list of rects
(2 answers)
How do I detect collision in pygame?
(5 answers)
Closed 2 years ago.
I'm writing an asteroids clone in Pygame and I've tried a few different ways to detect collisions between 'asteroid' rectangles and 'projectile' rectangles (both of which inherit from Rect). I'd like to know which of them (or what other method of detecting collisions) will lead to the least amount of lag. Moreover, I'd like to know why they have the least lag (I tried looking at the source for the Rect class to see how collidelist, colliderect, and collidepoint worked, but it was compiled in bytecode or something of that nature, which I unfortunately don't know anything about). The asteroids and projectiles are in lists named 'asteroids' and 'projectiles' respectively.
I first tried iterating through my projectiles list and using pygame.rect.collidelist on the asteroids list to find collisions:
index = 0
for projectile in projectiles[:]: #copy so I can delete from original
index = projectile.collidelist(asteroids)
if index != -1:
del asteroids[index]
projectiles.remove(projectile)
Unfortunately, this is very laggy, probably because I'm checking all of the rectangles on the screen.
To minimize unnecessary checks, I tried defining a 10 by 10 grid using my screen WIDTH and HEIGHT, and both the projectile and asteroid classes have a method which sets their gridRect attribute to a tuple with the coordinates of the grid segment they are in. I then detect collisions with a nested for-each-in loop like this:
for projectile in projectiles[::-1]:
for asteroid in asteroids[::-1]:
if projectile.gridRect == asteroid.gridRect and projectile.colliderect(asteroid):
projectiles.remove(projectile)
asteroids.remove(asteroid)
This worked really well as long as there weren't more than 50 projectiles and asteroids, but if possible I'd like even better performance.
Finally, I tried creating an array, sorting the rects into it, and then detecting collisions:
gridRects = []
for i in range(GriddedRect.GRIDSIZE):
gridRects.append([])
for j in range(GriddedRect.GRIDSIZE):
gridRects[i].append({"Projectiles": [], "Asteroids": []})
for asteroid in asteroids:
gridRects[asteroid.gridRect[0]][asteroid.gridRect[1]]["Asteroids"].append(asteroid)
for projectile in projectiles:
gridRects[projectile.gridRect[0]][projectile.gridRect[1]]["Projectiles"].append(projectile)
for i in range(GriddedRect.GRIDSIZE):
for j in range(GriddedRect.GRIDSIZE):
for projectile in gridRects[i][j]["Projectiles"][::-1]:
index = projectile.collidelist(gridRects[i][j]["Asteroids"])
if not index == -1:
asteroids.remove(gridRects[i][j]["Asteroids"][index])
del gridRects[i][j]["Asteroids"][index]
projectiles.remove(projectile)
gridRects[i][j]["Projectiles"].remove(projectile)
This was probably more laggy than the first one, but I thought that it should be the least laggy because although it's more complex, it minimizes the amount of calls to check if a Rect should colllide.
I know that the second method is the least laggy, but an explanation of why would be really helpful.
Instead of the second method, I considered trying to only check asteroids within a certain distance of each projectile, but I can't see how I could do this in an efficient way, because for each projectile I would still have to check the distance of every asteroid.
I'm sure there must be a better way than these methods, so if you know of one please let me know. Also, if you need to see more of my code, let me know and I can edit it in. I think this is the minimum amount needed to get the idea though.
I am using a for loop combined with .colliderect() for collision detection while attempting to make a game using pygame, the loop gets too slow with ~340 wall Rectangles, I was wondering if it could be faster somehow because it severely effects the game play loop?
I have tried using coordinate points on different places on every wall but only works if you're moving certain amounts of pixels at a time and every time you half the movement speed it quadruples the amount of coordinate points you save.
#disregard indent, this is all in an update function that is called every time that a player decides to move.
self._old_position = self.position
PlayerRectangle = pygame.Rect(self.position[0]+ x,self.position[1]+y,16,16)
cwd = os.getcwd()
tmxData = load_pygame(cwd+"\\Maps\\TestfileMap.tmx")
movement = True
for obj in self.walls:
if(pygame.Rect(obj[0],obj[1],16,16).colliderect(PlayerRectangle)):
movement = False
self.move_back()
else:
continue
if movement:
self.position[0] += x
self.position[1] += y
self.stats["position"]=self.position
self.rect.topleft = self.position
self.feet.midbottom = self.rect.midbottom
The provided code works, however it is too slow, I was wondering if there is a different method in collision detection or if there is a way to make what is shown faster, it bogs down things greatly. Thank you
EDIT:
So the solution was basically that I had load_pygame that ran literally every time it looped simply take out the line that does that and it clears things up a lot more, for further optimization change the line that makes a Rect for each object and just use a list of Rects that are already constructed, this limits function calls.
Without a minimal working example it is tough to give assertive advice.
As I stated in the comments, there is a spurious call to a "load_pygame" function that seems to read file data inside this code - that alone could be the cause of your slowdown. Moreover, the data read is not used in the collision detection.
The other advice I't have is to let your walls rectangles pre-calculated in a sprite group, instead of creating new rectangles for all walls in every check with:
pygame.Rect(obj[0],obj[1],16,16).colliderect(PlayerRectangle)). then you can use the sprite method "spritecollideany" - https://www.pygame.org/docs/ref/sprite.html#pygame.sprite.spritecollideany - that should optimize the rectangle-collision verification.
(This will also require your Player object to be a Sprite if it is not already).
I am currently working on a 2D game in which the player has to sneak up on a still person within a certain amount of time. There are various crates in the way (depending on which level it is), and I would like to make it so that the player can hide behind crates to sneak up on the still person.
I thought that I could use a cone-type vision for the person looking, but I'm not exactly sure how I would accomplish that. The player doesn't have to see the vision cone of the person looking either.
A similar effect to what I would like is in this sample code on github.
NOTE: The player cannot pass through the crates, and the people and crates are sprites.
You have to calculate the if the player is in line with the person, if it is you can check for every box if the 3 objects are ate the same position, if not you are in vision field person_looking. concidere player and person a list with coords.
def isInLine(player, person):
deltaX = person[0] - player[0]
deltaY = person[1] - player[1]
if (person[0] == player[0]) or (person[1] == player[1]) or (abs(deltaX) == abs(deltaY)):
return true
Like in a chess game, imagine you ahve to check if the king is in check by a queen. Its the same logic here.
I think you can create an invisible projectile of a size of one pixel which you launch at desired angle towards player. You make it have some travel speed (say 2 pixels at a time) but instead of actually allowing the projectile to travel every frame you just loop for its travel untill it collides with something (either player or crate or end of level). So the whole emmision and collision process is done outside of while True main game loop in some function. You can emit it every second or something instead of every frame. And also you can emmit it in a cone shape either by scailing it's size every loop or emiting new one at different angle. If any of those collide with player mask or rect -> you have established a line of sight. This idea actually came to me right now out of the blue...
;)
On the second thought... maybe not 1 pixel size projectile but just collidepoint function. Just move your point from emmision origin to target at desired resolution.
I'm trying to do what the title says. I have a character with a gun constrained to its hand, and I'm trying to get the gun to point at the cursor. I figured that a DampedRotarySpring would be a nice way to do it, but it turns out not to be as simple as that. The gun is a dynamic body with a Segment shape, and for the cursor I create a static body whose position I set to the mouse location with pygame each step.
When I run the program, the gun simply does not move at all except for the effect of gravity or collisions.
Here is the relevant code:
# add crosshairs at the location of the mouse
pointer_body = pymunk.Body()
pointer_shape1 = pymunk.Segment(pointer_body, (0,CROSSHAIRS_SIZE), (0,-CROSSHAIRS_SIZE), 1) # vertical segment
pointer_shape2 = pymunk.Segment(pointer_body, (-CROSSHAIRS_SIZE,0), (CROSSHAIRS_SIZE,0), 1) # horizontal segment
# add a spring that will angle the gun toward the mouse
spring = pymunk.DampedRotarySpring(me.gun.body, pointer_body, 0, 0.01, 1)
space.add(pointer_shape1, pointer_shape2, spring)
while True:
# handle event queue
for event in pygame.event.get():
if event.type == pygame.MOUSEMOTION:
from math import atan2
# update location of pointer
pointer_body.position = flipy(pygame.mouse.get_pos())
pointer_body.angle = atan2( (pointer_body.position.y - me.gun.body.position.y), (pointer_body.position.x - me.gun.body.position.x) )
Edit:
Here is a Gist repository of all my code: https://gist.github.com/4470807.
The main loop is in ragdoll.py.
The problem with the code in the gist is that you have attached the gun to the hand with two joints to keep them in the same place and same rotation. However, the the hand is a rouge body and wont rotate. Therefor the gun wont rotate when its pulled by the spring between it and the cursor, because that other joint is stronger.
Im not sure exactly how you want the setup, but you can see that it all works if you remove the RotaryLimitJoint from the gun-hand.
Take a look at a fixed fork of the code for the exact details: https://gist.github.com/4505219
Some tips for future troubleshooting that I did to find the problem:
Make everything 10x bigger so its easy to see what happens. I know pymunk only draws in one size, but it was easy to just add a 0 on the end of all sizes in the code.
Make the hand not move so its easier to see how it rotates (removed all stuff in the update_hand_position method)
Disable collisions between all shapes in the scene so that the rotating gun is not hindered by some body part. (did a simple loop of space.shapes and ran shape.group=1)
Maybe your problem is with the spring parameters? The stiffness and damping looks very low unless the gun is extremely light.
Check out this code example I added to pymunk yesterday: http://code.google.com/p/pymunk/source/browse/trunk/examples/damped_rotary_spring_pointer.py
(There is one thing going on with the rotation when it flip over between positive and negative pi that I will look at)