Pygame碰撞问题

发布于 2025-01-09 12:57:05 字数 11259 浏览 0 评论 0原文

因此,当我注意到游戏中的碰撞行为很奇怪时,我正在为我的平台游戏编写攻击方法,当我向没有损坏的敌人发起攻击(游戏中的火球)时,我添加了一条打印语句来检查是否他们失去了任何生命值并且没有任何记录,但是当我按住攻击方法(即空格键)时,会对敌人进行多次攻击。

玩家角色为蓝色,敌人为绿色。

控制台显示敌人生命值下降,但仅当按住空格键时才显示。

另外,当我走进敌人精灵并点击空格 时酒吧一旦我也能以这种方式造成伤害,但不能远距离伤害。

敌人内的玩家

角色周围形成的矩形有问题,但我不确定是什么问题错误的。

任何建议都会有帮助。

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.

player within enemy

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

悍妇囚夫 2025-01-16 12:57:05

我在变量 fire 中看到了问题。仅当检测到 K_SPACE 且检测到与火球的碰撞(在 player.attack() 内部)时,才会发生 火==真。因此,仅当您按空格键时才会检查碰撞。我建议将方法 Character.attack() (请注意,按照惯例,类名应该是单数) 分为两种方法:一种仅用于攻击,第二种用于检查碰撞。因此,您可以在主循环中检查是否有任何火球与玩家对象发生碰撞,并独立地施放另一个火球。

除此之外,您可能希望将游戏循环包装在一个函数中(将其命名为 main),并分成几个较小的函数以符合 单一责任原则。在全局范围内(函数和类之外)编写代码几乎总是一种不好的做法,应该避免。另外,注释每一行明显的代码是不必要的(除非只是为了学习目的)。

I see the problem in a variable fire. It is set to True only if K_SPACE is detected and detecting collision (inside player.attack()) with fireball only occurs if fire == True. So collision is checked only if you press the spacebar. I would advise to split method Character.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).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文