Pygame碰撞问题
因此,当我注意到游戏中的碰撞行为很奇怪时,我正在为我的平台游戏编写攻击方法,当我向没有损坏的敌人发起攻击(游戏中的火球)时,我添加了一条打印语句来检查是否他们失去了任何生命值并且没有任何记录,但是当我按住攻击方法(即空格键)时,会对敌人进行多次攻击。
另外,当我走进敌人精灵并点击空格 时酒吧一旦我也能以这种方式造成伤害,但不能远距离伤害。
角色周围形成的矩形有问题,但我不确定是什么问题错误的。
任何建议都会有帮助。
import pygame #imports the pygame library
#variables for player
moveright = False #declared a variable to store boolean value for if player is moving right
moveleft = False #declared a variable to store boolean value for if player is moving left
fire = False
#constant variables
width = 700
height = 700
size_of_screen = (width, height) #declared a variable to store the height and width of the screen
bg_colour = (222, 203, 104) #declared a variable to store the bg colour of screen
pf_colour = (0, 0, 0)
FPS = 60 #declared variable to store the speed at which game runs
gravity = 0.5
#init
pygame.init() #all imported python modules are initialised
screen = pygame.display.set_mode(size_of_screen) #a game window is initialised
pygame.display.set_caption("Platform Game") #game windows caption is set
system_clock = pygame.time.Clock() #declared variable to control speed of game
fireball_img = pygame.image.load("FB500-5.png").convert_alpha()
class Characters(pygame.sprite.Sprite): #base class is created and inherits from sprite class
def __init__(self, char, x, y, scale, speed, health): #values are put in the constructor method
pygame.sprite.Sprite.__init__(self) #constructor from sprite class is called
self.health = health
self.max_health = self.health
self.attack_cooldown = 0
self.direction = 1
self.air = True
self.char = char
self.life = True #attribute initialised to store state of characters life
self.jump = False
self.vv = 0
self.speed = speed #attribute for character is initialised to store speed of character
self.spin = False #attribute initialised to store direction character is moving
self.animation_list = [] #attribute initialised to store all sequence of animations for sprites within 2D list
self.index = 0 #attribute initialised to locate index of sequences within specific animation
self.activity = 0 #attribute initialised to locate index of animation sequences within animation list
self.time = pygame.time.get_ticks() #attribute declared to store current time game has been running
types_of_animation = ['idle', 'moving', 'jump']
for ani in types_of_animation:
templist = [] #declared a variable which will be used to store a temporary list
for x in range(10): #loops through numbers 0-10
try:
img = pygame.image.load(f'{self.char}/{self.char}.{ani}.sprite_{x}.png').convert_alpha() #loads an image from your file
img = pygame.transform.scale(img, (int(scale), int(scale))) #resizes your image
templist.append(img) #resized image is added to temporary list
except:
break
self.animation_list.append(templist) #adds all images passed into temporary list to main one
self.img = self.animation_list[self.activity][self.index] #attribute initialised to store intial image of sprite
self.rect = self.img.get_rect() #a rectangle is formed around sprite
self.rect.center = (x+100, y-50) #centre of rectangle varies with the position of sprite
def movecharacter(self, moveleft, moveright): #new method defined to move character
#change in movement variables
dy = 0 #declared a variable to store the change in distance travelled in the vertical direction
dx = 0 #declared a variable to store the change in distance travelled in the horizontal direction
#movement variables are updated if there are movements left or right
if moveleft: #checks to see if user has inputted "a" key
dx -= self.speed #character has moved backwards
self.direction = -1
self.spin = True #character changed direction(faces left)
if moveright: #checks to see if the user has inputted "d" key
dx += self.speed #character has moved forward
self.spin = False #character changed direction(faces right)
self.direction = 1
#these variables are for jumping
if self.jump and not self.air: #checks to see if user has inputted "w" key and is in mid-air
self.vv = -10 #determines how high the character can jump
self.jump = False #this variable is set to false so character doesn't fly of the screen
self.air = True #this variable is set to true so character can't infinitely jump while in mid-dair
#gravity is applied here
self.vv += gravity #vertical velocity is incremented until character starts to move in opposite direction
if self.vv > 9: #checks to see if character's vertical velocity has passed 9
self.vv = 9 #vertical velocity is capped to 9
dy += self.vv #change in distance travelled in vertical direction is increased
if self.rect.bottom + dy > 400: #checks if distance the player travelled
dy = 400 - self.rect.bottom
self.air = False
#rectangle position is updated
self.rect.x += dx #position of rectangle has changed in horizontal direction
self.rect.y += dy #position of rectangle has changed in vertical direction
def attack(self):
if not self.attack_cooldown:
self.attack_cooldown = 50
ball = Fireball(self.rect.centerx, self.rect.centery, self.direction, self.spin)
fireball_group.add(ball)
if pygame.sprite.spritecollide(player, fireball_group, False):
if player.life:
player.health -=5
self.kill()
if pygame.sprite.spritecollide(enemy, fireball_group, False):
if player.life:
enemy.health -=25
print(enemy.health)
self.kill()
def cooldown(self):
if self.attack_cooldown > 0:
self.attack_cooldown -=1
def animation(self): #new method defined to change characters animation
animation_timer = 100 #declared variable to store time for how long character is in each animation
self.img = self.animation_list[self.activity][self.index] #characters animation is updated
if pygame.time.get_ticks() - self.time > animation_timer: #checks to see if it is time to update sprites
self.time = pygame.time.get_ticks() #time game has been running for is redefined
self.index += 1 #index for current animation sequence is increased so next animation can be outputted
if self.index >= len(self.animation_list[self.activity]): #checks to see if index value has passed the highest index of animation sequence list
self.index = 0 #index is reset back to initial value
def update_activity(self, updated_activity): #new method defined to update which animation sequence character is in
if updated_activity != self.activity: #checks if current activity is equal to new activity
self.activity = updated_activity #value for activity is updated
self.index = 0 #index is reset to initial value
self.time = pygame.time.get_ticks() #time game has been running is updated
def draw(self): #new method defined to draw images to screen
screen.blit(pygame.transform.flip(self.img, self.spin, False), self.rect) #draws image to screen
class Fireball(pygame.sprite.Sprite):
def __init__(self, x, y, direction, spin):
pygame.sprite.Sprite.__init__(self)
self.spin = spin
self.direction = direction
self.speed = 10
self.image = fireball_img
self.image = pygame.transform.scale(self.image, (50,50))
self.image = pygame.transform.flip(self.image, self.spin, False)
self.rect = self.image.get_rect()
self.rect.center = (x,y)
def update(self):
self.rect.x +=(self.direction * self.speed)
if self.rect.right < 100 or self.rect.left > width - 100:
self.kill()
fireball_group = pygame.sprite.Group()
player = Characters("player", 100, 200, 100, 5, 100)
enemy = Characters("enemy", 10, 400, 100, 5, 100)
run = True #declared a variable to store a boolean value to determine state of game
while run: #game loop-infinite loop which runs game until game is finished
#draw
screen.fill(bg_colour) #displays the specified bg colour
pygame.draw.line(screen, pf_colour, (0, 400), (width, 400))
player.draw() #calls the draw method from base class so character can move
enemy.draw()
fireball_group.update()
fireball_group.draw(screen)
pygame.display.flip() #the contents of entire display are updated
system_clock.tick(FPS) #function declared to limit the speed game runs at
player.animation() #calls animation method from base class so characters animation is updated
player.cooldown()
if player.life: #checks to see if player is alive
if fire:
player.attack()
if player.air: #checks to see if player is in air
player.update_activity(2) #updates the value of activity attribute within base class
elif moveleft or moveright: #checks if player is moving
player.update_activity(1) #updates the value of activity attribute within base class
else:
player.update_activity(0) #current value of acitivity remains 0
player.movecharacter(moveleft, moveright) #calls movecharacter method from base class so character can move
#input
for event in pygame.event.get(): #loops through all the events registered by user
#quit game
if event.type == pygame.QUIT: #checks to see if user has selected the exit button
run = False #state of game is set to false to signal game window has been closed
#keyboard inputs
if event.type == pygame.KEYDOWN: #checks to see if user has inputted a key
if event.key == pygame.K_a: #checks to see if user has inputted "a" key
moveleft = True #variable is set to true so character moves left
if event.key == pygame.K_d: #checks to see if user has inputted "d" key
moveright = True #variable is set to true so character moves right
if event.key == pygame.K_w and player.life: #checks to see if the user has inputted "w" key
player.jump = True #variable is set to true so character jumps
if event.key == pygame.K_SPACE:
fire = True
#keyboard input released
if event.type == pygame.KEYUP: #checks to see if user released a key
if event.key == pygame.K_a: #checks to see if user has inputted "a" key
moveleft = False #variable is set to false so character stops moving left
if event.key == pygame.K_d: #checks to see if user has inputted the "d" key
moveright = False #variable is set to false so character stops moving right
if event.key == pygame.K_SPACE:
fire = False
#quit
pygame.quit() #all pygame modules are uninitialised
So I was coding in the attack method for my platform game when I noticed that the collisions behaved weirdly in my game, when I launched an attack(fireballs in my game) to the enemy no damaged registered, I added a print statement to check if they lose any health and nothing registers, however when I hold down the attack method(which is the space bar) multiple attacks are registered on the enemy.
players character is blue one and enemy is green one.
Console shows decrease in enemies health but only when the space bar is held down.
Also when I walk into the enemies sprite and I tap the space bar once I can deal damage that way as well but not at a distance.
There is something wrong with the rectangle formed around the characters but I am not sure what's wrong.
Any suggestions would be helpful.
import pygame #imports the pygame library
#variables for player
moveright = False #declared a variable to store boolean value for if player is moving right
moveleft = False #declared a variable to store boolean value for if player is moving left
fire = False
#constant variables
width = 700
height = 700
size_of_screen = (width, height) #declared a variable to store the height and width of the screen
bg_colour = (222, 203, 104) #declared a variable to store the bg colour of screen
pf_colour = (0, 0, 0)
FPS = 60 #declared variable to store the speed at which game runs
gravity = 0.5
#init
pygame.init() #all imported python modules are initialised
screen = pygame.display.set_mode(size_of_screen) #a game window is initialised
pygame.display.set_caption("Platform Game") #game windows caption is set
system_clock = pygame.time.Clock() #declared variable to control speed of game
fireball_img = pygame.image.load("FB500-5.png").convert_alpha()
class Characters(pygame.sprite.Sprite): #base class is created and inherits from sprite class
def __init__(self, char, x, y, scale, speed, health): #values are put in the constructor method
pygame.sprite.Sprite.__init__(self) #constructor from sprite class is called
self.health = health
self.max_health = self.health
self.attack_cooldown = 0
self.direction = 1
self.air = True
self.char = char
self.life = True #attribute initialised to store state of characters life
self.jump = False
self.vv = 0
self.speed = speed #attribute for character is initialised to store speed of character
self.spin = False #attribute initialised to store direction character is moving
self.animation_list = [] #attribute initialised to store all sequence of animations for sprites within 2D list
self.index = 0 #attribute initialised to locate index of sequences within specific animation
self.activity = 0 #attribute initialised to locate index of animation sequences within animation list
self.time = pygame.time.get_ticks() #attribute declared to store current time game has been running
types_of_animation = ['idle', 'moving', 'jump']
for ani in types_of_animation:
templist = [] #declared a variable which will be used to store a temporary list
for x in range(10): #loops through numbers 0-10
try:
img = pygame.image.load(f'{self.char}/{self.char}.{ani}.sprite_{x}.png').convert_alpha() #loads an image from your file
img = pygame.transform.scale(img, (int(scale), int(scale))) #resizes your image
templist.append(img) #resized image is added to temporary list
except:
break
self.animation_list.append(templist) #adds all images passed into temporary list to main one
self.img = self.animation_list[self.activity][self.index] #attribute initialised to store intial image of sprite
self.rect = self.img.get_rect() #a rectangle is formed around sprite
self.rect.center = (x+100, y-50) #centre of rectangle varies with the position of sprite
def movecharacter(self, moveleft, moveright): #new method defined to move character
#change in movement variables
dy = 0 #declared a variable to store the change in distance travelled in the vertical direction
dx = 0 #declared a variable to store the change in distance travelled in the horizontal direction
#movement variables are updated if there are movements left or right
if moveleft: #checks to see if user has inputted "a" key
dx -= self.speed #character has moved backwards
self.direction = -1
self.spin = True #character changed direction(faces left)
if moveright: #checks to see if the user has inputted "d" key
dx += self.speed #character has moved forward
self.spin = False #character changed direction(faces right)
self.direction = 1
#these variables are for jumping
if self.jump and not self.air: #checks to see if user has inputted "w" key and is in mid-air
self.vv = -10 #determines how high the character can jump
self.jump = False #this variable is set to false so character doesn't fly of the screen
self.air = True #this variable is set to true so character can't infinitely jump while in mid-dair
#gravity is applied here
self.vv += gravity #vertical velocity is incremented until character starts to move in opposite direction
if self.vv > 9: #checks to see if character's vertical velocity has passed 9
self.vv = 9 #vertical velocity is capped to 9
dy += self.vv #change in distance travelled in vertical direction is increased
if self.rect.bottom + dy > 400: #checks if distance the player travelled
dy = 400 - self.rect.bottom
self.air = False
#rectangle position is updated
self.rect.x += dx #position of rectangle has changed in horizontal direction
self.rect.y += dy #position of rectangle has changed in vertical direction
def attack(self):
if not self.attack_cooldown:
self.attack_cooldown = 50
ball = Fireball(self.rect.centerx, self.rect.centery, self.direction, self.spin)
fireball_group.add(ball)
if pygame.sprite.spritecollide(player, fireball_group, False):
if player.life:
player.health -=5
self.kill()
if pygame.sprite.spritecollide(enemy, fireball_group, False):
if player.life:
enemy.health -=25
print(enemy.health)
self.kill()
def cooldown(self):
if self.attack_cooldown > 0:
self.attack_cooldown -=1
def animation(self): #new method defined to change characters animation
animation_timer = 100 #declared variable to store time for how long character is in each animation
self.img = self.animation_list[self.activity][self.index] #characters animation is updated
if pygame.time.get_ticks() - self.time > animation_timer: #checks to see if it is time to update sprites
self.time = pygame.time.get_ticks() #time game has been running for is redefined
self.index += 1 #index for current animation sequence is increased so next animation can be outputted
if self.index >= len(self.animation_list[self.activity]): #checks to see if index value has passed the highest index of animation sequence list
self.index = 0 #index is reset back to initial value
def update_activity(self, updated_activity): #new method defined to update which animation sequence character is in
if updated_activity != self.activity: #checks if current activity is equal to new activity
self.activity = updated_activity #value for activity is updated
self.index = 0 #index is reset to initial value
self.time = pygame.time.get_ticks() #time game has been running is updated
def draw(self): #new method defined to draw images to screen
screen.blit(pygame.transform.flip(self.img, self.spin, False), self.rect) #draws image to screen
class Fireball(pygame.sprite.Sprite):
def __init__(self, x, y, direction, spin):
pygame.sprite.Sprite.__init__(self)
self.spin = spin
self.direction = direction
self.speed = 10
self.image = fireball_img
self.image = pygame.transform.scale(self.image, (50,50))
self.image = pygame.transform.flip(self.image, self.spin, False)
self.rect = self.image.get_rect()
self.rect.center = (x,y)
def update(self):
self.rect.x +=(self.direction * self.speed)
if self.rect.right < 100 or self.rect.left > width - 100:
self.kill()
fireball_group = pygame.sprite.Group()
player = Characters("player", 100, 200, 100, 5, 100)
enemy = Characters("enemy", 10, 400, 100, 5, 100)
run = True #declared a variable to store a boolean value to determine state of game
while run: #game loop-infinite loop which runs game until game is finished
#draw
screen.fill(bg_colour) #displays the specified bg colour
pygame.draw.line(screen, pf_colour, (0, 400), (width, 400))
player.draw() #calls the draw method from base class so character can move
enemy.draw()
fireball_group.update()
fireball_group.draw(screen)
pygame.display.flip() #the contents of entire display are updated
system_clock.tick(FPS) #function declared to limit the speed game runs at
player.animation() #calls animation method from base class so characters animation is updated
player.cooldown()
if player.life: #checks to see if player is alive
if fire:
player.attack()
if player.air: #checks to see if player is in air
player.update_activity(2) #updates the value of activity attribute within base class
elif moveleft or moveright: #checks if player is moving
player.update_activity(1) #updates the value of activity attribute within base class
else:
player.update_activity(0) #current value of acitivity remains 0
player.movecharacter(moveleft, moveright) #calls movecharacter method from base class so character can move
#input
for event in pygame.event.get(): #loops through all the events registered by user
#quit game
if event.type == pygame.QUIT: #checks to see if user has selected the exit button
run = False #state of game is set to false to signal game window has been closed
#keyboard inputs
if event.type == pygame.KEYDOWN: #checks to see if user has inputted a key
if event.key == pygame.K_a: #checks to see if user has inputted "a" key
moveleft = True #variable is set to true so character moves left
if event.key == pygame.K_d: #checks to see if user has inputted "d" key
moveright = True #variable is set to true so character moves right
if event.key == pygame.K_w and player.life: #checks to see if the user has inputted "w" key
player.jump = True #variable is set to true so character jumps
if event.key == pygame.K_SPACE:
fire = True
#keyboard input released
if event.type == pygame.KEYUP: #checks to see if user released a key
if event.key == pygame.K_a: #checks to see if user has inputted "a" key
moveleft = False #variable is set to false so character stops moving left
if event.key == pygame.K_d: #checks to see if user has inputted the "d" key
moveright = False #variable is set to false so character stops moving right
if event.key == pygame.K_SPACE:
fire = False
#quit
pygame.quit() #all pygame modules are uninitialised
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我在变量
fire
中看到了问题。仅当检测到K_SPACE
且检测到与火球的碰撞(在player.attack()
内部)时,才会发生火==真
。因此,仅当您按空格键时才会检查碰撞。我建议将方法Character.attack()
(请注意,按照惯例,类名应该是单数) 分为两种方法:一种仅用于攻击,第二种用于检查碰撞。因此,您可以在主循环中检查是否有任何火球与玩家对象发生碰撞,并独立地施放另一个火球。除此之外,您可能希望将游戏循环包装在一个函数中(将其命名为
main
),并分成几个较小的函数以符合 单一责任原则。在全局范围内(函数和类之外)编写代码几乎总是一种不好的做法,应该避免。另外,注释每一行明显的代码是不必要的(除非只是为了学习目的)。I see the problem in a variable
fire
. It is set toTrue
only ifK_SPACE
is detected and detecting collision (insideplayer.attack()
) with fireball only occurs iffire == True
. So collision is checked only if you press the spacebar. I would advise to split methodCharacter.attack()
(note that by convention class names should be singular) into 2 methods: one for attacking only and second for checking for collision. So then you can check in main loop if any fireball collides with the player object and independently cast another fireball.Besides that you may want to wrap your game loop in a function (name it sth like
main
) and split into several smaller functions to comply with Single-responsibility principle. Having code in a global scope (outside functions and classes) is almost always a bad practice and should be avoided. Also commenting every obvious line of code is unnecessary (unless just for learning purposes).