【问题标题】:Pygame Beat-Em-Up Game // Enemy Attack Timer Never Reaches ZeroPygame Beat-Em-Up Game // 敌人攻击计时器永远不会归零
【发布时间】:2017-04-03 19:44:04
【问题描述】:

Pastebin Link

我正在与 Pygame 合作开发一款节拍游戏,我遇到了一个问题,即敌人会永远抓住玩家而不让他走。计时器应该运行,当它到达零时,敌人应该将玩家推开,但它永远不会到达零。

变量 contact 是一组实际接触玩家的敌人。它使用 pygame.sprite.Group() 函数。如果精灵重叠(玩家和所有敌人列表中的任何敌人),则将它们添加到组中。如果精灵停止重叠(敌人走开,或玩家走开),则敌人将从该组中移除。

contact = pygame.sprite.spritecollide(heroBoonrit, enemy_list, False)

对于时钟的每一个滴答声,我都设置了它来检查是否有任何敌人接触玩家,如果有,则遍历每个敌人并查看是否有任何敌人处于抓取状态(villan.grabbing_cooldown)。它只是一个开/关开关,这意味着敌人当前正在使用抓取动作进行攻击。我可能会想出一个更合乎逻辑的变量名。

如果满足这些条件,就会发生一些事情,例如将玩家的位置向上(或向下)捕捉,以便与敌人处于相同的 y 坐标上。变量 heroBoonrit.held_cooldown 是另一个开关,表示他当前被关押。玩家和敌人都有自己的变量,分别称为 heroBoonrit.held_countdownvillan.grabbing_countdown。我看到的问题(通过运行 print() 进行诊断是敌人倒计时递减 1 然后停止递减,而我的英雄的倒计时计时器递减到 0。因此敌人永远不会放手。

我感觉可能有一种更优雅、更简洁的方式来处理玩家和敌人的行为,而不是为玩家设置与是否被击晕、抓住等相关的开关(以及此外,还有相应的倒数计时器),但我只做了一个月的编程,如果有任何反馈,我将不胜感激。也许在时钟的每一个滴答声之后,敌人的持有计时器再次重置为 60...我在其他帖子中读到,当您使用 Pygame Group() 函数时,您无法轻松地遍历该组。仍在学习许多细节......

我在我的主游戏循环中运行了这个命令,发现敌人的抓取倒计时只从 60 变为 59,然后停止倒计时:

print("||Shit Clown|| Grabbing = ", enemyShit_Clown.grabbing_cooldown, " Countdown = ", enemyShit_Clown.grabbing_countdown, "||Boonrit|| Grabbed = ", heroBoonrit.held_cooldown, " Countdown = ", heroBoonrit.held_countdown)

这是我遇到问题的代码块。

    for villan in contact:
        for villan in enemy_list:
            if villan.grabbing_cooldown:
                heroBoonrit.held_cooldown = True
                heroBoonrit.rect.y = villan.rect.y
                heroBoonrit.rect.x = (villan.rect.x + 30)
                heroBoonrit.held_countdown = villan.grabbing_countdown
                villan.grabbing_countdown -= 1
                heroBoonrit.held_countdown -= 1
                if villan.grabbing_countdown <= 0:
                    villan.grabbing_countdown = 0
                    heroBoonrit.held_countdown = 0
                    villan.grabbing_cooldown = False
                    heroBoonrit.held_cooldown = False
                    heroBoonrit.rect.x += 30
            elif villan.attacking_cooldown:
                if heroBoonrit.blocking != True:
                    heroBoonrit.hp -= 100
                else:
                    heroBoonrit.hp -= 10

这里是 Enemy 类:

class Enemy(pygame.sprite.Sprite):
def __init__(self, name, level, speed, hp, stamina, fear, blocking_cooldown, jumping_cooldown, attacking_cooldown, held_cooldown, knockdown_cooldown, stun_cooldown, jumping_countdown, attacking_countdown, held_countdown, knockdown_countdown, stun_countdown, grabbing_cooldown, grabbing_countdown, blocking_countdown):
    super().__init__()
    self.image = pygame.image.load(os.path.join('res','img','chars','shit_clown-3.png'))
    #self.image = pygame.Surface([30,20])
    #self.image.fill(color)
    self.rect = self.image.get_rect()
    self.surf = pygame.Surface((30,20))
    self.name = name
    self.speed = speed
    self.level = level
    self.hp = hp
    self.stamina = stamina
    self.fear = fear
    self.blocking_cooldown = blocking_cooldown
    self.jumping_cooldown = jumping_cooldown
    self.jumping_countdown = jumping_countdown
    self.attacking_cooldown = attacking_cooldown
    self.attacking_countdown = attacking_countdown
    self.held_cooldown = held_cooldown
    self.held_countdown = held_countdown
    self.knockdown_cooldown = knockdown_cooldown
    self.knockdown_countdown = knockdown_countdown
    self.stun_cooldown = stun_cooldown
    self.stun_countdown = stun_countdown
    self.grabbing_cooldown = grabbing_cooldown
    self.grabbing_countdown = grabbing_countdown
    self.blocking_countdown = blocking_countdown

    blocking_cooldown = False
    jumping_cooldown = False
    jumping_coutndown = 0
    attacking_cooldown = False
    attacking_countdown = 0
    held_cooldown = False
    held_countdown = 0
    knockdown_cooldown = False
    knockdown_countdown = 0
    stun_cooldown = False
    stun_countdown = 0
    grabbing_cooldown = False
    grabbing_countdown = 0
    blocking_countdown = 0

然后生成敌人:

enemyShit_Clown = Enemy("Shit Clown", 1, 4, 1000, 10, 90, False, False, False, False, False, False, 0, 0, 0, 0, 0, True, 60, 0)
enemyShit_Clown.image = pygame.image.load(os.path.join('res','img','chars','shit_clown-3.png')).convert()
enemyShit_Clown.rect.x = 300 #random.randrange(300, 400)
enemyShit_Clown.rect.y =  300 #random.randrange(200, 400)
enemy_list.add(enemyShit_Clown)
all_sprites_list.add(enemyShit_Clown)

非常感谢您的帮助

【问题讨论】:

  • 你有 for villain in contact: for villain in enemy_list: 是错字还是什么?
  • @AustinHastings Hastings - 我修复了帖子中的缩进。在我的代码中它已经是正确的。感谢您指出这一点。
  • 再往下看,我看到了这个:heroBoonrit.held_cooldown = True,然后是这个:heroBoonrit.held_countdown = villan.grabbing_countdown。你需要弄清楚它是布尔值还是整数,然后清理你的代码。
  • Re: for villain: 你为什么要用同一个变量循环两个不同的东西?内层掩盖了外层,而您只是循环了不必要的内容。
  • @AustinHastings - heroBoonrit.held_cooldown 是确定他是否被关押的布尔值。 heroBoonrit.held_countdown 是一个整数变量,它是倒计时到零的计时器。它们是两个不同的变量(可能应该以不同的方式命名以减少晦涩)。

标签: python timer pygame


【解决方案1】:

我看不出有什么明显的理由让小人停止倒计时。也就是说,我确实看到您在 hero.update 代码和主循环中都在减少倒计时。我希望你的英雄的倒计时速度是反派的两倍,但不是 60 倍。

我想建议一些代码更改。首先,去掉__init__ 代码中的大部分参数。只需将默认值设置为 0 或 False 或其他任何值。

接下来,我看到您创建一个角色,然后为其分配一个图像。这应该在构造函数中,可能有一个默认参数来选择不同的起始图像。

接下来,你在英雄的update方法中有倒计时代码,但在反派中没有。我认为这是一个错误。将倒计时移动到更新例程中,不用担心在主循环中搜索它。

最后,您缺少 OO 编程的一条经验法则:“告诉,不要问。”

基本上,这意味着您不应该访问另一个对象的属性,并且您绝对不应该修改另一个对象的属性。

我建议你为这些东西写方法,像这样:

class Villain: 
    def grab(self, hero):
        """Start a grabbing attack on hero, if not blocked/busy"""

        if self.grabbing:
            # Already grabbing someone. Fail.
            return False

        ticks = 60

        if hero.grabbed(self, ticks):
            self.grabbing = hero
            self.grabbing_countdown = ticks
            hero.moveto(self.rect.y, self.rect.x + 30)
            return True
        else:
            return False

    def update(self, *args):
        :
        blah blah blah
        :
        if self.grabbing:
            self.grabbing_countdown -= 1

            if self.grabbing_countdown == 0:
                self.grabbing.release(self)
                self.grabbing.push_away(self.rect.x, self.rect.y, 30)
                self.grabbing = None

【讨论】:

  • 感谢您的建议。关于“告诉,不要问”的心态,因为不鼓励一个对象访问/更改另一个对象的属性,所以像击中玩家这样的基本操作如何工作?如果敌人攻击,则敌人物体图像变为攻击图像,villan.attacking = True,在攻击动画持续时间内开始倒计时,然后villan.attacking = False。在计时器期间,主游戏循环检查精灵矩形是否重叠,如果是,则检查敌人是否处于攻击状态,如果是,则将玩家更改为 stun = True 状态?
  • 我会说你告诉英雄你试图击中它,它会告诉你这是否可能(返回值)。如果可能的话,你假设英雄会尽自己的一份力量,而你也会尽自己的一份力量。 (在这种情况下,返回 false 表示“由于规则原因(例如:玩家正在阻挡,或被另一个怪物抓住),我不可能被击中”,而不是“因为我躲闪而错过了”。)
  • 一个稍微不相关的问题。当我生成敌人时,如何直接调用它们?如果我只有一个敌人Shit_Clown 并且我想移动它,我可以说enemyShit_Clown.rect.x += 5 ...但是当我生成10 个时会发生什么?我怎么知道哪个在抢?对于范围内的我(10):enemyShit_Clown = Enemy("Shit Clown"+str(i), 1, 4,False, False)enemy_list.add(enemyShit_Clown) all_sprites_list.add(enemyShit_Clown)
  • 您的代码是否有在线仓库? (这个聊天的地方比这里更好。)当你创建恶棍时,把他们放在列表中。然后调用 list.update()。类的更新方法应该有某种策略:如果我在攻击,就攻击。如果不攻击,要么等待(让人类玩家看到/做出反应),要么开始攻击。
  • 如果我问什么是回购协议,我不会被这个网站禁止,对吧? :D 你说的是一个我可以更新我的代码的新版本并允许人们发表评论的网站?我有一个 Pastebin 和 Github 帐户...github.com/pizzaholic81/beat-em-up
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-24
  • 1970-01-01
  • 2017-06-29
  • 2015-01-19
  • 2019-11-29
相关资源
最近更新 更多