[英]Cannot get the enemies to chase player pygame
So I have these enemies that are supposed to swan the player like in the old arcade game RoboTron. 因此,我有这些敌人应该像旧的街机游戏RoboTron一样吸引玩家。
The problem is, only a couple of the enemies go for the player, while the others kind of just stand around, and maybe or more of them will come after the player. 问题是,只有几个敌人会追随玩家,而其他敌人只会站在那里,也许更多或更多的敌人会追随玩家。 What is going on here?
这里发生了什么?
Here is the code involved in moving the enemies: 这是移动敌人的代码:
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)
Here is the entire code, for reference: 这是完整的代码,以供参考:
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()
self.rect.x += dx * self.x_speed
self.rect.y += dy * self.y_speed
But x_speed and y_speed are randomly -1, 0, or 1, right? 但是x_speed和y_speed随机为-1、0或1,对吧? Then it will move in a random direction... Maybe you meant the value 1 to be more likely than the value -1.
然后它将向随机方向移动...也许您是说值1比值-1更有可能。
I had a similar problem, which I eventually resolved by separating obj.speed from obj.direction, and implementing a strategy pattern to let me swap out enemy behaviors as needed. 我遇到了类似的问题,最终通过将obj.speed与obj.direction分离,并实现了一种策略模式来解决该问题,以便根据需要交换敌人的行为,从而解决了这个问题。
For example, let Enemy
class have a series of methods it calls during its self.update()
call: 例如,让
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
So self.move()
uses a string to determine which direction the object should go and how to apply its speed. 因此
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
In this way the object is moved based on its self.direction
value. 这样,根据对象的
self.direction
值移动对象。 After this it will execute self.find_rect()
which just sets its Rect to the correct value after changing the object's x and y values. 之后,它将执行
self.find_rect()
,在更改对象的x和y值后,将其Rect设置为正确的值。
Then the seeker behavior can be applied in any number of ways -- as a wrapper to an empty def foo(self): pass
or by using types.MethodType()
or just left as a controller in the main loop, whatever you like best -- but the idea is, it just updates the Enemy
object's self.direction
value. 然后可以以多种方式应用
types.MethodType()
器行为-作为对空def foo(self): pass
的包装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
It's kind of a lot of code and some maths are probably redundant (late night coding will do that, heh) but it basically checks to see that the 'target' (x, y) is further away than its speed will take it, and if it is, which string to set self.direction
to. 这类代码很多,有些数学可能是多余的(深夜编码会做到这一点,呵呵),但基本上可以检查出“目标”(x,y)距离比其速度要快得多,并且如果是,则将
self.direction
设置为哪个字符串。
I mean obviously it doesn't have to be a string to determine self.direction
, it can be whatever system you want -- but I think separating the direction from the movement is helpful. 我的意思是很显然,确定
self.direction
是字符串,它可以是您想要的任何系统-但我认为将方向与运动分开是很有帮助的。 If nothing else one manages to avoid some repetition in code by ensuring that obj.move()
has a consistent implementation. 如果没有别的,可以通过确保
obj.move()
具有一致的实现来设法避免代码中的某些重复。 And it also avoids that ugly obj.speed *= -1
that I see a lot in people's code to indicate something is moving in the other direction. 而且它还避免了我在人们的代码中经常看到的丑陋的
obj.speed *= -1
,它指示事物正在朝着另一个方向发展。 Maybe I'm weird but I don't think that's the right value to be altering; 也许我很奇怪,但我认为这不是改变的正确价值; when I drive home from work, I'm not driving -35 MPH...
当我下班开车回家时,我不会开车-35 MPH ...
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.