I can't get my "world_data2" and "world_data3" to draw to the screen. only the "world_data" at index position 0 of the "levels" list draws. Everything else seems to be working. The bats and the doors change in every level, but the tile layout stays exactly the same. Is it possibly something to do with my "reset_level" function? or my world class?
Improved code:
# Importing the pygame library
import pygame
# Initializing pygame
pygame.init()
# Clock and frame rate
clock = pygame.time.Clock()
fps = 60
# Creating the game window
screen_width = 1500
screen_height = 1000
screen = pygame.display.set_mode((screen_width, screen_height))
# Setting game window caption
pygame.display.set_caption('Castle Escape')
# Define game variables
tile_size = 50
game_over = 0
main_menu = True
current_level_index = 0
total_levels = 2
# Loading images
background_img = pygame.image.load('Assets/Images/purplebackground.jpg')
restart_img = pygame.image.load('Assets/Images/Sprites/Buttons/restart.png')
restart_img = pygame.transform.scale(restart_img, (80, 40))
play_img = pygame.image.load('Assets/Images/Sprites/Buttons/play.png')
play_img = pygame.transform.scale(play_img, (350, 190))
exit_img = pygame.image.load('Assets/Images/Sprites/Buttons/exit.png')
exit_img = pygame.transform.scale(exit_img, (350, 190))
controls_img = pygame.image.load('Assets/Images/Sprites/Buttons/controls.png')
controls_img = pygame.transform.scale(controls_img, (350, 190))
settings_img = pygame.image.load('Assets/Images/Sprites/Buttons/settings.png')
settings_img = pygame.transform.scale(settings_img, (350, 190))
menu_img = pygame.image.load('Assets/Images/Sprites/Buttons/menu.png')
menu_img = pygame.transform.scale(menu_img, (80, 40))
# Instances for groups
bat_group = pygame.sprite.Group()
door_group = pygame.sprite.Group()
# Function to draw grid
def draw_grid():
for line in range(0, 30):
pygame.draw.line(screen, (255, 255, 255), (0, line * tile_size), (screen_width, line * tile_size))
pygame.draw.line(screen, (255, 255, 255), (line * tile_size, 0), (line * tile_size, screen_height))
# Function to reset levels
def reset_level(current_level_index):
# Reset player position
player.reset(100, screen_height - 130)
# Empty groups
bat_group.empty()
door_group.empty()
# Load in level data and create world
if current_level_index <= total_levels:
level = levels[current_level_index]
world = World(level)
return world
# Class for world map
class World:
# Constructor
def __init__(self, data):
# List to store locations of tiles
self.tile_list = []
# Load images
ground_img = pygame.image.load('Assets/Medieval Tileset/PNG/Tiles/tile59.png')
ground2_img = pygame.image.load('Assets/Medieval Tileset/PNG/Tiles/tile34.png')
leftwall_img = pygame.image.load('Assets/Medieval Tileset/PNG/Tiles/tile56.png')
rightwall_img = pygame.image.load('Assets/Medieval Tileset/PNG/Tiles/tile58.png')
ceiling_img = pygame.image.load('Assets/Medieval Tileset/PNG/Tiles/tile146.png')
# Loop to run through each row of world grid
row_count = 0
for row in data:
# Loop to run through each column in each row of world grid
col_count = 0
for tile in row:
if tile == 1:
# Scale images to 50 x 50px
img = pygame.transform.scale(ground_img, (tile_size, tile_size))
# Take rectangle
img_rect = img.get_rect()
# x and y coordinates for rectangle
img_rect.x = col_count * tile_size
img_rect.y = row_count * tile_size
tile = (img, img_rect)
self.tile_list.append(tile)
if tile == 2:
# Scale images to 50 x 50px
img = pygame.transform.scale(leftwall_img, (tile_size, tile_size))
# Take rectangle
img_rect = img.get_rect()
# x and y coordinates for rectangle
img_rect.x = col_count * tile_size
img_rect.y = row_count * tile_size
tile = (img, img_rect)
self.tile_list.append(tile)
if tile == 3:
# Scale images to 50 x 50px
img = pygame.transform.scale(rightwall_img, (tile_size, tile_size))
# Take rectangle
img_rect = img.get_rect()
# x and y coordinates for rectangle
img_rect.x = col_count * tile_size
img_rect.y = row_count * tile_size
tile = (img, img_rect)
self.tile_list.append(tile)
if tile == 4:
# Scale images to 50 x 50px
img = pygame.transform.scale(ceiling_img, (tile_size, tile_size))
# Take rectangle
img_rect = img.get_rect()
# x and y coordinates for rectangle
img_rect.x = col_count * tile_size
img_rect.y = row_count * tile_size
tile = (img, img_rect)
self.tile_list.append(tile)
if tile == 5:
# Scale images to 50 x 50px
img = pygame.transform.scale(ground2_img, (tile_size, tile_size))
# Take rectangle
img_rect = img.get_rect()
# x and y coordinates for rectangle
img_rect.x = col_count * tile_size
img_rect.y = row_count * tile_size
tile = (img, img_rect)
self.tile_list.append(tile)
if tile == 6:
# Create instance of bat enemy
bat = Enemy(col_count * tile_size, row_count * tile_size)
bat_group.add(bat)
if tile == 7:
# Create instance of door
door = Doors(col_count * tile_size, row_count * tile_size - 50)
door_group.add(door)
col_count += 1
row_count += 1
# Method to draw tiles to screen
def drawWorld(self):
# Loop to iterate through tile_list
for tile in self.tile_list:
screen.blit(tile[0], tile[1])
pygame.draw.rect(screen, (255, 255, 255), tile[1], 2)
# Class for buttons
class Button:
def __init__(self, x, y, image):
self.image = image
# Create rectangle
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
# Check whether mouse click occurs
self.clicked = False
# Draw method
def draw(self):
# Variable holds whether restart button been clicked or not
action = False
# Get mouse position
pos = pygame.mouse.get_pos()
# Check mouseover and clicked conditions
if self.rect.collidepoint(pos):
if pygame.mouse.get_pressed()[0] == 1 and self.clicked == False:
self.clicked = True
action = True
# Make sure button can only be clicked once
if pygame.mouse.get_pressed()[0] == 0:
self.clicked = False
# Draw button
screen.blit(self.image, self.rect)
return action
# Class for player (sprite)
class Player:
# Spawn Player
def __init__(self, x, y):
# Call reset method
self.reset(x, y)
def updatePlayer(self, game_over):
dx = 0 # move along x
dy = 0 # move along y
run_slowDown = 5 # Slow down run animation
if game_over == 0:
# Key controls
key = pygame.key.get_pressed()
# Move player left
if key[pygame.K_LEFT]:
dx -= 3
self.counter += 1
self.direction = -1
# Move player right
if key[pygame.K_RIGHT]:
dx += 3
self.counter += 1
self.direction = 1
# Show idle image if no keys are being pressed
if key[pygame.K_LEFT] == False and key[pygame.K_RIGHT] == False:
self.counter = 0
self.index = 0
if self.direction == 0 or self.direction == 1:
self.image = pygame.image.load('Assets/Images/Sprites/Warrior/Idle/idle1.png')
self.image = pygame.transform.scale(self.image, (60, 90))
if self.direction == -1:
self.image = pygame.image.load('Assets/Images/Sprites/Warrior/Idle/idle1.png')
self.image = pygame.transform.scale(self.image, (60, 90))
self.image = pygame.transform.flip(self.image, True, False)
# Make player jump
self.rect.y += self.vel_y
if key[pygame.K_SPACE] and self.jumped == False and self.in_air == False:
self.vel_y = -12
self.jumped = True
if not key[pygame.K_SPACE]:
self.jumped = False
# Handle animations
if self.counter > run_slowDown:
self.counter = 0
self.index += 1
if self.index >= len(self.transformed_rightRun):
self.index = 0
if self.direction == 1:
self.image = self.transformed_rightRun[self.index]
if self.direction == -1:
self.image = self.images_leftRun[self.index]
# Add gravity
self.vel_y += 1
# Add terminal velocity
if self.vel_y > 2:
self.vel_y = 2
dy += self.vel_y
# Check for collision with tiles
self.in_air = True
for tile in world.tile_list:
# Check for collision in x-direction
if tile[1].colliderect(self.rect.x + dx, self.rect.y, self.width, self.height):
dx = 0
# Check for collision in y-direction
if tile[1].colliderect(self.rect.x, self.rect.y + dy, self.width, self.height):
# Check if hitting head on tile (jumping)
if self.vel_y < 0:
dy = tile[1].bottom - self.rect.top
self.vel_y = 0
# Check if landing on tile (falling)
elif self.vel_y >= 0:
dy = tile[1].top - self.rect.bottom
self.vel_y = 0
# Make sure player can only jump once
self.in_air = False
# Check for collision with enemies
if pygame.sprite.spritecollide(self, bat_group, False):
game_over = -1 # Value of -1 triggers game over
# Check collision with doors
if pygame.sprite.spritecollide(self, door_group, False):
game_over = 1 # Value of 1 means player has completed level
# update player coordinates
self.rect.x += dx
self.rect.y += dy
elif game_over == -1:
# Turn player into ghost
self.image = self.dead_image
# Make ghost float up
if self.rect.y > 200:
self.rect.y -= 5
# Draw player to screen
screen.blit(self.image, self.rect)
# Draw rectangle
pygame.draw.rect(screen, (255, 255, 255), self.rect, 2)
return game_over
# Method to reset game
def reset(self, x, y):
# Right run animation list
self.images_rightRun = []
# Append all right run animation images to list
self.images_rightRun.append(pygame.image.load('Assets/Images/Sprites/Warrior/Run/run1.png'))
self.images_rightRun.append(pygame.image.load('Assets/Images/Sprites/Warrior/Run/run2.png'))
self.images_rightRun.append(pygame.image.load('Assets/Images/Sprites/Warrior/Run/run3.png'))
self.images_rightRun.append(pygame.image.load('Assets/Images/Sprites/Warrior/Run/run4.png'))
self.images_rightRun.append(pygame.image.load('Assets/Images/Sprites/Warrior/Run/run5.png'))
self.images_rightRun.append(pygame.image.load('Assets/Images/Sprites/Warrior/Run/run6.png'))
self.images_rightRun.append(pygame.image.load('Assets/Images/Sprites/Warrior/Run/run7.png'))
self.images_rightRun.append(pygame.image.load('Assets/Images/Sprites/Warrior/Run/run8.png'))
# New transformed right run list
self.transformed_rightRun = []
for img in self.images_rightRun:
self.transformed_rightRun.append(pygame.transform.scale(img, (60, 90)))
# Left run animation list
self.images_leftRun = []
for img in self.transformed_rightRun:
self.images_leftRun.append(pygame.transform.flip(img, True, False))
# Set index and counter of lists to 0
self.index = 0
self.counter = 0 # Counter controls speed of animation
# Load and scale ghost image
self.dead_image = pygame.image.load('Assets/Images/Sprites/Ghost/ghost.png')
self.dead_image = pygame.transform.scale(self.dead_image, (40, 60))
# Statement to display current sprite animation on screen
self.image = self.transformed_rightRun[self.index]
# Create player rectangle
self.rect = self.image.get_rect()
# Get x coordinate
self.rect.x = x
# Get y coordinate
self.rect.y = y
# Width and height for rectangle
self.width = self.image.get_width()
self.height = self.image.get_height()
# Set velocity in y direction to 0
self.vel_y = 0
# Player jump
self.jumped = False
# Player direction
self.direction = 0
# Check if player is in air
self.in_air = True
# Class for enemies
class Enemy(pygame.sprite.Sprite):
def __init__(self, x, y):
# Calling constructor from super class
pygame.sprite.Sprite.__init__(self)
# Load and scale bat images
self.image = pygame.image.load('Assets/Images/Sprites/Bat/Fly/fly2.png')
self.image = pygame.transform.scale(self.image, (50, 40))
# Flip bat image
self.left_image = pygame.transform.flip(self.image, True, False)
# Create rectangle
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
# Variables for movement
self.move_direction = 1
self.move_counter = 0
def update(self):
# Move bats left and right
self.rect.x += self.move_direction
self.move_counter += 1
if abs(self.move_counter) > 100:
self.move_direction *= -1
self.move_counter *= -1
# Flip bat images
if self.move_direction == -1:
self.image = self.left_image
if self.move_direction == 1:
self.image = pygame.transform.flip(self.left_image, True, False)
# Draw bat rectangle to screen
pygame.draw.rect(screen, (255, 255, 255), self.rect, 2)
# Class for enemies
class Doors(pygame.sprite.Sprite):
def __init__(self, x, y):
# Calling constructor from super class
pygame.sprite.Sprite.__init__(self)
# Load and scale door images
self.image = pygame.image.load('Assets/Medieval Tileset/PNG/Objects/door4.png')
self.image = pygame.transform.scale(self.image, (80, 100))
# Create rectangle
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
# Level 1 map
world_data1 = [
[4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4],
[2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[2, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 3],
[2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[2, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 3],
[2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[2, 1, 1, 1, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3],
[2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 3],
[2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[2, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[2, 0, 0, 0, 0, 0, 0, 0, 1, 5, 5, 5, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 3],
[2, 0, 0, 0, 0, 7, 0, 1, 5, 5, 5, 5, 5, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5],
]
# Level 2 map
world_data2 = [
[4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4],
[2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[2, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 6, 0, 0, 3],
[2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[2, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 3],
[2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[2, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3],
[2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 3],
[2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[2, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[2, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[2, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5],
]
# Level 3 map
world_data3 = [
[4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4],
[2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[2, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 3],
[2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 3],
[2, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[2, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[2, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[2, 1, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[2, 0, 0, 0, 1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[2, 0, 0, 0, 0, 5, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[2, 0, 0, 0, 1, 5, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[2, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[2, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[2, 1, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[2, 0, 0, 0, 1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[2, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 3],
[1, 1, 1, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5],
]
# Store level maps in list
levels = [
world_data1,
world_data2,
world_data3,
]
# Load in level data and create world
if current_level_index <= total_levels:
level = levels[current_level_index]
world = World(level)
# Create instance of button class
restart_button = Button(screen_width // 2 - 50, screen_height // 2 + 100, restart_img)
play_button = Button(screen_width // 2 - 550, screen_height // 2 - 220, play_img)
exit_button = Button(screen_width // 2 - 550, screen_height // 2 + 50, exit_img)
controls_button = Button(screen_width // 2 + 150, screen_height // 2 - 220, controls_img)
settings_button = Button(screen_width // 2 + 150, screen_height // 2 + 50, settings_img)
menu_button = Button(screen_width // 2 - 50, screen_height // 2 + 100, menu_img)
# Create instance of player class
player = Player(100, 800)
# main game loop
run = True
while run:
# Set internal clock
clock.tick(fps)
# Draw background to screen
screen.blit(background_img, (0, 0))
# Display buttons on main menu
if main_menu:
# If exit button is clicked, close game
if exit_button.draw():
run = False
# If play button is clicked, start game
if play_button.draw():
main_menu = False
controls_button.draw()
settings_button.draw()
else:
# Draw levels to screen
world.drawWorld()
# if the player dies, stop updating bats
if game_over == 0:
bat_group.update()
# Draw bats to screen
bat_group.draw(screen)
# Draw doors to screen
door_group.draw(screen)
# Draw player to screen and return game_over value
game_over = player.updatePlayer(game_over)
# if player dies, display restart button
if game_over == -1:
if restart_button.draw():
# Send player back to level 1
current_level_index = 0
reset_level(current_level_index)
game_over = 0
# If player has completed level, move to next level
if game_over == 1:
current_level_index += 1
if current_level_index <= total_levels:
reset_level(current_level_index)
game_over = 0
else:
# If player has completed final level, open main menu
if menu_button.draw():
main_menu = True
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pygame.display.update()
pygame.quit()
Any help would be greatly appreciated. Thank you!
Keep in mind that your main loop logic is run at each frame, so here, at each frame you run level 1, then if game over == 1 you run level2 etc, so everything is reset all the time. I think your problem is there.
You could define a state that would hold the current level and use this.
For example:
levels= ( # store your levels in some convenient structure
World(world_data1),
World(world_data2),
World(world_data2),
)
current_level_index = 0 # The level state, initalized with the index of the first level
game_over = 0
while True:
level = levels[current_level_index] # use your state to get the level instead of naming it directly
level.drawWorld()
# whatever you need to do to update your level logic, move things etc
if game_over == 1:
# This just is a simplistic example implementation to illustrate a change of level
current_level_index += 1
This is just a trivial example but you can try to structure your code in this kind of way.
Given a number, n, I need to efficiently find how many times this number is a multiple of all powers of 4 less than the given number.
For examples:
16 is a multiple of 4, and 16, so the result would be 2.
64 is a multiple of 4, 16, and 64, so the result would be 3.
256 is a multiple of 4, 16, 64, and 256, so the result would be 4.
14 is not a multiple of any power of 4, so the result would be 0.
35 is not a multiple of any power of 4, so the result would be 0.
Bitwise operations are preferred, and this is in a very tight loop so it is inside of a bottleneck that needs to be efficient. My code at the moment is the obvious answer, but I have to believe there is something more mathematical that can figure out the result in less steps:
power = 4;
while (power < n) {
result += !(n & (power - 1));
power *= 4;
}
You could use logarithms. A quick Google search for "fast log2 c++" brought up a pretty long list of ideas. Then your answer is log2(x)/2, and you'd have to find some way to make sure that your result is a whole number if you only want an answer for exact powers of 4.
If you are programming for an x86 processor, you can use BitScanForward & BitScanReverse to find the set bit, and use it to compute log2. The following code works in Visual Studio, for GCC or others, there are other ways to do inline assembly.
uint32_t exact_power_of_4_scan(uint32_t num)
{
unsigned long reverse;
unsigned long forward;
if (!_BitScanReverse(&reverse, num)) return 0;
_BitScanForward(&forward, num);
if (reverse != forward) return 0; // makes sure only a single bit is set
if (reverse & 0x1) return 0; // only want every other power of 2
return reverse / 2;
}
If you need a portable solution, table lookup might be the way to go, but is more complicated.
uint8_t not_single_bit[256] = {
1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1,
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
};
uint8_t log2_table[256] = {
0, 0, 1, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0,
4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
uint32_t exact_power_of_2(uint32_t num)
{
auto a = not_single_bit[num & 0xff];
auto b = not_single_bit[(num >> 8) & 0xff];
auto c = not_single_bit[(num >> 16) & 0xff];
auto d = not_single_bit[(num >> 24) & 0xff];
if (a + b + c + d != 3) {
return 0;
}
if (!a) {
return log2_table[num & 0xff];
}
if (!b) {
return log2_table[(num >> 8) & 0xff] + 8;
}
if (!c) {
return log2_table[(num >> 16) & 0xff] + 16;
}
return log2_table[(num >> 24) & 0xff] + 24;
}
uint32_t exact_power_of_4(uint32_t num)
{
auto ret = exact_power_of_2(num);
if (ret & 0x1) return 0;
return ret / 2;
}
Both are linear algorithms. The first will probably beat out looping for almost any value of num, but I haven't tested it. The second is probably only good for largish nums.
The mathematics would be to keep dividing by 4 until the result is no longer divisible by 4.
If you really want to do it with bitwise operations, techniques here can be used to count the number of trailing zero bits (i.e. the number of times a value is divisible by 2). Those can be adjusted to count pairs of trailing bits (i.e. divisibility by a power of 4 rather than 2).
Note that you will need to work with unsigned values to avoid certain cases of undefined or unspecified behaviours.
I would dispute your assertion that bitwise operations will make for a more efficient solution. It is not a given without testing, particularly with modern compilers.