2016-12-06 228 views
1

我是一名编写Python程序的新手编程人员,使用Pygame库来模拟自然选择。如何测试区域是否重叠(Python)

我试图完成的事情之一是使移动椭圆(或者如果这是太复杂,矩形)重叠的对象会产生继承其特征的子对象。

我的问题是我无法创建一个工作代码来识别何时两个对象区域重叠。我需要代码来确定两个物体何时穿越路径(并且瞬间重叠),以便游戏知道产生另一个形状。

有一次,我尝试了一个复杂的嵌套for循环,我认为会工作,但这导致模拟崩溃。

这是我的代码是:

import pygame, random 

# Define some colors 
black = (0, 0, 0) 
white = (255, 255, 255) 
red = (255, 0, 0) 
green = (0, 255, 0) 
blue = (0, 0, 255) 

pygame.init() 

# Set the width and height of the screen 
map_width = 800 
map_height = 800 
size = [map_width, map_height] 
screen = pygame.display.set_mode(size) 

# Display name 
pygame.display.set_caption("Natural Selection Game") 

# Loop until the user clicks the close button. 
done = False 

# Used to manage how fast the screen updates 
clock = pygame.time.Clock() 

# -------- Organism ----------- 
class Organism: 

    def __init__(self): 
     # Speed and direction 
     self.change_x = random.randrange(0,6) 
     self.change_y = random.randrange(0,6) 

     # Dimensions 
     self.width = random.randrange(5,50) 
     self.height = random.randrange(5,50) 

     # Diet (WIP) 
     self.color = random.choice([red, green, blue]) 

     # Starting position 
     self.x = random.randrange(0 + self.width, 800 - self.width) 
     self.y = random.randrange(0 + self.height, 800 - self.height) 

    # Spawn 
    def spawn(self, screen): 
     pygame.draw.ellipse(screen, self.color, [self.x, self.y, self.width, self.height]) 

    # Initiate movement 
    def move(self): 
     self.x += self.change_x 
     self.y += self.change_y 

    # Bounce 
    def bounce(self, map_width, map_height): 
     if self.x < 0 or self.x > map_width - self.width: 
      self.change_x = self.change_x * -1 
     if self.y < 0 or self.y > map_height - self.height: 
      self.change_y = self.change_y * -1 

# Initial spawning conditions 
org_list = [] 
org_number = 15 

for i in range(0,org_number): 
    org = Organism() 
    org_list.append(org) 

# -------- Main Program Loop ----------- 
while not done: 
    # ---- Event Processing ---- 
    for event in pygame.event.get(): 
     if event.type == pygame.QUIT: 
      done = True 

    # ---- Logic ---- 
    # Movement 
    for i in org_list: 
     i.move() 
     i.bounce(map_width, map_height) 

    # Reproduction 

    # ---- Drawing ---- 
    # Set the screen background 
    screen.fill(white) 

    # Spawn organisms 
    for i in org_list: 
     i.spawn(screen) 

    # --- Wrap-up 
    # Limit to 60 frames per second 
    clock.tick(60) 

    # Go ahead and update the screen with what we've drawn. 
    pygame.display.flip() 

# Close everything down 
pygame.quit() 

任何有识之士将不胜感激。

+0

这将是很好,如果你可以MSPaint或一些你正在寻找的例子。我想我理解,但我不确定* –

+0

你的形状可以旋转吗?或者他们有固定的方向吗? – depperm

+0

您没有发布使仿真崩溃的代码,是吗? (我看不到嵌套的循环)。 – coredump

回答

0

在我看来,给你的有机体一个self.id = random.randomrange(11111,99999)可能有助于确定两个有机体是否相同或只是在相同的地方有相同的二聚体 - 也许有一个独特的属性可以访问的有机体对象,你可能想要一个确定性的id生成方法,所以不可能有无意的平等。

您需要构建一个带有两个生物体的spawn函数,或者在您的生物体对象上实现您的spawn方法,以便它可以将spawn伙伴当作参数,并且这样做可以访问每个属性并将它们混合在一起并创造后代。所以我假设OrganismA.spawn(OrganismB)会创建一个OrganismA-B菌种的后代。

您可以在下面看到重叠逻辑。如果以下四个逻辑表达式有效,那么这两个框重叠。椭圆的数学方法会有所不同,但如果需要,您可以使用椭圆的矩形近似值。我们将以A和B作为有机体对象,并定义它们的最小值和最大值x和y。所以minXB表示b的最小x坐标。

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 

我建议你通过地图的某些分区来提高生物体的效率。示例中的象限,其中0-400宽度0-400高度的每个有机体都位于象限'I'中,等等。从那里你可以获得更好的性能。

def quadrant(Organism): 
    if (Organism.x < 400 and Organism.y < 400): 
     return 'III' 
    elif (... 

def spawnOrganisms(): 
    # 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 spawn 
     for 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) 
0

您应该使用pygame中的Sprite类的,好了,精灵,因为它使你的整个应用程序更易于管理。

我们不需要spawnmovebounce,我们只需使用一个单一的功能update,这将be calledGroup,这反过来我们用,而不是一个简单的列表来保存我们的对象。

每个Sprite都有它自己的图像,我们在其上绘制省略号。然后我们创建一个Maskdo pixel perfect collision

下面是一个完整的运行示例。请注意,我更换了所有您的意见与我自己的,解释这是怎么回事:

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 
def id_generator(): 
    i = 0 
    while True: 
     i += 1 
     yield i 

ids = id_generator() 

# just a helper function that wraps pygame.sprite.collide_mask 
# to prevent a sprite from colliding with itself 
def collide(a, b): 
    if a.id == b.id: 
     return False 
    return pygame.sprite.collide_mask(a, b) 

class Organism(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 movement 
    def update(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 simpler 
     if self.rect.left < 0 or self.rect.right > map_width: 
      self.change_x *= -1 

     if self.rect.top < 0 or 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 themself 
     if self.age < 200: 
      return 

     # just a narbitary limit so the screen does not get too full 
     if self.age > 500: 
      print str(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: 
      print str(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 moment 
     if collided and not collided.id in self.collisions and collided.age > 200 and len(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) 
      print str(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 fun 
      if 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 += 1 
     else: 
      # 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 _ in range(15): 
    Organism(next(ids), org_list) 

while not 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() 

我设置的对象有多少是在屏幕上立即对一些arbitary限制。如果要加快碰撞检测速度,请跳过像素完美碰撞检测并使用矩形或circle。但是最大的改进可能是使用四叉树碰撞检测算法(但这超出了这个答案的范围;只是谷歌它)。

+0

谢谢!我试着将它更新为与Python 3一起工作。但是,一旦第一个对象发生碰撞,我一直会收到一条错误消息,读取第129行和第54行的“无效颜色参数”。 –

+0

@ChristopherCostello是的,因为在Python 3中,'map'不会返回一个列表,而是一个迭代器。只需将它包装在一个列表调用中,如“生物体(下一个(ids),self.org_list,列表(map(int,color)))' – sloth

+0

谢谢!这工作很好。 –

1

您应该使用pygame.Rect来保持位置和大小,然后您可以使用Pygame功能来检查碰撞。

one_rect.colliderect(second_rect) 

one_rect.coolidepoint(mouse_position) 

查看文档等功能为pygame.Rect


Pygame中也pygame.sprite.Group保持精灵的组,检查碰撞所有的精灵。