You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Pygame项目Sprite抖动、动画失效问题求助

Pygame项目问题求助:Sprite抖动+精灵表动画失效

运行Pygame项目时遇到两个核心问题:

  • Sprite角色持续上下抖动
  • 从精灵表导入的动画无法正常播放

以下是Fighter类的代码:

def load_images(self, sprite_sheet, animation_steps):
    #extract the certain animation images from spritesheet
    animation_list = []
    for y, animation in enumerate(animation_steps):
        temporary_img_list = []
        for x in range(animation):
            
            temporaryimg = sprite_sheet.subsurface(x , y , self.size, self.size)
            temporary_img_list.append(pygame.transform.scale(temporaryimg, (self.size * self.image_scale, self.image_scale * self.size)))
        
        animation_list.append(temporary_img_list)
    print(animation_list)
    return animation_list

def move(self, screen_base, screen_height, surface, target):
    speed = 10
    gravity = 2
    dx = 0
    dy = 0
    self.running = False
    self.attack_type = 0

    #key-presses recall
    key = pygame.key.get_pressed() = pygame.key.get_pressed()

    #can only happen if no other attack is occuring
    if self.attacking == False:
        
        #movement defined here
        if key[pygame.K_a]:
            dx = -speed
            self.running = True 

        if key[pygame.K_d]:
            dx = speed
            self.running = True
        
        #jump is defined here
        if key[pygame.K_w] and self.jump == False:
            self.vel_y =  -30
            self.jump = True

        #attack is defined here
        if key[pygame.K_k] or key[pygame.K_j]:
            self.attack(surface, target)
            #determine which attack type was used when the key was pressed
            if key[pygame.K_k]:
                self.attack_type = 1
            if key[pygame.K_j]:
                self.attack_type = 2

    #apply gravity for the jumps
    self.vel_y += gravity
    dy += self.vel_y

    #this makes sure that the character does not go off the screen
    if self.rect.left + dx < 0:
        dx = -self.rect.left
    if self.rect.right + dx > screen_base:
        dx = screen_base - self.rect.right
    if self.rect.bottom + dy > screen_height - 207:
        self.vel_y = 0
        self.jump = False
        dy = screen_height - 207 - self.rect.bottom
    
    #ensures the players face each other
    if target.rect.centerx > self.rect.centerx:
        self.flip = False
    else:
        self.flip = True
    
    #apply the attack cooldown to balance the amount of attacks possible
    if self.attack_cooldown > 0:
        self.attack_cooldown -= 1

    #update the player position constantly
    self.rect.x += dx
    self.rect.y += dy

#handle the animation updates
def update(self):

    #check what action the player is currently doing
    if self.health <= 0:
        self.health = 0
        self.alive = False
        self.update_action(6)
    elif self.hit == True:
        self.update_action(5)
    elif self.attacking == True:
        if self.attack_type == 1:
            self.update_action(5) # 3 = Attack
        elif self.attack_type == 2:
            self.update_action(6) # 4 = Attack2

    elif self.jump == True:
        self.update_action(7) # 7 = Jump
        
    elif self.running == True:
        self.update_action(2) # 2 = Run
    
    else:
        self.update_action(0) # 0 = Idle
    
    animation_cooldown = 50

    #update the image
    self.image = self.animation_list[self.action][self.frameindex]
    
    #check if enough time has passed since the last update
    if pygame.time.get_ticks() - self.update_time > animation_cooldown:
        self.frameindex += 1
        self.updatetime = pygame.time.get_ticks()
    
    #check if the animation has finished
    if self.frameindex >= len(self.animation_list[self.action]):
        #if the player is dead end the animation
        if self.alive == False:
            self.frameindex = len(self.animation_list[self.action]) - 1
        else:
            self.frameindex = 0

            #check if an attack was executed
            if self.action == 5 or self.action == 6:
                self.attacking = False
                self.attack_cooldown = 20
            #check if damage was taken
            if self.action == 7:
                self.hit = False
                #if the player was in the middle on an attack, then the attack is stopped
                self.attacking= False
                self.attack_cooldown = 20

def attack(self, surface, target):
    if self.attack_cooldown == 0:
        self.attacking = True
        attacking_rect = pygame.Rect(self.rect.centerx - (2 * self.rect.width * self.flip), self.rect.y, 2 * self.rect.width, self.rect.height)
        if attacking_rect.colliderect(target.rect):
            target.health -= 5
            target.hit = True
        pygame.draw.rect(surface, (0, 255, 0), attacking_rect)

def update_action(self, new_action):
    #check if the new action is different to the previous one
    if new_action != self.action:
        self.action = new_action
        #update the animation settings 
        self.frameindex = 0
        self.update_time = pygame.time.get_ticks()

def draw(self, surface):
    img = pygame.transform.flip(self.image, self.flip, False)
    #pygame.draw.rect(surface,(255, 0, 0), self.rect)
    surface.blit(img, (self.rect.x - (self.offset[0] * self.image_scale), self.rect.y - (self.offset[1] * self.image_scale)))

问题排查与修复方案

一、Sprite上下抖动问题

  1. 修复键盘输入赋值错误
    move方法里的key = pygame.key.get_pressed() = pygame.key.get_pressed()是语法错误,改成:

    key = pygame.key.get_pressed()
    
  2. 修正地面碰撞逻辑,避免位置反复跳动
    原代码通过计算dy间接调整位置,容易因重力累加导致角色在地面上下抖动,改成直接固定角色底部到地面:

    if self.rect.bottom + dy > screen_height - 207:
        self.vel_y = 0
        self.jump = False
        # 直接将角色底部贴到地面,消除抖动
        self.rect.bottom = screen_height - 207
        dy = 0  # 重置dy,避免额外位移
    

二、精灵表动画失效问题

  1. 修正精灵表帧截取坐标
    load_images里的subsurface参数错误,没有按单帧尺寸计算坐标,改成:

    temporaryimg = sprite_sheet.subsurface(x * self.size, y * self.size, self.size, self.size)
    

    (每帧的起始x坐标是x*self.size,y坐标是y*self.size,对应精灵表的行列位置)

  2. 修复动画更新的变量名笔误
    update方法里的self.updatetime = pygame.time.get_ticks()少了下划线,应该是:

    self.update_time = pygame.time.get_ticks()
    

    这个笔误会导致动画冷却逻辑完全失效,帧永远无法推进。

  3. 统一动作状态映射,避免逻辑混乱
    原代码中动作编号的注释和实际赋值不匹配(比如攻击注释是3,实际用5),建议定义常量统一管理:

    # 在类中或全局定义动作常量
    ACTION_IDLE = 0
    ACTION_RUN = 2
    ACTION_JUMP = 3
    ACTION_ATTACK1 = 4
    ACTION_ATTACK2 = 5
    ACTION_HIT = 6
    ACTION_DEAD = 7
    

    然后在update方法中替换数字为常量,比如:

    elif self.jump == True:
        self.update_action(ACTION_JUMP)
    
  4. 修正动画结束后的状态重置逻辑
    原代码中攻击动作的判断条件if self.action ==5 or self.action ==6需要和实际的攻击动作编号匹配,否则攻击状态无法重置,角色会一直卡在攻击动画里。比如如果攻击1的动作编号是4,攻击2是5,就改成:

    if self.action == ACTION_ATTACK1 or self.action == ACTION_ATTACK2:
        self.attacking = False
        self.attack_cooldown = 20
    

内容的提问来源于stack exchange,提问作者Sofian Ghailane

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 22:15:30