Devind Koswatte Devind Koswatte - 5 months ago 12
Python Question

change levels in pygame

I'm trying to switch levels, but I get this Traceback error.

Traceback (most recent call last):
File "C:\Users\Recovery 1\Desktop\Comp sci performance task\compscipretask (1) (1).py", line 228, in <module>
walls,endRect,wallRect,wall = load_level(currentLevel)
File "C:\Users\Recovery 1\Desktop\Comp sci performance task\compscipretask (1) (1).py", line 87, in load_level
for row in levels[level]:
IndexError: list index out of range


I am really stuck here, and this is the last component of my game so any help is appreciated. I'm also fairly new to pygame and python so my experience is very limited. Thanks in advance!

The code:

import pygame
import sys
import time
pygame.init()
screenSize = (800,600)
displayScreen = pygame.display.set_mode((screenSize),0)
pygame.display.set_caption("U1A2 Test")


WHITE = (255,255,255)
GREEN = (0,255,0)
RED = (255,0,0)
BLUE = (0,0,255)

displayScreen.fill(WHITE)
pygame.display.update()
clock = pygame.time.Clock()
fps= 60
game = False
menu = True
inst = False
jump= False
x = 53
y = 552


walls = []
dx = 0
dy = 0

currentLevel = 0
sprite= pygame.image.load('block sprite.png')

levels = [[

" ",
" ",
" D ",
" D ",
" ",
" ",
" WW ",
" ",
" ",
" WWWW ",
" ",
" ",
" WWW ",
" ",
" ",
" WWW ",
" WWW ",
" ",
"WWWWWWWWWWWWWWWWWWWWWWWWWW",
],

[

" ",
" ",
" D ",
" D ",
" ",
" WW ",
" ",
" WW ",
" ",
" WWWW ",
" ",
" ",
" WWW ",
" ",
" ",
" WWW ",
" WWW ",
" ",
"WWWWWWWWWWWWWWWWWWWWWWWWWW",
]]

def load_level(level):
m = n = 0
wall = (x, y)
walls = []
for row in levels[level]:
for col in row:

if col == "D":
endRect = pygame.Rect(m, n, 32, 32)
walls.append(endRect)
if col == " ":
jump = False
if col == "W":
wallRect = pygame.Rect(m,n,32,32)
walls.append(wallRect)
jump = True
m += 32
n += 32
m = 0
return walls,endRect,wallRect,wall

walls,endRect,wallRect,wall = load_level(currentLevel)
while menu:


fontTitle = pygame.font.SysFont("Ariel", 72)
textTitle = fontTitle.render("change this", True, (255,0,0))
displayScreen.blit(textTitle,(35, 50))

fontTitle = pygame.font.SysFont("Ariel", 52)
textTitle = fontTitle.render("Press 1 to play", True, (255,0,0))
displayScreen.blit(textTitle,(35, 100))

fontTitle = pygame.font.SysFont("Ariel", 52)
textTitle = fontTitle.render("Press 2 for instructions", True, (255,0,0))
displayScreen.blit(textTitle,(35, 150))
pygame.display.update()

fontTitle = pygame.font.SysFont("Ariel", 52)
textTitle = fontTitle.render("Press 3 to quit", True, (255,0,0))
displayScreen.blit(textTitle,(35, 200))
pygame.display.update()


for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_1:
print "ds"
game = True
currentLevel = 1




elif event.key == pygame.K_2:
menu = False
inst = True
displayScreen.fill(WHITE)
elif event.key == pygame.K_3:
menu = False

while game:


#pygame.draw.rect(displayScreen,GREEN,PlayerRect,0)



#pygame.draw.rect(displayScreen, (255, 0, 0), PlayerRect, 1)



#walls, players = load_level(level1)
clock.tick(fps)
for event in pygame.event.get():
if event.type ==pygame.QUIT:
pygame.display.quit()
sys.exit()

elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_a:
dx = -7
displayScreen.fill(WHITE)

elif event.key == pygame.K_d:
dx = 7
displayScreen.fill(WHITE)

elif event.key == pygame.K_w:
dy = -25

elif event.key == pygame.K_s:
dy = 25
displayScreen.fill(WHITE)

elif event.key == pygame.K_q:
stop = True
displayScreen.fill(WHITE)

if event.type == pygame.KEYUP:
if event.key == pygame.K_a or event.key == pygame.K_d:
dx = 0
elif event.key == pygame.K_w or event.key == pygame.K_s:
dy = 0

oldx = x
oldy = y
x = x + dx
y = y + dy

if game == True:
dy = dy + 2

if (x>=798 or x<=2):
x=oldx


if (y<=3 or y>=597):
y=oldy

displayScreen.fill(WHITE)

for wall in walls:
PlayerRect = pygame.Rect(x,y, sprite.get_width(), sprite.get_height())
if PlayerRect.colliderect(wall):
if dx > 0:
PlayerRect.left = wall.right

if dx < 0:
PlayerRect.right = wall.left

if dy > 0:
PlayerRect.bottom = wall.top

if dy < 0:
PlayerRect.top = wall.bottom

dy = 0

for wall in walls:
pygame.draw.rect(displayScreen,BLUE,wall,0)
pygame.draw.rect(displayScreen,RED, endRect, 0)

if PlayerRect.colliderect(endRect):
currentLevel += 1
walls,endRect,wallRect,wall = load_level(currentLevel)


displayScreen.blit(sprite,(x,y))
pygame.display.flip()


while inst:
fontTitle = pygame.font.SysFont("Ariel", 72)
textTitle = fontTitle.render("Instructions", True, (255,0,0))
displayScreen.blit(textTitle,(35, 50))

fontTitle = pygame.font.SysFont("Ariel", 52)
textTitle = fontTitle.render("- Use W to jump,S to fall", True, (255,0,0))
displayScreen.blit(textTitle,(35, 100))

fontTitle = pygame.font.SysFont("Ariel", 52)
textTitle = fontTitle.render("- Control your jump by using the A and D keys", True, (255,0,0))
displayScreen.blit(textTitle,(35,150))
pygame.display.update()

fontTitle = pygame.font.SysFont("Ariel", 52)
textTitle = fontTitle.render("- Move left and right with the A and D keys", True, (255,0,0))
displayScreen.blit(textTitle,(35, 200))
pygame.display.update()

fontTitle = pygame.font.SysFont("Ariel", 52)
textTitle = fontTitle.render("Press 3 to exit", True, (255,0,0))
displayScreen.blit(textTitle,(35, 250))
pygame.display.update()

for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_3:
inst = False
displayScreen.fill(WHITE)
menu = True

pygame.quit()
sys.exit()

Answer

The problem is, as the error message indicates, that the index used with the levels list is out of range. It can only be 0 or 1. The problem appears to be because in the main while game loop you increment currentLevel by one without checking its value before or after. Initially it was set to 0 but later it gets set to 1 when user presses the 1 key to play game.

            for wall in walls:
                pygame.draw.rect(displayScreen,BLUE,wall,0)
                pygame.draw.rect(displayScreen,RED, endRect, 0)

                if PlayerRect.colliderect(endRect):
                    currentLevel += 1  # unconditionally increments the value
                    walls,endRect,wallRect,wall = load_level(currentLevel)

I'm not sure, but setting its value to 1 when the user starts playing may be wrong. Regardless, you either need to add some checks to prevent it from ever getting out-of-range or always check it's value before use. One obvious way would be simply making incrementing it conditional:

            for wall in walls:
                pygame.draw.rect(displayScreen,BLUE,wall,0)
                pygame.draw.rect(displayScreen,RED, endRect, 0)

                if PlayerRect.colliderect(endRect):
                    if currentLevel < 1:  #### LIKE THIS ####
                        currentLevel += 1
                    walls,endRect,wallRect,wall = load_level(currentLevel)

Even better, you could make the program more flexible by avoiding hardcoding constants that might change in your code. Here that could be done by defining a global constant near the beginning:

HIGHEST_LEVEL = len(levels)-1  # index of topmost level

...and then using it any and every where necessary to ensure currentLevel is or stays valid:

                    ...
                if PlayerRect.colliderect(endRect)
                    if currentLevel < HIGHEST_LEVEL:
                        currentLevel += 1
                    ...

Using this approach here would allow you can add levels to the game and not need to search far and wide though the code making sure currentLevel was always in range — much as I had to do in the process of debugging your program.