Анимация не воспроизводится до конца
Я делаю игру на pygame,и на данный момент я завершаю делать анимации для персонажа. Все анимации работают правильно кроме анимации выстрела из пистолета. Она не проигрывается до конца, а отображается только первая картинка. Я пытался воспроизводить ее также как и анимацию прыжка (вместе с self.jump я добавил self.in_air. С помощью self.in_air анимация прыжка воспроизводится правильно). Такую же переменную я добавил и для выстрела (self.shooting). И вроде бы все работает, но после завершения анимации мне выдает ошибку:
self.image = self.animation_list[self.action][self.frame_index]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^
IndexError: list index out of range
Анимация состоит из 4 картинок, но self.frame_index равняется несуществующему индексу (4). Как это исправить? Вот код:
import pygame
import os
import time
pygame.init()
SCREEN_WIDTH = 1280
SCREEN_HEIGHT = 720
screen = pygame.display.set_mode((SCREEN_WIDTH,SCREEN_HEIGHT))
pygame.display.set_caption('Shooter')
# set framerate
clock = pygame.time.Clock()
FPS = 60
# define game variables
GRAVITY = 0.75
# define colours
BG = (245,145,165)
BLACK = (0,0,0)
moving_left = False
moving_right = False
shoot = False
duck = False
grenade = False
#load images
#bullet
grenade_img = pygame.image.load('Sprites/grenade.png').convert_alpha()
def draw_bg():
screen.fill(BG)
pygame.draw.line(screen, BLACK,(0,500),(SCREEN_WIDTH,500))
class Hero(pygame.sprite.Sprite):
def __init__(self,char_type, x,y,scale, speed):
pygame.sprite.Sprite.__init__(self)
self.alive = True
self.char_type = char_type
self.speed = speed
self.shoot_cooldown = 0
self.health = 100
self.max_health = self.health
self.shooting = True
self.direction = 1
self.vel_y =0
self.jump = False
self.in_air = True
self.flip = False
self.shooting = False
self.animation_list = []
self.frame_index = 0
self.action = 0
self.update_time = pygame.time.get_ticks()
#load all images for the players
animation_types = ['Idle','Run','Jump','Shoot','Death','Duck','Hurt']
for animation in animation_types:
#reset temporary list of images
temp_list = []
#count number of files in the folder
num_of_frames = len(os.listdir(f'Sprites/{self.char_type}/{animation}'))
for i in range(num_of_frames):
img = pygame.image.load(f'Sprites/{self.char_type}/{animation}/{i}.png').convert_alpha()
img = pygame.transform.scale(img, (img.get_width() * scale, img.get_height() * scale))
temp_list.append(img)
self.animation_list.append(temp_list)
self.image = self.animation_list[self.action][self.frame_index]
self.rect = self.image.get_rect()
self.rect.center = (x, y)
def update(self):
self.update_animation()
self.check_alive()
#update cooldown
if self.shoot_cooldown > 0 and self.shooting:
self.shoot_cooldown -=1
def move(self, moving_left, moving_right):
#reset movement variables
dx = 0
dy = 0
#assign movement variables if moving left or right
if moving_left:
dx = -self.speed
self.flip = True
self.direction = -1
if moving_right:
dx = self.speed
self.flip = False
self.direction = 1
#jump
if self.jump == True and self.in_air == False:
self.vel_y = -15
self.jump = False
self.in_air = True
#apply gravity
self.vel_y += GRAVITY
if self.vel_y > 10:
self.vel_y
dy += self.vel_y
#check collusion with floor
if self.rect.bottom + dy > 500:
dy = 500 - self.rect.bottom
self.in_air = False
#update rectangle position
self.rect.x += dx
self.rect.y += dy
def shoot(self):
self.shooting = True
if self.shooting:
self.update_action(3)
if self.shoot_cooldown == 0:
self.shoot_cooldown = 25
bullet = Bullet('Player-shoot', self.rect.centerx + (0.65 * self.rect.size[0] * self.direction),
self.rect.centery - 5, self.direction)
bullet_group.add(bullet)
def update_animation(self):
ANIMATION_COOLDOWN = 100
#update image depending on current frame
self.image = self.animation_list[self.action][self.frame_index]
#check it enough time has passed since the last update
if pygame.time.get_ticks() - self.update_time > ANIMATION_COOLDOWN:
self.update_time = pygame.time.get_ticks()
self.frame_index+=1
#if the animation has run out the reset back to the start
if self.frame_index >= len(self.animation_list[self.action]):
if self.action == 4:
self.frame_index = len(self.animation_list[self.action]) - 1
elif self.action == 5:
self.frame_index = len(self.animation_list[self.action]) - 1
elif self.action == 3:
self.shooting = False
else:
self.frame_index =0
def update_action(self, new_action):
#check is the new action is different to the previous one
if new_action != self.action:
self.action = new_action
#update the animation settings
self.frame_index = 0
self.update_time = pygame.time.get_ticks()
def check_alive(self):
if self.health <= 0:
self.health = 0
self.speed = 0
self.alive = False
self.update_action(4)
def draw(self):
screen.blit(pygame.transform.flip(self.image,self.flip,False), self.rect)
class Bullet(pygame.sprite.Sprite):
def __init__(self,bullet_type, x,y,direction):
pygame.sprite.Sprite.__init__(self)
self.bullet_type = bullet_type
self.speed = 10
self.flip = False
self.direction = direction
self.animation_list = []
self.frame_index = 0
self.action = 0
self.update_time = pygame.time.get_ticks()
# reset temporary list of images
temp_list = []
# count number of files in the folder
num_of_frames = len(os.listdir(f'Sprites/{self.bullet_type}'))
for i in range(num_of_frames):
img = pygame.image.load(f'Sprites/{self.bullet_type}/{i}.png')
img = pygame.transform.scale(img, (int(img.get_width() * 2.2), int(img.get_height() * 2.2)))
temp_list.append(img)
self.animation_list.append(temp_list)
self.image = self.animation_list[self.action][self.frame_index]
self.rect = self.image.get_rect()
self.rect.center = (x, y)
def update(self):
self.update_animation()
self.check_flip()
self.rect.x += (self.direction * self.speed)
#check if bullet has gone off screen
if self.rect.right <0 or self.rect.left>SCREEN_WIDTH:
self.kill()
#chrck collusions with character
if pygame.sprite.spritecollide(hero, bullet_group, False):
if hero.alive:
hero.health -= 5
self.kill()
if pygame.sprite.spritecollide(cop, bullet_group, False):
if cop.alive:
cop.health -= 25
self.kill()
def check_flip(self):
if self.direction == -1:
self.flip = True
else:
self.flip = False
if self.flip:
self.image = pygame.transform.flip(self.image, self.flip, False)
def update_animation(self):
# update animation
ANIMATION_COOLDOWN = 100
# update image depending on current frame
self.image = self.animation_list[self.action][self.frame_index]
# check if enough time has passed since the last update
if pygame.time.get_ticks() - self.update_time > ANIMATION_COOLDOWN:
self.update_time = pygame.time.get_ticks()
self.frame_index += 1
# if the animation has run out the reset back to the start
if self.frame_index >= len(self.animation_list[self.action]):
self.frame_index = 0
def update_action(self, new_action):
#check is the new action is different to the previous one
if new_action != self.action:
self.action = new_action
#update the animation settings
self.frame_index = 0
self.update_time = pygame.time.get_ticks()
class Grenade(pygame.sprite.Sprite):
def __init__(self, x,y,direction):
pygame.sprite.Sprite.__init__(self)
self.timer = 100
self.vel_y = -11
self.speed = 7
self.image = grenade_img
self.image = pygame.transform.scale(self.image, (self.image.get_width() * 2.2, self.image.get_height() * 2.2))
self.rect = self.image.get_rect()
self.rect.center = (x, y)
self.direction = direction
#create groups for bullets
bullet_group = pygame.sprite.Group()
grenade_group = pygame.sprite.Group()
hero = Hero('Player',200,200,2, 5)
cop = Hero('Cop',400,300,2, 5)
run = True
while run:
clock.tick(FPS)
draw_bg()
hero.update()
hero.draw()
cop.update()
cop.draw()
# update and draw groups
bullet_group.update()
grenade_group.update()
bullet_group.draw(screen)
grenade_group.draw(screen)
#update player actions
if hero.alive:
if duck:
hero.speed = 0
hero.update_action(5)
hero.jump = False
elif shoot:
hero.shoot()
hero.speed = 0
hero.shooting = False
# throw grenade
elif grenade:
grenade = Grenade(hero.rect.centerx + (0.5 * hero.rect.size[0]) * hero.direction, hero.rect.centery,hero.direction)
grenade_group.add(grenade)
elif hero.in_air:
hero.update_action(2) # 2: jump
elif moving_right or moving_left:
hero.update_action(1)#1: run
hero.speed = 5
else:
hero.update_action(0)
hero.move(moving_left,moving_right)
for event in pygame.event.get():
#quit game
if event.type == pygame.QUIT or (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE):
run = False
#keyboard
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_a:
moving_left = True
flip_bullet = True
if event.key == pygame.K_d:
moving_right = True
flip_bullet = False
if event.key == pygame.K_SPACE:
shoot = True
if event.key == pygame.K_q:
grenade = True
if event.key == pygame.K_s:
duck = True
if event.key == pygame.K_w and hero.alive:
hero.jump = True
#hero.shooting = True
#keybaord button realised
if event.type == pygame.KEYUP:
if event.key == pygame.K_a:
moving_left = False
if event.key == pygame.K_d:
moving_right = False
if event.key == pygame.K_SPACE:
pass
if event.key == pygame.K_q:
grenade = False
if event.key == pygame.K_s:
duck = False
pygame.display.update()
pygame.quit()
Ответы (1 шт):
#if the animation has run out the reset back to the start
if self.frame_index >= len(self.animation_list[self.action]):
if self.action == 4:
self.frame_index = len(self.animation_list[self.action]) - 1
elif self.action == 5:
self.frame_index = len(self.animation_list[self.action]) - 1
elif self.action == 3: # <- проблема в этой ветке
self.shooting = False
else:
self.frame_index = 0
Если внимательно посмотреть на этот код, то видно, что в ветке elif self.action == 3
(то есть как-раз 'Shoot') вы ничего не делаете с self.frame_index
, он так и остаётся выходящим за пределы анимации.