【问题标题】:Cannot get the enemies to chase player pygame无法让敌人追逐玩家 pygame
【发布时间】:2013-12-08 05:01:50
【问题描述】:

所以我有这些敌人,它们应该像旧街机游戏 RoboTron 中那样吞噬玩家。

问题是,只有几个敌人会追捕玩家,而其他人则只是站在周围,可能或更多的敌人会追捕玩家。这是怎么回事?

这是移动敌人的代码:

class Beetle(pygame.sprite.Sprite):
    '''class that builds up the player class'''

    x_speed = 0
    y_speed = 0

#Beetle construction stuff (images, goes here)

.
.
.



    def speedChange(self,x,y):
        self.x_speed += x
        self.y_speed += y

    def move_towards_player(self, player):

        #contains the beetle to the screen
        self.rect.clamp_ip(screen_rect)

        # find normalized direction vector (dx, dy) between enemy and player
        dx, dy = self.rect.x - player.rect.x, self.rect.y - player.rect.y
        dist = math.hypot(dx, dy)
        if dist == 0: #prevents a divide by zero erro
            dist = 1
        else:

            dx, dy = dx / dist, dy / dist
        # move along this normalized vector towards the player at current speed
        self.rect.x += dx * self.x_speed
        self.rect.y += dy * self.y_speed

while done == False:
   while beetleCount < 10:
        beetle = Beetle() #make a beetle
        random1 = randint(0, width -1 ) #make random positions for the beetle
        random2 = randint(0, height - 1)

        beetle.rect.x = random1 #make new random nums
        beetle.rect.y = random2

        beetle_list.add(beetle) #add beetle to list
        all_sprites_list.add(beetle)
        beetleCount += 1 #increment count'''

    for bug in beetle_list:
        random1 = randint(-1, 1)
        if random1 != 0:

            random2 = randint(-1, 1)
        else:
            random2 = 0

        bug.speedChange(random1, random2)
        bug.move_towards_player(player)

以下是完整代码,供参考:

import pygame
import math
from pygame import K_SPACE
from random import randint

#Define some colors
black = (0,0,0)
white = (255,255,255)
magenta = (255,0,255)
darkOrange = (28, 44, 64)

pygame.init()

#beetleCount
beetleCount = 0


#set the width and height of the screen
width = 800
height = 400
size = [width,height]
mainScreen = pygame.display.set_mode(size)
#make a rectangle of the screen size.  This is for keeping 
#the moving non-player objects in bounds
screen_rect = pygame.Rect((0,0), (width,height))


#set the window title
pygame.display.set_caption("My Game")

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

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

#list of all the sprites in the game
all_sprites_list = pygame.sprite.Group()

#sounds in the game that are played
gunshot = pygame.mixer.Sound("gunshot.ogg")
beetleDie = pygame.mixer.Sound("orc_die.ogg")
pygame.mixer.music.load("DeadCity.ogg")
pygame.mixer.music.play(-1)

#Functions VVV
def sprite_sheet_load(colorKey, spriteLocX, spriteLocY, spriteSizeX, spriteSizeY, fileName):
        '''purpose: to extract a sprite from a sprite sheet at the choosen location'''
        '''credit to StackOverFlow User hammyThePig for original concept.'''

        sheet = pygame.image.load(fileName).convert()#loads up the sprite sheet.
        sheet.set_colorkey(colorKey) #set the color key

        #grabs the sprite at the given location
        sprite = sheet.subsurface(pygame.Rect(spriteLocX, spriteLocY, spriteSizeX, spriteSizeY))

        #returns the sprite to where ever it was called
        return sprite

##Functions! ^^^

#classes VV

class Player(pygame.sprite.Sprite):
    '''class that builds up the player class'''

    x_speed = 0
    y_speed = 0

    imageIndex = 0

    playerImagesUp = [] #lists for the player images
    playerImagesDown = [] #lists for the player images
    playerImagesLeft = [] #lists for the player images
    playerImagesRight = [] #lists for the player images
    playerDeathImages = [] # ""

    #constructor function
    def __init__(self):#create a self variable to refer to the object

        #call up the pygame sprite constructor
        pygame.sprite.Sprite.__init__(self)

        #get the first row of the sprite sheet (moving up)
        spriteXLoc = 15
        spriteYLoc = 15
        spriteXSize = 34
        spriteYSize = 47

        for x in range(0,9):
                self.playerImagesUp.append(sprite_sheet_load(white, spriteXLoc, spriteYLoc, spriteXSize, spriteYSize, "mainCharacter.png"))
                spriteXLoc += 64

        #get the second row (moving left)
        spriteXLoc = 15
        spriteYLoc = 78
        spriteXSize = 34
        spriteYSize = 48
        for x in range(0,9):
                self.playerImagesLeft.append(sprite_sheet_load(white, spriteXLoc, spriteYLoc, spriteXSize, spriteYSize, "mainCharacter.png"))
                spriteXLoc += 65

        #get the third row (moving down)
        spriteXLoc = 15
        spriteYLoc = 143
        spriteXSize = 33
        spriteYSize = 49        
        for x in range(0,9):
                self.playerImagesDown.append(sprite_sheet_load(white, spriteXLoc, spriteYLoc, spriteXSize, spriteYSize, "mainCharacter.png"))
                spriteXLoc += 64


        #get the fourth row (moving right)
        spriteXLoc = 15
        spriteYLoc = 207
        spriteXSize = 34
        spriteYSize = 48
        for x in range(0,9):
                self.playerImagesRight.append(sprite_sheet_load(white, spriteXLoc, spriteYLoc, spriteXSize, spriteYSize, "mainCharacter.png"))
                spriteXLoc += 65


        self.image = self.playerImagesDown[0]


        self.rect = self.image.get_rect()

    def speedChange(self,x,y):
        '''adjust the player speed'''
        self.x_speed += x
        self.y_speed += y


    def update(self):
        '''animate and move the player'''
        #move the character according to the speed it's moving
        #self.rect.x += self.x_speed
        #self.rect.y += self.y_speed

        self.rect.move_ip(self.x_speed,self.y_speed)

        if self.y_speed < 0: #if the player is moving up

            self.image = self.playerImagesUp[self.imageIndex]
            self.imageIndex += 1
            if self.imageIndex >= len(self.playerImagesUp):
                self.imageIndex = 0

        if self.y_speed > 0: #if the player is moving down

            self.image = self.playerImagesDown[self.imageIndex]
            self.imageIndex += 1
            if self.imageIndex >= len(self.playerImagesDown):
                self.imageIndex = 0

        if self.x_speed > 0: #if the player is moving right

            self.image = self.playerImagesRight[self.imageIndex]
            self.imageIndex += 1
            if self.imageIndex >= len(self.playerImagesRight):
                self.imageIndex = 0

        if self.x_speed < 0: #if the player is moving left

            self.image = self.playerImagesLeft[self.imageIndex]
            self.imageIndex += 1
            if self.imageIndex >= len(self.playerImagesLeft):
                self.imageIndex = 0

class Bullet(pygame.sprite.Sprite):
    '''class the builds up the bullets that the player shoots'''

    #constructor function
    def __init__(self):

        #call up the parent's constructor
        pygame.sprite.Sprite.__init__(self)

        #set the size of the bullet
        self.image = img = pygame.image.load("bullet.png").convert()

        self.rect = self.image.get_rect()

    def move_bullet(self):
        '''function tells the bullet what direction to go into'''
        bulletSpeed=25
        bulletDirection = 'down'

        if self.bulletDirection =='left':#if the player is facing left
            bullet.rect.x -= bulletSpeed #bullet will go in this direction at that speed
        elif self.bulletDirection == 'right': #if the player is facing right
            bullet.rect.x += bulletSpeed
        elif self.bulletDirection == 'down': #if the player is facing down
            bullet.rect.y += bulletSpeed
        elif self.bulletDirection == 'up': #if the player if facing up
            bullet.rect.y -=bulletSpeed


class Beetle(pygame.sprite.Sprite):
    '''class that builds up the player class'''

    x_speed = 0
    y_speed = 0
    imageIndex = 0
    BeetleImagesDown = [] #lists for the beetle images
    BeetleImagesUp = [] #lists for the beetle images
    BeetleImagesLeft = [] #lists for the beetle images
    BeetleImagesRight = [] #lists for the beetle images

    #constructor function
    def __init__(self): #create a self variable to refer to the object

        #call up the parent's constructor
        pygame.sprite.Sprite.__init__(self)



        spriteXLoc=11
        spriteYLoc=14
        spriteXSize=30
        spriteYSize=30
        for x in range (0,5):
            self.BeetleImagesDown.append(sprite_sheet_load(magenta,spriteXLoc,spriteYLoc,spriteXSize,spriteYSize, "beetle.png"))
            spriteXLoc + 49

        spriteXLoc=12
        spriteYLoc=65
        spriteXSize=28
        spriteYSize=31
        for x in range (0,5):
            self.BeetleImagesUp.append(sprite_sheet_load(magenta,spriteXLoc,spriteYLoc,spriteXSize,spriteYSize,"beetle.png"))
            spriteXLoc + 49

        spriteXLoc=8
        spriteYLoc=120
        spriteXSize=35
        spriteYSize=24
        for x in range (0,5):
            self.BeetleImagesRight.append(sprite_sheet_load(magenta,spriteXLoc,spriteYLoc,spriteXSize,spriteYSize,"beetle.png"))
            spriteXLoc + 49

        spriteXLoc=9
        spriteYLoc=171
        spriteXSize=36
        spriteYSize=22
        for x in range (0,5):
            self.BeetleImagesLeft.append(sprite_sheet_load(magenta,spriteXLoc,spriteYLoc,spriteXSize,spriteYSize,"beetle.png"))
            spriteXLoc + 49


        self.image = self.BeetleImagesUp[0]
        self.rect = self.image.get_rect()

    def speedChange(self,x,y):
        self.x_speed += x
        self.y_speed += y

    def update(self):

        #moves the beetle
        self.rect.move_ip(self.x_speed,self.y_speed)

        #contains the beetle to the screen
        self.rect.clamp_ip(screen_rect)

        if self.y_speed < 0: #if the beetle is moving up
            self.image = self.BeetleImagesUp[self.imageIndex]
            self.imageIndex += 1
            if self.imageIndex >= len(self.BeetleImagesUp):
                self.imageIndex = 0

        if self.y_speed > 0: #if the beetle  is moving down

            self.image = self.BeetleImagesDown[self.imageIndex]
            self.imageIndex += 1
            if self.imageIndex >= len(self.BeetleImagesDown):
                self.imageIndex = 0

        if self.x_speed > 0: #if the beetle  is moving right

            self.image = self.BeetleImagesRight[self.imageIndex]
            self.imageIndex += 1
            if self.imageIndex >= len(self.BeetleImagesRight):
                self.imageIndex = 0

        if self.x_speed < 0: #if the beetle  is moving left

            self.image = self.BeetleImagesLeft[self.imageIndex]
            self.imageIndex += 1
            if self.imageIndex >= len(self.BeetleImagesLeft):
                self.imageIndex = 0

    def move_towards_player(self, player):

        #contains the beetle to the screen
        self.rect.clamp_ip(screen_rect)

        # find normalized direction vector (dx, dy) between enemy and player
        dx, dy = self.rect.x - player.rect.x, self.rect.y - player.rect.y
        dist = math.hypot(dx, dy)
        if dist == 0: #prevents a divide by zero erro
            dist = 1
        else:

            dx, dy = dx / dist, dy / dist
        # move along this normalized vector towards the player at current speed
        self.rect.x += dx * self.x_speed
        self.rect.y += dy * self.y_speed


##classes ^^

player = Player()

player.rect.x = 350
player.rect.y = 225

#list of all the bullets in the game
bullet_list = []



#default value to what direction the player is facing
faceWhatDirection = 'down'
all_sprites_list.add(player) #add player to list of objects

beetle_list  = pygame.sprite.Group() #list of beetles in the game


# add the beetle list to list of objects
all_sprites_list.add(beetle_list) 




###Main Program Loop

##NOTE: WATCH WHAT YOU ARE PUTTING IN THE LOOPS! EASY WAY TO GET A BUG!
while done == False:
    #Event processing! --------------------------
    for event in pygame.event.get(): #User did something
        if event.type == pygame.QUIT: #if user clicked close
            done = True #flag that we are done so we exit this loop

        elif event.type == pygame.KEYDOWN:
            if event.key ==pygame.K_LEFT:
                player.speedChange(-3,0)
                #player.animate()
                faceWhatDirection = 'left'
            elif event.key == pygame.K_RIGHT:
                player.speedChange(3,0)
                #player.animate()
                faceWhatDirection = 'right'
            elif event.key ==pygame.K_UP:
                player.speedChange(0,-3)
                #player.animate()
                faceWhatDirection = 'up'
            elif event.key ==pygame.K_DOWN:
                player.speedChange(0,3)
                #player.animate()
                faceWhatDirection = 'down'
            elif event.key == pygame.K_SPACE:
                gunshot.play()
                bullet = Bullet()
                bullet_list.append(bullet)#adds the bullet to the bullet list
                all_sprites_list.add(bullet)#adds the bullet to the sprite list
                #put the bullet in same location as player
                bullet.rect.x = player.rect.x
                #add in a plus 15 so bullet doesn't spawn in player's face
                bullet.rect.y = player.rect.y + 15 
                bullet.bulletDirection = faceWhatDirection

        #user let up on a key
        elif event.type ==pygame.KEYUP:
            #if it is an arrow key, reset speed back to zero.
            if event.key ==pygame.K_LEFT:
                player.speedChange(3,0)
            elif event.key == pygame.K_RIGHT:
                player.speedChange(-3,0)
            elif event.key == pygame.K_UP:
                player.speedChange(0,3)
            elif event.key == pygame.K_DOWN:
                player.speedChange(0,-3)

    player.update() #moves the player around


    #event processing ------------^^

    #Game handling! -------------VV
    ##beetle moving code --------------------------

    #make some beetles
    while beetleCount < 10:
        beetle = Beetle() #make a beetle
        random1 = randint(0, width -1 ) #make random positions for the beetle
        random2 = randint(0, height - 1)

        beetle.rect.x = random1 #make new random nums
        beetle.rect.y = random2

        beetle_list.add(beetle) #add beetle to list
        all_sprites_list.add(beetle)
        beetleCount += 1 #increment count'''

    for bug in beetle_list:
        random1 = randint(-1, 1)
        if random1 != 0:

            random2 = randint(-1, 1)
        else:
            random2 = 0

        bug.speedChange(random1, random2)
        bug.move_towards_player(player)



    #moves the bullets
    for bullet in bullet_list:
        bullet.move_bullet()
        #see if the bullet left the screen
        if bullet.rect.y > 400 or bullet.rect.y < 0:
            all_sprites_list.remove(bullet)
        elif bullet.rect.x > 800 or bullet.rect.x < 0:
            all_sprites_list.remove(bullet)
        #see if bullet hit the beetle
        beetle_hit_list = pygame.sprite.spritecollide(bullet,beetle_list,False)
        for beetle in beetle_hit_list:
        #remove the bullet and beetle if hit 
            #bullet_list.remove(bullet) #delete the bullet
            all_sprites_list.remove(bullet) #remove it from the universal list
            all_sprites_list.remove(beetle) #get rid of the bettle from the universal list
            beetle_list.remove(beetle) #get rid of it from the beetle list
            beetleDie.play() #play the beetle death sound


    #Game Handling! -------------^^



    #Drawing code! --------------VVV

    #clear the screen to a set color
    #NOTE, putting any drawing commands above this will erase
    #whatever you are trying to put there
    mainScreen.fill(white)

    #draw the sprites
    all_sprites_list.draw(mainScreen)
    #update screen on the regular
    pygame.display.flip()

    #Drawing code! -----------------^^

    #limit game frames to 20 frames per second
    clock.tick(20)

#quit the program when the loop is ended

pygame.quit()  

【问题讨论】:

  • 我想你的意思是spriteXLoc += 49,而不是spriteXLoc + 49
  • 感谢您的收获。拥有第二双眼睛总是好的。
  • 不!我希望我能提供更多帮助!

标签: python python-2.7 pygame sprite game-physics


【解决方案1】:
self.rect.x += dx * self.x_speed
self.rect.y += dy * self.y_speed

但是 x_speed 和 y_speed 是随机的 -1、0 或 1,对吗?然后它将向随机方向移动...也许您的意思是值 1 比值 -1 更有可能。

【讨论】:

  • 当我让甲虫在屏幕上跑来跑去时,那是旧代码。问题是,如果我给它分配一个设定的速度,那么它会朝着一个随机的方向前进,而不是追随玩家。
【解决方案2】:

我遇到了类似的问题,我最终通过将 obj.speed 与 obj.direction 分离并实施一种策略模式让我根据需要交换敌人的行为来解决这个问题。

例如,让Enemy 类有一系列在其self.update() 调用期间调用的方法:

def update(self):
    self.unique_action()
    self.shot_check() ##do I shoot or pass?
    self.move()
    self.find_rect() ##make sure the object's Rect is where it needs to be

所以self.move() 使用一个字符串来确定对象应该去哪个方向以及如何应用它的速度。

def move(self):
    """Calculates the new position for the object. 
    Uses strings to determine which direction to move in, 
    and uses a speed constant to determine how many pixels 
    in a given direction to move. A string representing 
    two directions ('upleft' or 'downright' for example) 
    will cause it to divide the speed constant by 1.4 
    so it does not appear to move faster when traveling diagonally."""
    speedConstant = self.speed
    if len(self.direction) > 5: ##can only happen if direction is diagonal!
        speedConstant /= 1.4  ##close enough to sqrt(2) for me
    if 'up' in self.direction:
        self.y -= speedConstant
    if 'down' in self.direction:
        self.y += speedConstant
    if 'left' in self.direction:
        self.x -= speedConstant
    if 'right' in self.direction:
        self.x += speedConstant

通过这种方式,对象根据其self.direction 值移动。在此之后它将执行self.find_rect(),它只是在更改对象的 x 和 y 值后将其 Rect 设置为正确的值。

然后可以通过多种方式应用搜索器行为——作为空def foo(self): pass的包装器或使用types.MethodType()或作为控制器留在主循环中,无论你最喜欢什么——但是这个想法是,它只是更新Enemy 对象的self.direction 值。

def enemy_rammer(self):
    """Compares its x and y coordinates against the target and moves toward it.
    If the ship is respawning, the target is its own x and y of origin. 
    If the ship is NOT respawning, the ship is of course the target."""
    self.cooldown = 5 #placeholder, keeps it from seeking AND shooting
    selfX, selfY = self.rect.center
    seekX, seekY = self.xy if ship.respawn else ship.rect.center ##makes it 'go home' if the ship is respawning
    newDirection = '' #safe to assign to self.direction - it won't move
    absX = abs(selfX - seekX)
    absY = abs(selfY - seekY)
    if math.hypot(absX, absY) > self.speed:
        if seekY > selfY and absY > self.speed:
            newDirection += 'down'
        elif seekY < selfY:
            newDirection += 'up'
        else:
            pass
        if seekX > selfX and absX > self.speed:
            newDirection += 'right'
        elif seekX < selfX:
            newDirection += 'left'
        else:
            pass
        self.direction = newDirection

这是一种很多代码,一些数学可能是多余的(深夜编码会这样做,呵呵)但它基本上会检查“目标”(x,y)是否比它的速度更远它,如果是,将self.direction 设置为哪个字符串。

我的意思很明显,它不必是一个字符串来确定self.direction,它可以是你想要的任何系统——但我认为将方向运动 很有帮助。如果没有别的办法,可以通过确保obj.move() 具有一致的实现来避免代码中的一些重复。它还避免了丑陋的obj.speed *= -1,我在人们的代码中看到很多,表明某些事情正在朝着另一个方向发展。也许我很奇怪,但我认为这不是改变的正确价值;当我下班开车回家时,我不会开 -35 MPH...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-11
    • 2013-12-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多