How To Test If Areas Overlap (python)
Solution 1:
You should use pygame.Rect to keep position and size and then you can use Pygame functions to check collisions.
one_rect.colliderect(second_rect)
one_rect.coolidepoint(mouse_position)
See other functions in docs for pygame.Rect
Pygame has also pygame.sprite.Group to keep group of sprites and check collisions for all sprites.
Solution 2:
It would seem to me that giving your organism a self.id = random.randomrange(11111,99999)
might help in identifying whether two organisms are the same or just at the same location with the same dimesnions - tho perhaps there is a unique property that can be accessed of the organism objects and you probably want a deterministic id generation method so there is no possibility of unintentional equality.
You need to either build a spawn function that takes two organisms or implement your spawn method on your organism object so that it can take as a parameter the spawn partner and in so doing can access the properties of each and mix them together and create the progeny. So I'm assuming that OrganismA.spawn(OrganismB)
will create a progeny of OrganismA-B spawn.
You can see the overlap logic below. If the following four logical expressions are valid then the two boxes overlap. The math for the ellipse would be different but you could use a rectangular approximation for your ellipse if you'd like. We'll start with A and B as organism objects and define their min and max x and y. So minXB means the minimum x coord for b.
maxXB = B.x - B.width/2
maxXA = A.x - A.width/2
minYB = B.y - B.height/2
...etc.. below are the equalites that hold for overlapping boxes
maxXB > minXA
maxYB > minYA
maxYA > minYB
maxXA > minXA
I suggest you bin the organisms by some division of the map for efficiency. Example quadrants, where every organism in the 0-400 width 0-400 height is in quadrant 'I', etc. From there you will have better performance when you do something like the following.
defquadrant(Organism):
if (Organism.x < 400and Organism.y < 400):
return'III'elif (...
defspawnOrganisms():
# create org_subLists for each quadrant
org_subLists = { 'I': [], 'II': [], 'III': [], 'IV': [] }
for i in org_list:
org_quadrant = quadrant(i)
org_subLists[org_quadrant].append(i)
for quad in org_subLists:
# only spawn progeny for lowerID to higerID parir to prevent A-B and B-A from each spawning the same/a similar org and avoid spawining an A-A spawnfor orgA in org_subLists[quad]:
for orgB in org_subLists[quad]:
if (orgA.id < orgB.id):
if(maxXB > minXA and maxYB > minYA and maxYA > minYB and maxXA > minXA):
orgA.spawn(orgB)
Solution 3:
You should use pygame's Sprite
class for your, well, sprites, since it makes your whole application easier to manage.
We don't need spawn
, move
and bounce
, we simply use a single function update
, which will be called by a Group
, which in turn we use instead of a simple list to hold our objects.
Each Sprite has it's own image, on which we draw a ellipsis. Then we create a Mask
to do pixel perfect collision.
Here's a full, running example. Note that I replaced all your comments with my own, explaining what's going on:
import pygame, random
pygame.init()
map_width = 800
map_height = 800
size = [map_width, map_height]
screen = pygame.display.set_mode(size)
# pygame already defines a lot of colors, we we just use them
colors = pygame.color.THECOLORS
pygame.display.set_caption("Natural Selection Game")
done = False
clock = pygame.time.Clock()
# just a simple generator to generate an id for each object defid_generator():
i = 0whileTrue:
i += 1yield i
ids = id_generator()
# just a helper function that wraps pygame.sprite.collide_mask# to prevent a sprite from colliding with itselfdefcollide(a, b):
if a.id == b.id:
returnFalsereturn pygame.sprite.collide_mask(a, b)
classOrganism(pygame.sprite.Sprite):
def__init__(self, id, org_list, color = None):
pygame.sprite.Sprite.__init__(self, org_list)
self.org_list = org_list
self.id = id# Speed and direction
self.change_x = random.randrange(0,6)
self.change_y = random.randrange(0,6)
# Dimensions
width = random.randrange(5,50)
height = random.randrange(5,50)
x = random.randrange(0 + width, map_width - width)
y = random.randrange(0 + height, map_height - height)
self.rect = pygame.rect.Rect(x, y, width, height)
self.image = pygame.surface.Surface((width, height))
self.image.fill(colors['hotpink2'])
self.image.set_colorkey(colors['hotpink2'])
# we either pass in the color, or create a random one
self.color = color or random.choice([colors['red'], colors['green'], colors['blue']])
pygame.draw.ellipse(self.image, self.color, [0, 0, width, height])
self.mask = pygame.mask.from_surface(self.image)
# we keep track of collisions currently happening# so we only spawn one children for each collisions
self.collisions = set()
# just something to limit the number of organisms
self.age = 0
self.children = 0# Initiate movementdefupdate(self):
self.age += 1# we move by simply moving the rect# the Group's draw function will look that the rect attribute # to determine the position for drawing the image
self.rect.move_ip(self.change_x, self.change_y)
# we can make use of a lot of Rect's attributes to make # this computation simplerif self.rect.left < 0or self.rect.right > map_width:
self.change_x *= -1if self.rect.top < 0or self.rect.bottom > map_height:
self.change_y *= -1# only reproduce if we are at least 200 ticks old# so newly created organisms spwan new ones at the# very moment they spawned themselfif self.age < 200:
return# just a narbitary limit so the screen does not get too fullif self.age > 500:
printstr(self.id) + ' died of age'# kill() removes the Sprite from all its Groups (which is only org_list at the moment)
self.kill()
return# just an arbitary limit so the screen does not get too full if self.children > 4:
printstr(self.id) + ' died of too many children'
self.kill()
return# check if we collided with another Sprite
collided = pygame.sprite.spritecollideany(self, self.org_list, collide)
# also check if this # - is a new collision# - the other organism is at least 200 ticks old# - there are not too many organisms at the screen at the momentif collided andnot collided.idin self.collisions and collided.age > 200andlen(self.org_list) < 100:
# keep track of the current collision, so this code is not triggerd # every frame while the colliding organisms move other each other
self.collisions.add(collided.id)
collided.collisions.add(self.id)
printstr(self.id) + ' collided with ' + str(collided.id)
# we create a new color out of the colors of the parents
r, g, b = (self.color[0] + collided.color[0]) / 2, \
(self.color[1] + collided.color[1]) / 2, \
(self.color[2] + collided.color[2]) / 2
color = [r, g, b]
# let the color mutate sometimes for funif random.randrange(0, 100) < 10:
color[random.randrange(0, 3)] = random.randrange(0, 256)
print'Offspring of ' + str(self.id) + ' and ' + str(collided.id) + ' mutates'# create the new child with the new color
Organism(next(ids), self.org_list, map(int, color))
self.children += 1
collided.children += 1else:
# if there are currently no collisions, we clear the collisions set# so new collisions can happen
self.collisions = set()
# we use a Group for all the draw/update/collision magic
org_list = pygame.sprite.Group()
for _ inrange(15):
Organism(next(ids), org_list)
whilenot done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True# we just call update on the group so update is called# an every sprite in the group
org_list.update()
screen.fill(colors['white'])
# same for drawing: just call draw on the group
org_list.draw(screen)
clock.tick(60)
pygame.display.flip()
pygame.quit()
I set some arbitary limits on how much objects are on the screen at once. If you want to speed up the collision detection, either skip the pixel perfect collision detection and use a rect or circle based one. But the biggest improvement would probably be using a quadtree collision detection algorithm (but that is out of scope of this answer; just google it).
Post a Comment for "How To Test If Areas Overlap (python)"