Pygame: Accessing colliding sprite's properties using spritecollide()

时间:2015-10-30 21:23:59

标签: python pygame

I am building a top down shooter in the style of Raiden 2. I need to know how to get access to the enemy object when I detect collision using 'spritecollide'.

The reason I need the enemy object is so that I can lower their energy level for every bullet that hits them.

This is the dictionary that I have to work with:

enemyCollisions = pygame.sprite.groupcollide(shipBulletGroup, enemyGroup, True, False)

Here's my Bullet class:

class Bullet(Entity):
    def __init__(self, ship, angle):
        Entity.__init__(self)
        self.speed = 8
        self.level = 0
        self.image = pygame.Surface((BULLET_DIMENSIONS, BULLET_DIMENSIONS)).convert()
        self.rect = self.image.get_rect()
        self.rect.x = ship.rect.centerx
        self.rect.y = ship.rect.top
        self.angle = angle

    def update(self, enemyGroup):
        if self.rect.bottom < 0:
            self.kill()
        else:
            self.rect.y -= self.speed

Here's my unfinished Enemy class:

class Enemy_1(Entity):
    def __init__(self):
        Entity.__init__(self)
        self.speed = (1, 1)
        self.energy = 10
        self.begin = False
        self.run = False
        self.image = pygame.Surface((WIN_W/5, WIN_H/15)).convert()
        self.image.fill((70, 70, 70))
        self.rect = self.image.get_rect()
        self.rect.centerx = WIN_W/1.333
        self.rect.y = -WIN_H/15

    def shoot(self):
        bullet_1 = Bullet(self, 270)
        bullet_2 = Bullet(self, 225)
        bullet_3 = Bullet(self, 315)

    def update(self, enemyGroup, timer):
        if timer == 300:
            self.begin = True
        elif timer == 900:
            self.run = True
        elif timer > 900 and self.rect.x > WIN_W:
            self.remove(enemyGroup)

        if self.run:
            self.rect.x += self.speed[0]
            self.rect.y += self.speed[1]
        elif self.begin:
            self.rect.y += self.speed[1]
            if self.rect.y > WIN_H/4:
                self.rect.y = WIN_H/4

        if self.energy == 0:
            self.kill()

Here's the complete program:

import sys, pygame, os, random, math

from ast import literal_eval

# Force static position of screen
os.environ['SDL_VIDEO_CENTERED'] = '1'

# Constants
LEFT = 'left'
RIGHT = 'right'

BLACK = (0, 0, 0)
WHITE = (255, 255, 255)

PILL_WIDTH = 5
PILL_HEIGHT = 20

WIN_W = 500
WIN_H = 800

SHIP_WIDTH = WIN_W/15
SHIP_HEIGHT = WIN_H/15

BULLET_DIMENSIONS = 5
TIMER = 0


class Entity(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)


class Text(Entity):
    def __init__(self, text, size, color, position, font=None):
        Entity.__init__(self)
        self.color = color
        self.font = pygame.font.Font(font, size)
        self.text = text
        self.set(text, position)

    def set(self, text, position):
        self.image = self.font.render(str(text), 1, self.color)
        self.rect = self.image.get_rect()
        self.rect.move_ip(position[0]-self.rect.width/2, (position[1]-self.rect.height)/2)


class Ship(Entity):
    def __init__(self, container):
        Entity.__init__(self)
        self.speed = 5
        self.score = 0
        self.image = pygame.Surface((SHIP_WIDTH, SHIP_HEIGHT)).convert()
        self.rect = self.image.get_rect()
        self.rect.centerx = container.centerx
        self.rect.y = container.centery

    def update(self, bulletGroup):
        key = pygame.key.get_pressed()
        if key[pygame.K_w]:
            self.rect.centery -= self.speed
        if key[pygame.K_s]:
            self.rect.centery += self.speed
        if key[pygame.K_d]:
            self.rect.centerx += self.speed
        if key[pygame.K_a]:
            self.rect.centerx -= self.speed
        if key[pygame.K_SPACE]:
            if TIMER % 7 == 0:
                bullet = Bullet(self, 90, 'friend')
                bulletGroup.add(bullet)

        # Ship Movement Boundaries
        if self.rect.y < WIN_H/25:
            self.rect.y = WIN_H/25
        if self.rect.y > WIN_H - SHIP_HEIGHT:
            self.rect.y = WIN_H - SHIP_HEIGHT
        if self.rect.x < 0:
            self.rect.x = 0
        if self.rect.x > WIN_W - SHIP_WIDTH:
            self.rect.x = WIN_W - SHIP_WIDTH

class Bullet(Entity):
    def __init__(self, ship, angle, type):
        Entity.__init__(self)
        self.speed = 8
        self.level = 0
        self.image = pygame.Surface((BULLET_DIMENSIONS, BULLET_DIMENSIONS)).convert()
        self.rect = self.image.get_rect()
        self.dx = math.cos(math.radians(angle)) * self.speed
        self.dy = math.sin(math.radians(angle)) * self.speed
        self.setXY(ship, type)

    def setXY(self, ship, type):
        self.rect.x = ship.rect.centerx
        if type == 'friend':
            self.rect.y = ship.rect.top
        elif type == 'enemy':
            self.rect.y = ship.rect.bottom

    def update(self):
        if self.rect.bottom < 0:
            self.kill()
        else:
            self.rect.x -= self.dx
            self.rect.y -= self.dy
            if type == 'friend':
                self.rect.y = -self.rect.y
                self.rect.x = -self.rect.x


class Powerup(Entity):
    def __init__(self, xden, pillCount):
        Entity.__init__(self)
        self.speed = 3
        self.density = xden
        self.image = pygame.Surface((PILL_WIDTH, PILL_HEIGHT)).convert()
        self.rect = self.image.get_rect()
        self.rect = self.rect.move(100,100)

    def restart(self):
        pass

    def update(self):
        if self.rect.y > WIN_H:
            del self
        else:
            self.rect = self.rect.move((0, self.speed))


class Enemy(Entity):
    def __init__(self):
        Entity.__init__(self)
        self.begin = False
        self.run = False
        self.color = (153, 0, 76)

class Planes(Enemy):
    def __init__(self, speed, energy, size, location):
        Enemy.__init__(self)
        self.speed = speed
        self.energy = energy
        self.image = pygame.Surface(size).convert()
        self.image.fill(self.color)
        self.rect = self.image.get_rect()
        self.rect.centerx = location[0]
        self.rect.y = location[1]

    def shoot(self, enemyBulletGroup):
        bullet_1 = Bullet(self, 240, 'enemy')
        bullet_2 = Bullet(self, 270, 'enemy')
        bullet_3 = Bullet(self, 300, 'enemy')
        enemyBulletGroup.add(bullet_1, bullet_2, bullet_3)

    def update(self, enemyGroup, enemyBulletGroup):
        if TIMER == 300:
            self.begin = True
        elif TIMER == 900:
            self.run = True
        elif self.rect.x > WIN_W or self.energy == 0:
            self.kill()

        if self.run:
            self.rect.x += self.speed[0]
            self.rect.y += self.speed[1]
            if TIMER % 100 == 0:
                self.shoot(enemyBulletGroup)
        elif self.begin:
            self.rect.y += self.speed[1]
            if self.rect.y > WIN_H/4:
                self.rect.y = WIN_H/4
                if TIMER % 100 == 0:
                    self.shoot(enemyBulletGroup)

def main():

    # Initialize Everything
    pygame.init()
    global TIMER
    fps = 60
    lLeft = lRight = lUp = lDown = shoot = False
    clock = pygame.time.Clock()
    play = True
    pygame.display.set_caption('Pong')
    screen = pygame.display.set_mode((WIN_W, WIN_H), pygame.SRCALPHA)

    # Create Game Objects
    ship = Ship(pygame.rect.Rect(0, 0, WIN_W, WIN_H))
    score1 = Text("Score: " + str(ship.score), 40, BLACK, (WIN_W/2, (WIN_H/25)))
    enemy_1 = Planes((1, 1), 10, (WIN_W/5, WIN_H/15), (WIN_W/1.4, -WIN_H/15))

    # Create Groups
    powerupGroup = pygame.sprite.Group()
    shipGroup = pygame.sprite.Group()
    shipGroup.add(ship)
    textGroup = pygame.sprite.Group()
    textGroup.add(score1)
    shipBulletGroup = pygame.sprite.Group()
    enemyBulletGroup = pygame.sprite.Group()
    enemyGroup = pygame.sprite.Group()
    enemyGroup.add(enemy_1)


    # Gameplay
    while play:
        # Checks if window exit button pressed
        for event in pygame.event.get():
            if event.type == pygame.QUIT: sys.exit()
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    pygame.quit()
                    sys.exit()

        # Update
        powerupGroup.update()
        ship.update(shipBulletGroup)
        shipBulletGroup.update()
        textGroup.update()
        enemyGroup.update(enemyGroup, enemyBulletGroup)
        enemyBulletGroup.update()


        enemyCollisions = pygame.sprite.groupcollide(shipBulletGroup, enemyGroup, True, False)
        for key in enemyCollisions:
            print enemyCollisions[key].energy


        # Print Background/Sprites
        screen.fill(WHITE)
        powerupGroup.draw(screen)
        shipGroup.draw(screen)
        shipBulletGroup.draw(screen)
        textGroup.draw(screen)
        enemyGroup.draw(screen)
        enemyBulletGroup.draw(screen)

        # Print Score Bar
        hori_partition = pygame.Surface((WIN_W, 1))
        screen.blit(hori_partition, (0, WIN_H/25))

        TIMER += 1
        # Limits frames per iteration of while loop
        clock.tick(fps)
        # Writes to main surface
        pygame.display.flip()

if __name__ == "__main__":
    main()

2 个答案:

答案 0 :(得分:1)

pygame.sprite.groupcollide() 函数返回值为列表对象的字典

  

group1 中的每个Sprite都会添加到返回字典中。每个项目的 group2 中相交的精灵列表。

这就是为什么在迭代返回dict时调用print enemyCollisions[key].energy失败的原因,因为列表对象 - enemyCollisions[key]的值 - 没有energy()方法。

要在Python 2.X中访问字典的所有值,您可以使用dict实例的.itervalues()方法。要获取每个碰撞的精灵,请再次迭代这些值:

enemyCollisions = pygame.sprite.groupcollide(shipBulletGroup, enemyGroup, True, False)

#iterate over all values of the enemyCollisions dict
for enemyCollisions in enemyCollisions.itervalues():

    #access all enemies which collided with an shipBullet
    for enemy in enemyCollisions:
        print enemy.energy

答案 1 :(得分:0)

访问碰撞精灵属性的语法如下:

collisions = pygame.sprite.spritecollide(self, pillGroup, True)
    for key in collisions:
        self.density += key.density