index error when using lists to blit rotated images - Printable Version +- Python Forum (https://python-forum.io) +-- Forum: Python Coding (https://python-forum.io/forum-7.html) +--- Forum: Game Development (https://python-forum.io/forum-11.html) +--- Thread: index error when using lists to blit rotated images (/thread-29408.html) |
index error when using lists to blit rotated images - codefun45 - Sep-01-2020 Hello, I am getting an index error when trying to use lists of rotated images. originally, to rotate images of 2 spaceships (think asteroids), I used a function to rotozoom each image for every degree change. It worked, but not surprisingly, it was lagging. I read that "cached images" might be a better choice, so I have preloaded two lists with 72 images each (5 degree increments through 360 degrees). However, after making one full rotation either cw or ccw, I get: in ship_draw win.blit(self.image_list[self.rot_angle_index], (self.x_move - int(self.image_list[self.rot_angle_index].get_width() / 2), self.y_move - int( IndexError: list index out of range Even though I am ensuring the index value returns to 0, it won't display the image. Any help would be appreciated as to why this is happening and how to fix it. Thanks! class spaceship(object): def __init__(self, x_move, y_move, width, height, rot, image): self.x_move = x_move self.y_move = y_move self.width = width self.height = height self.rot_angle = rot self.vel = 0 self.hspeed = 0 self.vspeed = 0 self.thrust = False self.rotated = False self.fired_shots_delay = 10 self.image = image self.image_list = [] self.rot_angle_index = 0 self.shield = 100 self.visable = True self.score = 0 self.image_list.append(self.image) for i in range(1, 71): holder = pygame.transform.rotozoom(self.image, i * 5, 1) self.image_list.append(holder) def ship_draw(self, win, list, angle): if self.rot_angle <= -360 or self.rot_angle >= 360: self.rot_angle = self.rot_angle - (360 * (ship_one.rot_angle // 360)) self.rot_angle_index = self.rot_angle // 5 if self.visable: win.blit(self.image_list[self.rot_angle_index], (self.x_move - int(self.image_list[self.rot_angle_index].get_width() / 2), self.y_move - int(self.image_list[self.rot_angle_index].get_height() / 2))) else: self.reset() win.blit(self.image_list[self.rot_angle_index], (self.x_move - int(self.image_list[self.rot_angle_index].get_width() / 2), self.y_move - int( self.image_list[self.rot_angle_index].get_height() / 2))) # screen wrap if self.x_move > game_screen_x: self.x_move = 0 elif self.x_move < 0: self.x_move = game_screen_x elif self.y_move > game_screen_y: self.y_move = 40 elif self.y_move < 40: self.y_move = game_screen_y # These are called right before main game loop ship_one = spaceship(left_rnd_location_x, left_rnd_location_y, 32, 32, 0, ship_one_image) ship_two = spaceship(right_rnd_location_x, right_rnd_location_y, 32, 32, 0, ship_two_image) # Called at end of game loop before pygame.display.flip ship_one.ship_draw(win, ship_one.image_list, ship_one.rot_angle) ship_two.ship_draw(win, ship_two.image_list, ship_two.rot_angle) RE: index error when using lists to blit rotated images - DPaul - Sep-01-2020 We can't see in the error message which line is giving the problem. So my guess is that Line 22 counts 70 ticks: and at first sight this is 2 shy from the 72 images you are talking about. Paul RE: index error when using lists to blit rotated images - buran - Sep-01-2020 (Sep-01-2020, 06:52 AM)DPaul Wrote: and at first sight this is 2 shy from the 72 images you are talking about.actually they have one image already in the list, so total of 71 images, still not 72 as OP expects As to design - it may be better to use dict with angle as key, instead of converting the angle to index in list For debug - I would print both angle and index just before the problem line RE: index error when using lists to blit rotated images - codefun45 - Sep-02-2020 Thanks to you both for your replies. I was playing around with the suggestions and realized my mistake...I forgot to change ship_one.rot_angle to self.rot_angle. It works now. If possible, buran, would you elaborate on the using dict with angle as key, instead of my conversion to an index? I am new to python and coding... RE: index error when using lists to blit rotated images - buran - Sep-03-2020 (Sep-02-2020, 11:44 PM)codefun45 Wrote: I forgot to change ship_one.rot_angle to self.rot_angle. It works now.can you post your current code for the class, because there are number of issues in your Spaceship.ship_draw() method:
There are also other problems with your class, but let's start with these RE: index error when using lists to blit rotated images - codefun45 - Sep-03-2020 Here is what I have so far. import pygame import math import random pygame.init() pygame.font.init() clock = pygame.time.Clock() infoObject = pygame.display.Info() game_screen_x = infoObject.current_w - 50 game_screen_y = infoObject.current_h - 100 win = pygame.display.set_mode((game_screen_x, game_screen_y)) screen_center_x = int(game_screen_x / 2) screen_center_y = int(game_screen_y / 2) left_rand_location_x = 0 left_rand_location_y = 0 right_rand_location_x = 0 right_rand_location_y = 0 pygame.display.set_caption("SpaceDemoDerby") # define colors black = (0, 0, 0) red = (255, 0, 0) green = (0, 255, 0) blue = (0, 0, 255) lightblue = (173, 216, 230) grey = (87, 87, 87) offwhite = (230, 230, 230) # load images back_ground = pygame.image.load('bg.png').convert() pygame.transform.scale(back_ground, (game_screen_x, game_screen_y)) load_image_one = pygame.image.load('blueship.png').convert_alpha() load_image_two = pygame.image.load('redship.png').convert_alpha() load_image_one.set_colorkey(black) load_image_two.set_colorkey(black) ship_one_image = pygame.transform.scale(load_image_one, (32, 32)) ship_two_image = pygame.transform.scale(load_image_two, (32, 32)) explosion_anim = {} explosion_anim['boom'] = [] for i in range(6): filename = 'exp{}.png'.format(i) img = pygame.image.load(filename).convert() img.set_colorkey(black) explosion_anim['boom'].append(img) # load music and sound effects music = pygame.mixer.music.load('unbreakable havoc redone.wav') pygame.mixer.music.play(-1) shoot_sound = pygame.mixer.Sound('Laser.wav') explode_sound = pygame.mixer.Sound('Explosion.wav') shield_sounds = [] for i in range(5): filename = 'Metal_sounds_{}.wav'.format(i) snd = pygame.mixer.Sound(filename) shield_sounds.append(snd) crash_sounds = [] for i in range(4): filename = 'crash{}.wav'.format(i) snd = pygame.mixer.Sound(filename) crash_sounds.append(snd) # starter variables bullet_speed = 15 fd_fric = 0.5 bd_fric = 0.1 player_max_speed = 10 rotation_max_speed = 5 all_sprites = pygame.sprite.Group() left_rnd_location_x = random.randint(25, (screen_center_x - 50)) left_rnd_location_y = random.randint(25, (screen_center_y - 50)) right_rnd_location_x = random.randint((screen_center_x + 50), (game_screen_x - 25)) right_rnd_location_y = random.randint((screen_center_y - 50), (game_screen_y - 25)) # fonts font1 = pygame.font.SysFont('Arial', 20, True) font2 = pygame.font.SysFont('Arial', 16) font3 = pygame.font.SysFont('Arial', 28, True, True) class explosion(pygame.sprite.Sprite): def __init__(self, center, name): pygame.sprite.Sprite.__init__(self) self.name = name self.image = explosion_anim[self.name][0] self.rect = self.image.get_rect() self.rect.center = center self.frame = 0 self.last_update = pygame.time.get_ticks() self.frame_rate = 60 def update(self): now = pygame.time.get_ticks() if now - self.last_update > self.frame_rate: self.last_update = now self.frame += 1 if self.frame == len(explosion_anim[self.name]): self.kill() else: center = self.rect.center self.image = explosion_anim[self.name][self.frame] self.rect = self.image.get_rect() self.rect.center = center class bullet(object): def __init__(self, x, y, dir, color): self.x = x self.y = y self.dir = dir self.color = color self.life = 60 def updatebullet(self): self.x += bullet_speed * math.cos((self.dir + 90) * math.pi / 180) self.y += bullet_speed * -math.sin((self.dir + 90) * math.pi / 180) pygame.draw.circle(win, self.color, (int(self.x), int(self.y)), 3) self.life -= 1 class spaceship(object): def __init__(self, x_move, y_move, width, height, rot, image): self.x_move = x_move self.y_move = y_move self.width = width self.height = height self.rot_angle = rot self.vel = 0 self.hspeed = 0 self.vspeed = 0 self.thrust = False self.rotated = False self.fired_shots_delay = 10 self.image = image self.image_list = [] self.rot_angle_index = 0 self.shield = 100 self.visable = True self.score = 0 for i in range(72): holder = pygame.transform.rotozoom(self.image, i * 5, 1) self.image_list.append(holder) def propulsion(self): # from asteroids speed = math.sqrt(self.hspeed ** 2 + self.vspeed ** 2) if self.thrust: if speed + fd_fric < player_max_speed: self.hspeed += fd_fric * math.cos((self.rot_angle + 90) * math.pi / 180) self.vspeed += fd_fric * -math.sin((self.rot_angle + 90) * math.pi / 180) else: self.hspeed = player_max_speed * math.cos((self.rot_angle + 90) * math.pi / 180) self.vspeed = player_max_speed * -math.sin((self.rot_angle + 90) * math.pi / 180) else: if speed - bd_fric > 0: change_in_hspeed = (bd_fric * math.cos(self.vspeed / self.hspeed)) change_in_vspeed = (bd_fric * math.sin(self.vspeed / self.hspeed)) if self.hspeed != 0: if change_in_hspeed / abs(change_in_hspeed) == self.hspeed / abs(self.hspeed): self.hspeed -= change_in_hspeed else: self.hspeed += change_in_hspeed if self.vspeed != 0: if change_in_vspeed / abs(change_in_vspeed) == self.vspeed / abs(self.vspeed): self.vspeed -= change_in_vspeed else: self.vspeed += change_in_vspeed else: self.hspeed = 0 self.vspeed = 0 self.x_move += self.hspeed self.y_move += self.vspeed def ship_draw(self, win, list, angle): if self.rot_angle <= -360 or self.rot_angle >= 360: self.rot_angle = self.rot_angle - (360 * (self.rot_angle // 360)) self.rot_angle_index = self.rot_angle // 5 if self.visable: win.blit(self.image_list[self.rot_angle_index], (self.x_move - int(self.image_list[self.rot_angle_index].get_width() / 2), self.y_move - int( self.image_list[self.rot_angle_index].get_height() / 2))) print (self.rot_angle, self.rot_angle_index) else: self.reset() win.blit(self.image_list[self.rot_angle_index], (self.x_move - int(self.image_list[self.rot_angle_index].get_width() / 2), self.y_move - int( self.image_list[self.rot_angle_index].get_height() / 2))) # screen wrap if self.x_move > game_screen_x: self.x_move = 0 elif self.x_move < 0: self.x_move = game_screen_x elif self.y_move > game_screen_y: self.y_move = 40 elif self.y_move < 40: self.y_move = game_screen_y def hitship(self): if self.shield > 0: self.shield -= 10 if self.shield < 0: self.shield = 0 random.choice(shield_sounds).play() def reset(self): if not ship_one.visable: self.x_move = random.randint(25, (screen_center_x - 50)) self.y_move = random.randint(50, (game_screen_y - 25)) if not ship_two.visable: self.x_move = random.randint((screen_center_x + 50), (game_screen_x - 25)) self.y_move = random.randint(50, (game_screen_y - 25)) self.rot_angle = 0 self.hspeed = 0 self.vspeed = 0 self.thrust = False self.rotated = False self.visable = True ship_one.shield = 100 ship_two.shield = 100 def shipscollide(rect_one, rect_two): c_tolerance = 10 ship_one.thrust = False ship_two.thrust = False if abs(rect_two.top - rect_one.bottom) < c_tolerance: if ship_one.hspeed > 0 or ship_two.hspeed < 0: ship_one.vspeed *= -1 ship_two.vspeed *= -1 if abs(rect_two.bottom - rect_one.top) < c_tolerance: if ship_one.hspeed < 0 or ship_two.hspeed > 0: ship_one.vspeed *= -1 ship_two.vspeed *= -1 if abs(rect_two.right - rect_one.left) < c_tolerance: if ship_one.hspeed < 0 or ship_two.hspeed > 0: ship_one.hspeed *= -1 ship_two.hspeed *= -1 if abs(rect_two.left - rect_one.right) < c_tolerance: if ship_one.hspeed > 0 or ship_two.hspeed < 0: ship_one.hspeed *= -1 ship_two.hspeed *= -1 if abs(rect_two.top - rect_one.left) < c_tolerance: if ship_one.hspeed > 0 or ship_two.hspeed < 0: ship_one.vspeed *= -1 ship_two.vspeed *= -1 if abs(rect_two.bottom - rect_one.right) < c_tolerance: if ship_one.hspeed < 0 or ship_two.hspeed > 0: ship_one.vspeed *= -1 ship_two.vspeed *= -1 if abs(rect_two.right - rect_one.top) < c_tolerance: if ship_one.hspeed < 0 or ship_two.hspeed > 0: ship_one.hspeed *= -1 ship_two.hspeed *= -1 if abs(rect_two.left - rect_one.bottom) < c_tolerance: if ship_one.hspeed > 0 or ship_two.hspeed < 0: ship_one.hspeed *= -1 ship_two.hspeed *= -1 if abs(rect_two.top - rect_one.top) < c_tolerance: if ship_one.hspeed > 0 or ship_two.hspeed < 0: ship_one.vspeed *= -1 ship_two.vspeed *= -1 if abs(rect_two.bottom - rect_one.bottom) < c_tolerance: if ship_one.hspeed < 0 or ship_two.hspeed > 0: ship_one.vspeed *= -1 ship_two.vspeed *= -1 if abs(rect_two.right - rect_one.right) < c_tolerance: if ship_one.hspeed < 0 or ship_two.hspeed > 0: ship_one.hspeed *= -1 ship_two.hspeed *= -1 if abs(rect_two.left - rect_one.left) < c_tolerance: if ship_one.hspeed > 0 or ship_two.hspeed < 0: ship_one.hspeed *= -1 ship_two.hspeed *= -1 if abs(rect_two.bottom - rect_one.left) < c_tolerance: if ship_one.hspeed > 0 or ship_two.hspeed < 0: ship_one.vspeed *= -1 ship_two.vspeed *= -1 if abs(rect_two.top - rect_one.right) < c_tolerance: if ship_one.hspeed < 0 or ship_two.hspeed > 0: ship_one.vspeed *= -1 ship_two.vspeed *= -1 if abs(rect_two.right - rect_one.bottom) < c_tolerance: if ship_one.hspeed < 0 or ship_two.hspeed > 0: ship_one.hspeed *= -1 ship_two.hspeed *= -1 if abs(rect_two.left - rect_one.top) < c_tolerance: if ship_one.hspeed > 0 or ship_two.hspeed < 0: ship_one.hspeed *= -1 ship_two.hspeed *= -1 impact_damage = [2, 5, 10] if ship_one.shield > 0: ship_one.shield -= random.choice(impact_damage) if ship_one.shield < 0: ship_one.shield = 0 if ship_two.shield > 0: ship_two.shield -= random.choice(impact_damage) if ship_two.shield < 0: ship_two.shield = 0 random.choice(crash_sounds).play() def collision(bx, by, sx, sy, size): if bx > sx - size and bx < sx + size and by > sy - size and by < sy + size: return True return False def status_bar(): # background bar pygame.draw.rect(win, grey, (0, 0, game_screen_x, 45)) #Title title_text = font3.render('SPACE DEMO DERBY', True, black) win.blit(title_text, (800, 10)) # score score1_text = font1.render('Red Ships Destroyed: ' + str(ship_one.score), True, offwhite) score2_text = font1.render('Blue Ships Destroyed: ' + str(ship_two.score), True, offwhite) win.blit(score1_text, (410, 10)) win.blit(score2_text, (game_screen_x - 648, 10)) # shield health bars shield1_text = font2.render('SHIELD INTEGRITY', True, offwhite) win.blit(shield1_text, (245, 15)) win.blit(shield1_text, (game_screen_x - 395, 15)) pygame.draw.rect(win, red, (40, 15, 200, 15)) pygame.draw.rect(win, red, (game_screen_x - 240, 15, 200, 15)) pygame.draw.rect(win, green, (40, 15, 200 - (2 * (100 - ship_one.shield)), 15)) pygame.draw.rect(win, green, (game_screen_x - 240, 15, 200 - (2 * (100 - ship_two.shield)), 15)) def clear_screen(): win.fill((0, 0, 0)) win.blit(back_ground, (0, 0)) # create a spaceship ship_one = spaceship(left_rnd_location_x, left_rnd_location_y, 32, 32, 0, ship_one_image) ship_two = spaceship(right_rnd_location_x, right_rnd_location_y, 32, 32, 0, ship_two_image) # initial variables s1_fired_shots = [] s2_fired_shots = [] fired_shots_cap = 10 shot_delay_constant = 10 run = True # main loop while run: clock.tick(80) for shot in s1_fired_shots: if shot.life == 0: s1_fired_shots.pop(s1_fired_shots.index(shot)) for shot in s2_fired_shots: if shot.life == 0: s2_fired_shots.pop(s2_fired_shots.index(shot)) for event in pygame.event.get(): if event.type == pygame.QUIT: run = False if event.type == pygame.KEYDOWN: if event.key == pygame.K_e: ship_one.thrust = True if event.key == pygame.K_KP8: ship_two.thrust = True if event.type == pygame.KEYUP: if event.key == pygame.K_e: ship_one.thrust = False if event.key == pygame.K_KP8: ship_two.thrust = False keys = pygame.key.get_pressed() if keys[pygame.K_f]: # CW rotation ship_one.rot_angle -= rotation_max_speed if keys[pygame.K_KP6]: ship_two.rot_angle -= rotation_max_speed if keys[pygame.K_s]: #CCW rotation ship_one.rot_angle += rotation_max_speed if keys[pygame.K_KP4]: ship_two.rot_angle += rotation_max_speed if keys[pygame.K_TAB] and len(s1_fired_shots) < fired_shots_cap: if ship_one.fired_shots_delay == 0: s1_fired_shots.append(bullet(ship_one.x_move, ship_one.y_move, ship_one.rot_angle, lightblue)) # shoot_sound.play() ship_one.fired_shots_delay = shot_delay_constant else: ship_one.fired_shots_delay -= 1 if keys[pygame.K_RETURN] and len(s2_fired_shots) < fired_shots_cap: if ship_two.fired_shots_delay == 0: s2_fired_shots.append(bullet(ship_two.x_move, ship_two.y_move, ship_two.rot_angle, red)) # shoot_sound.play() ship_two.fired_shots_delay = shot_delay_constant else: ship_two.fired_shots_delay -= 1 clear_screen() # movement ship_one.propulsion() ship_two.propulsion() all_sprites.update() # check for bullet hits for shot in s1_fired_shots: shot.updatebullet() if ship_two.visable: if collision(shot.x, shot.y, ship_two.x_move, ship_two.y_move, 32): ship_two.hitship() s1_fired_shots.pop(s1_fired_shots.index(shot)) if ship_two.shield <= 0: tmp = pygame.Rect(ship_two.x_move, ship_two.y_move, 32, 32) exp = explosion(tmp.topleft, 'boom') explode_sound.play() all_sprites.add(exp) ship_two.visable = False ship_one.score += 1 for shot in s2_fired_shots: shot.updatebullet() if ship_one.visable: if collision(shot.x, shot.y, ship_one.x_move, ship_one.y_move, 32): ship_one.hitship() s2_fired_shots.pop(s2_fired_shots.index(shot)) if ship_one.shield <= 0: tmp = pygame.Rect(ship_one.x_move, ship_one.y_move, 32, 32) exp = explosion(tmp.topleft, 'boom') explode_sound.play() all_sprites.add(exp) ship_one.visable = False ship_two.score += 1 # check for ship collisions if ship_one.visable and ship_two.visable: tmp1 = pygame.Rect(ship_one.x_move, ship_one.y_move, 32, 32) tmp2 = pygame.Rect(ship_two.x_move, ship_two.y_move, 32, 32) # if collision(ship_one.x_move, ship_one.y_move,ship_two.x_move, ship_two.y_move, 24): # shipscollide(tmp1, tmp2) if tmp1.colliderect(tmp2): shipscollide(tmp1, tmp2) if ship_two.shield <= 0: tmp = pygame.Rect(ship_two.x_move, ship_two.y_move, 32, 32) exp = explosion(tmp.topleft, 'boom') explode_sound.play() all_sprites.add(exp) ship_two.visable = False ship_one.score += 1 if ship_one.shield <= 0: tmp = pygame.Rect(ship_one.x_move, ship_one.y_move, 32, 32) exp = explosion(tmp.topleft, 'boom') explode_sound.play() all_sprites.add(exp) ship_one.visable = False ship_two.score += 1 # draw new positions ship_one.ship_draw(win, ship_one.image_list, ship_one.rot_angle) ship_two.ship_draw(win, ship_two.image_list, ship_two.rot_angle) status_bar() all_sprites.draw(win) pygame.display.flip() pygame.quit() RE: index error when using lists to blit rotated images - buran - Sep-03-2020 I moved this to game development, because I think people who are more familiar with pygame can give you better advise/feedback on your code. My idea was to use a dict with angle, e.g. 0, 5, 10, 15... 355 to hold images and then use directly self.rot_angle to get respective image, instead of converting rot_angle to index and retrieving image from list. However I think you have much serious problems with your code and this change is minor compared to them. for example, in your spaceship.ship_draw() method you have list parameter. as already mentioned - this is bad name, but more importantly - you already have self.images_list and you work with it. There is no need to pass it as argument when you call the method. Same for rot_angle. width and height - are these width and height of the image? why pass them when instantiate the class? and so on... RE: index error when using lists to blit rotated images - codefun45 - Sep-03-2020 I appreciate the input, and your time, buran. Thanks, again. |