Posts: 4
Threads: 1
Joined: Sep 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)
Posts: 741
Threads: 122
Joined: Dec 2017
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
It is more important to do the right thing, than to do the thing right.(P.Drucker)
Better is the enemy of good. (Montesquieu) = French version for 'kiss'.
Posts: 8,160
Threads: 160
Joined: Sep 2016
Sep-01-2020, 07:35 AM
(This post was last modified: Sep-01-2020, 07:35 AM by buran.)
(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
Posts: 4
Threads: 1
Joined: Sep 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...
Posts: 8,160
Threads: 160
Joined: Sep 2016
Sep-03-2020, 04:46 AM
(This post was last modified: Sep-03-2020, 04:46 AM by buran.)
(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: - it takes arguments, that are in fact available as properties of the class and should be used instead - list, angle. This and other similar mistakes make me think you don't understand the concept of class.
- using
self.reset() on line#39 - there is no Spaceship.reset() method
- not sure where
get_height() and get_width() methods you apply on the image come from (we don't know what object is ship_one_image , so they may be available for whatever object it is)
There are also other problems with your class, but let's start with these
Posts: 4
Threads: 1
Joined: Sep 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()
Posts: 8,160
Threads: 160
Joined: Sep 2016
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...
Posts: 4
Threads: 1
Joined: Sep 2020
I appreciate the input, and your time, buran. Thanks, again.
|