popping a particular element from a list in pygame [duplicate] - python
This question already has answers here:
How to remove items from a list while iterating?
(25 answers)
Closed 2 years ago.
I recently started making Atari Breakout in Pygame. I encountered a problem that I don't know how to fix. Whenever I run my code, and the ball comes in contact with the block, the block that's colliding with it, doesn't disappear as it should, but the last block from the list. (I made a list of 21 pygame.Surfaces and iterate over it and blit every single one). Obviously, if the ball touches the last block from the list, the correct one disappears. Please can you help me?
This is my code:
import pygame, random, math
pygame.init()
screen = pygame.display.set_mode((800,600))
pygame.display.set_caption('Atari Breakout')
player = pygame.image.load('player copy.png')
ball = pygame.image.load('poland.png')
blocks = [pygame.image.load('block.png') for i in range(21)]
x, y = 25,25
blockx, blocky = [], []
for i in range(21):
if i % 7 == 0:
y += 52
x = 25
blockx.append(x)
blocky.append(y)
x += (50 + 64)
ballx, bally = 400, 300
balldx, balldy = 4,4
score = 0
score_font = pygame.font.Font('freesansbold.ttf', 32)
def show_score():
score_text = score_font.render('Score : {}'.format(score), True, (255,255,255))
screen.blit(score_text, (0,0))
def isCollision(x1,y1,x2,y2):
ballRect = ball.get_rect(topleft = (x1, y1))
playerRect = player.get_rect(topleft = (x2, y2))
return ballRect.colliderect(playerRect)
def isCollision2(x1,y1,x2,y2):
ballRect = ball.get_rect(topleft = (x1,y1))
blockRect = blocks[i].get_rect(topleft = (x2,y2))
return ballRect.colliderect(blockRect)
def blit_blocks():
for i in range(len(blocks)):
screen.blit(blocks[i], (blockx[i], blocky[i]))
running = True
while running:
if score >= 21:
running = False
screen.fill((0,0,0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pos = pygame.mouse.get_pos()
for i, block in enumerate(blocks):
if isCollision2(ballx, bally, blockx[i], blocky[i]):
balldy *= -1
score += 1
blocks.pop(i)
if isCollision(ballx, bally, pos[0], 525):
balldy *= -1
if bally <= 0:
balldy *= -1
if bally >= 570:
running = False
if ballx <= 0:
balldx *= -1
if ballx >= 770:
balldx *= -1
ballx += balldx
bally += balldy
screen.blit(player, (pos[0], 525))
screen.blit(ball, (ballx,bally))
blit_blocks()
show_score()
pygame.display.update()
What you actually do ist manipulate a list (by pop) while you iterate it. I recommend to travers a copy of the list (blocks[:]) and to manipulate the original list. e.g:
i = 0
for x, y, block in zip(blockx[:], blocky[:], blocks[:]):
if isCollision2(ballx, bally, x, y):
blockx.pop(i)
blocky.pop(i)
blocks.pop(i)
balldy *= -1
score += 1
else:
i += 1
Anyway, I recommend to create a class Block:
class Block():
def __init__(self, image, x, y)
self.image = image
self.x = x
self.y = y
image = pygame.image.load('block.png')
x, y = 25,25
blocks = []
for i in range(21)
if i % 7 == 0:
y += 52
x = 25
blocks.append(Block(image, x, y))
x += (50 + 64)
i = 0
for block in block[:]:
if isCollision2(ballx, bally, block.x, block.y):
balldy *= -1
score += 1
block.pop(i)
else:
i += 1
it is a bad practice to pop from a list while you are iterating over this will cause unexpected behaviour, here:
for i, block in enumerate(blocks):
if isCollision2(ballx, bally, blockx[i], blocky[i]):
balldy *= -1
score += 1
blocks.pop(i)
you can use:
new_blocks = []
for i, block in enumerate(blocks):
if isCollision2(ballx, bally, blockx[i], blocky[i]):
balldy *= -1
score += 1
else:
new_blocks.append(block)
blocks = new_blocks
Related
Deleting/Duplicating an instance of a class when there are many
I am trying to delete the dots outside of the safe zone but so far the only solution I have found is to not draw them, which is not what I want since using this method they still exist. Is there anyway to delete the specific dots outside of this region? Is there also a way to create a duplicate of an instance? import pygame import sys import random pygame.init() win = pygame.display.set_mode((800,600)) pygame.display.set_caption("Simulation") class Dot: def __init__(self): self.spawnX = random.randrange(0, 800) self.spawnY = random.randrange(0, 600) self.r = random.randrange(0, 256) self.g = random.randrange(0, 256) self.b = random.randrange(0, 256) self.vel = [None] * 0 self.spd = random.random() self.vel.append(self.spd) self.vel.append(self.spd * -1) self.fertility = random.randrange(0, 3) def safeZone(): #Draws a top rectangle pygame.draw.rect(win, (50,205,50), (0, 0, 800, 100)) def drawdot(dot): width = 10 height = 10 pygame.draw.rect(win, (dot.r, dot.g, dot.b), (dot.spawnX, dot.spawnY, width, height)) def population(dots): for dot in dots: dot.spawnX += random.choice(dot.vel) dot.spawnY += random.choice(dot.vel) if dot.spawnX >= 800: dot.spawnX -= 5 if dot.spawnY >= 600: dot.spawnY -= 5 if dot.spawnX <= 0: dot.spawnX += 5 if dot.spawnY <= 0: dot.spawnY += 5 if dot.spawnX >= 0 and dot.spawnY >= 100: pass #Here I want to delete the objects outside of this region drawdot(dot) alldots = [Dot() for _ in range(1000)] run = True while run: for event in pygame.event.get(): if event.type == pygame.QUIT: run = False win.fill((255, 255, 255)) safeZone() # Always draw dots after safe zone population(alldots) pygame.display.update()
"not draw them" just means removing them from the container they are in. Use pygame.Rect.colliderect or pygame.Rect.contains to test whether a dot object is in the safe zone. Remove the dots form the list alldots that are not in the safe zone (see How to remove items from a list while iterating?): def population(dots): safe_zone_rect = pygame.Rect(0, 0, 800, 100) for dot in dots: dot.spawnX += random.choice(dot.vel) dot.spawnY += random.choice(dot.vel) if dot.spawnX >= 800: dot.spawnX -= 5 if dot.spawnY >= 600: dot.spawnY -= 5 if dot.spawnX <= 0: dot.spawnX += 5 if dot.spawnY <= 0: dot.spawnY += 5 if dot.spawnX >= 0 and dot.spawnY >= 100: pass #Here I want to delete the objects outside of this region dot_rect = pygame.Rect(dot.spawnX, dot.spawnY, 10, 10) # if not safe_zone_rect.colliderect(dot_rect): if not safe_zone_rect.contains(dot_rect): dots.remove(dot) drawdot(dot) pygame.Rect.colliderect tests whether 2 rectangles intersect. pygame.Rect.contains tests whether a rectangle lies completely within another rectangle.
issues with checking if my images collided in pygame [duplicate]
This question already has an answer here: Why is my collision test always returning 'true' and why is the position of the rectangle of the image always wrong (0, 0)? (1 answer) Closed 1 year ago. So I'm trying to check im my bird images are touching my could images and if they are to print print('Collided1!'). My Issue is that print('Collided1!') goes off no matter what and is not checking whether the images are touching. colliderect was the solution I found online but I don't seem to know how it works because this is not working. Do You Know how to fix this? and check whether my images are touching or not? from random import randint import pygame, sys import random import time pygame.init() pygame.display.set_caption('Lokaverkefni') DISPLAYSURF = pygame.display.set_mode((1224, 724)) fpsClock = pygame.time.Clock() FPS = 60 a = 1 b = 1 c = 15 x = 100 y = 480 start = 0 score = 0 landX = 1205 totalScore = 0 level = 'low' directionForBird = 'none' WHITE = (255, 255, 255) BLACK = (0, 0, 0) BASICFONT = pygame.font.Font('freesansbold.ttf', 30) background_resized = pygame.image.load('sky.jpg') background = pygame.transform.scale(background_resized, (1224, 724)) bird1 = pygame.image.load('bird1.png') bird1_resized = pygame.transform.scale(bird1, (170, 150)) bird1Surface = bird1_resized.get_rect() bird2 = pygame.image.load('bird2.png') bird2_resized = pygame.transform.scale(bird2, (170, 150)) bird2Surface = bird2_resized.get_rect() cloudsList = ['cloud1.png', 'cloud2.png', 'cloud3.png', 'cloud4.png'] clouds = random.choice(cloudsList) cloud = pygame.image.load(clouds) cloud_resized = pygame.transform.scale(cloud, (352, 352)) cloudSurface = cloud_resized.get_rect() while True: for event in pygame.event.get(): if event.type == QUIT: pygame.quit() sys.exit() elif event.type == KEYDOWN: if level == 'low': if (event.key == K_SPACE ): directionForBird = 'up' level = 'high' FPS += 2 c += 1 if directionForBird == 'up': y -= 10 if y == 10: directionForBird = 'down' if directionForBird == 'down': y += 10 if y == 480: directionForBird = 'none' if a == 1: DISPLAYSURF.blit(background, (0, 0)) DISPLAYSURF.blit(bird1_resized, (x, y)) DISPLAYSURF.blit(cloud_resized, (landX, 300)) b += 1 if b == c: a += 1 if a == 2: DISPLAYSURF.blit(background, (0, 0)) DISPLAYSURF.blit(bird2_resized, (x, y)) DISPLAYSURF.blit(cloud_resized, (landX, 300)) b -= 1 if b == 1: a -= 1 start += 1 if start == 100: start -= 1 directionForLand = 'left' if directionForLand == 'left': landX -= 15 if landX == -550: landX = 1205 level = 'low' clouds = random.choice(cloudsList) cloud = pygame.image.load(clouds) cloud_resized = pygame.transform.scale(cloud, (352, 352)) score += 1 if score == 30: score = 0 totalScore += 1 scoreText = BASICFONT.render('Stig : %s' % (totalScore), True, (BLACK)) scoreRect = scoreText.get_rect() scoreRect.topleft = (1070, 10) DISPLAYSURF.blit(scoreText, scoreRect) # This is Supossed to Be what checks if the bird images # colide with the cloud images if bird1Surface.colliderect(cloudSurface): print('Collided1!') if bird2Surface.colliderect(cloudSurface): print('Collided1!') pygame.display.update() fpsClock.tick(FPS)
bird1Surface, bird2Surface and cloudSurface always have an upper left of (0,0), so they are always on top of each other.. You don't change the rectangles when you move the birds. You need to track the bird x,y and the cloud x,y, and construct new rectangles with the current x,y and the known width and height before you do the collision check.
How to fix 'apples' sometimes not appearing in my version of Snake? [duplicate]
I'm currently in the process of creating a Snake game and I want to create a food generator that generates an apple every 10 seconds based on my in-game timer. The timer counts down from 60 to 0(when the game ends) and I want an new apple to generate every 10 seconds, keeping the old one even if it hasn't been eaten. I don't know how to approach this and could use some help. Here is my full program. Edit: this is a beginner Computer Science school project so the more basic the better. import random import pygame pygame.init() #---------------------------------------# # # window properties # width = 640 # height = 480 game_window=pygame.display.set_mode((width,height)) black = ( 0, 0, 0) # #---------------------------------------# # snake's properties outline=0 body_size = 9 head_size = 10 apple_size = 8 speed_x = 8 speed_y = 8 dir_x = 0 dir_y = -speed_y segx = [int(width/2.)]*3 segy = [height, height + speed_y, height + 2*speed_y] segments = len(segx) apple_counter = 0 grid_step = 8 regular_font = pygame.font.SysFont("Andina",18) blue = [11, 90, 220] clock = pygame.time.Clock() time = 60 fps = 25 time = time + 1.0/fps text = regular_font.render("Time from start: "+str(int(time)), 1, blue) text2 = regular_font.render("Score: "+str(int(apple_counter)), 1, blue) apple_x = random.randrange(0, 640, grid_step) apple_y = random.randrange(0, 480, grid_step) apple_colour = (255,0,0) def redraw_game_window(): game_window.fill(black) for i in range(segments): segment_colour = (random.randint(1,50),random.randint(100,150),random.randint(1,50)) head_colour = (random.randint(180,220),random.randint(180,220),random.randint(1,15)) apple_colour = (255,0,0) pygame.draw.circle(game_window, segment_colour, (segx[i], segy[i]), body_size, outline) pygame.draw.circle(game_window, head_colour, (segx[0], segy[0]), head_size, outline) game_window.blit(text, (530, 20)) game_window.blit(text2, (30, 20)) pygame.draw.circle(game_window, apple_colour, (apple_x, apple_y), apple_size, outline) pygame.display.update() exit_flag = False print "Use the arrows and the space bar." print "Hit ESC to end the program." ########################################################## TIMER/CONTROLS while exit_flag == False: redraw_game_window() clock.tick(fps) time = time - 1.00/fps text = regular_font.render("Time: "+str(int(time)), 1, blue) text2 = regular_font.render("Score: "+str(int(apple_counter)), 1, blue) if time < 0.1: print "Game Over" exit_flag = True pygame.event.get() keys = pygame.key.get_pressed() if time == if keys[pygame.K_ESCAPE]: exit_flag = True if keys[pygame.K_LEFT] and dir_x != speed_x: dir_x = -speed_x dir_y = 0 if keys[pygame.K_RIGHT] and dir_x != -speed_x: dir_x = speed_x dir_y = 0 if keys[pygame.K_UP] and dir_y != speed_x: dir_x = 0 dir_y = -speed_y if keys[pygame.K_DOWN] and dir_y != -speed_x: dir_x = 0 dir_y = speed_y ############################################################ SNAKE MOVEMENT for i in range(segments-1,0,-1): segx[i]=segx[i-1] segy[i]=segy[i-1] segx[0] = segx[0] + dir_x segy[0] = segy[0] + dir_y ############################################################ COLLISION for i in range(segments-1, 3, -1): if segments > 3: if segx[0] == segx[i] and segy[0] == segy[i]: print "You have collided into yourself, Game Over." exit_flag = True ############################################################# BORDERS if segx[0] > 640 or segx[0] < 0: print "Game Over, you left the borders." break if segy[0] > 480 or segy[0] < 0: print "Game Over, you left the borders." break ############################################################# APPLE DETECT for i in range (0 , 13): if segx[0] == apple_x + i and segy[0] == apple_y + i: segments = segments + 1 segx.append(segx[-1]) segy.append(segy[-1]) apple_counter = apple_counter + 1 if segx[0] == apple_x - i and segy[0] == apple_y - i: segments = segments + 1 segx.append(segx[-1]) segy.append(segy[-1]) apple_counter = apple_counter + 1 ############################################################# pygame.quit()
You either A) use pygame.time.set_timer to call a function every 10 seconds to spawn food, and every 60 seconds to end the round. or B) compare get_ticks() def new_round(): last_apple = pygame.time.get_ticks() + 10*1000 while true: now = pygame.time.get_ticks() if now - last_apple >= 1000: spawn_apple() last_apple = now if now - round_start >= 60*1000: round_end()
How do I add a score tracker?
So I want it to count the score every time the snake eats a candy. I haven't tried much, but I tried to find existing codes and adding them to mine but that just broke the game. I also tried to make my own score board by watching a tutorial, but I don't know where the code should go like at the beginning or end. import pygame import random score = 0 welcome = ("Welcome to our snake game") print(welcome) class cube: height = 20 w = 500 def __init__(movee,start,x=1,y=0,color=(0,0,0)): movee.pos = start movee.x = 1 movee.y = 0 movee.color = color def move(movee, x, y): movee.x = x movee.y = y movee.pos = (movee.pos[0] + movee.x, movee.pos[1] + movee.y) def draw(movee, surface, eyes=False): leng = movee.w // movee.height i = movee.pos[0] j = movee.pos[1] pygame.draw.rect(surface, movee.color, (i*leng+1,j*leng+1, leng-2, leng-2)) class snake: body = [] turns = {} def __init__(movee, color, pos): movee.color = color movee.head = cube(pos) movee.body.append(movee.head) def move(movee): for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() keys = pygame.key.get_pressed() for key in keys: if keys[pygame.K_LEFT]: movee.x = -1 movee.y = 0 movee.turns[movee.head.pos[:]] = [movee.x, movee.y] elif keys[pygame.K_RIGHT]: movee.x = 1 movee.y = 0 movee.turns[movee.head.pos[:]] = [movee.x, movee.y] elif keys[pygame.K_UP]: movee.x = 0 movee.y = -1 movee.turns[movee.head.pos[:]] = [movee.x, movee.y] elif keys[pygame.K_DOWN]: movee.x = 0 movee.y = 1 movee.turns[movee.head.pos[:]] = [movee.x, movee.y] for i, c in enumerate(movee.body): p = c.pos[:] if p in movee.turns: turn = movee.turns[p] c.move(turn[0],turn[1]) if i == len(movee.body)-1: movee.turns.pop(p) else: if c.x == -1 and c.pos[0] <= 0: c.pos = (c.height-1, c.pos[1]) elif c.x == 1 and c.pos[0] >= c.height-1: c.pos = (0,c.pos[1]) elif c.y == 1 and c.pos[1] >= c.height-1: c.pos = (c.pos[0], 0) elif c.y == -1 and c.pos[1] <= 0: c.pos = (c.pos[0],c.height-1) else: c.move(c.x,c.y) def add(movee): tail = movee.body[-1] dx, dy = tail.x, tail.y if dx == 1 and dy == 0: movee.body.append(cube((tail.pos[0]-1,tail.pos[1]))) elif dx == -1 and dy == 0: movee.body.append(cube((tail.pos[0]+1,tail.pos[1]))) elif dx == 0 and dy == 1: movee.body.append(cube((tail.pos[0],tail.pos[1]-1))) elif dx == 0 and dy == -1: movee.body.append(cube((tail.pos[0],tail.pos[1]+1))) movee.body[-1].x = dx movee.body[-1].y = dy def draw(movee, surface): for i, c in enumerate(movee.body): if i ==0: c.draw(surface, True) else: c.draw(surface) def drawingAGrid(w, height, surface): sizein = w // height x = 0 y = 0 for l in range(height): x = x + sizein y = y + sizein def redrawGrid(surface): global height, width, s, snack surface.fill((255,255,255)) s.draw(surface) snack.draw(surface) drawingAGrid(width, height, surface) pygame.display.update() def Candy(height, item): positions = item.body while True: x = random.randrange(height) y = random.randrange(height) if len(list(filter(lambda z:z.pos == (x,y), positions))) > 0: continue else: break return (x,y) def gameloop(): global width, height, s, snack, x_pos, y_pos, reset width = 500 height = 20 win = pygame.display.set_mode((width, width)) s = snake((255, 0, 0), (10, 10)) snack = cube(Candy(height, s), color=(0, 0, 0)) flag = True clock = pygame.time.Clock() x_pos, y_pos = s.body[0].pos while flag: pygame.time.delay(50) clock.tick(7) s.move() x, y = s.body[0].pos if not -1 <= x - x_pos <= 1 or not -1 <= y - y_pos <= 1: movee.reset((10,10)) x_pos, y_pos = s.body[0].pos if s.body[0].pos == snack.pos: s.add() snack = cube(Candy(height, s), color=(0, 0, 0)) redrawGrid(win) gameloop() I just want like a scoreboard in any of the corners counting the score.
Use pygame.freetype to render text. e,g, crated a pygame.freetype.SysFont object: import pygame.freetype pygame.init() font = pygame.freetype.SysFont('Times New Roman', 30) The score is the number of body parts. Use str to convert a number to a text and .render() to render a text to a pygame.Surface object: score = len(s.body) text_surf, text_rect = font.render(str(score), (255, 0, 0), size=30) Define a margin to the border of the window, calculate the text position (e.g. bottom right) and .blit the text to the window surfrace: margin = 10 text_pos = (width - text_rect.width - margin, width - text_rect.height - margin) surface.blit(text_surf, text_pos) Function redrawGrid: def redrawGrid(surface): global height, width, s, snack surface.fill((255,255,255)) s.draw(surface) snack.draw(surface) drawingAGrid(width, height, surface) score = len(s.body) text_surf, text_rect = font.render(str(score), (255, 0, 0), size=30) margin = 10 text_pos = (width - text_rect.width - margin, width - text_rect.height - margin) surface.blit(text_surf, text_pos) pygame.display.update()
Scrolling in 2D game?
I'm trying to add a scrolling "camera" that follows the player when it moves but can't figure out how to do this. I know that you can just move the level in the opposite direction when you press one of the movement keys but I'd rather not do that as I plan on adding enemies later on and don't want have to keep update their coordinates as the player moves. I've added my code with a sample level below. Code: import pygame, sys, time, random, math from pygame.locals import * BACKGROUNDCOLOR = (255, 255, 255) WINDOWW = 800 WINDOWH = 600 PLAYERW = 66 PLAYERH = 22 FPS = 60 MOVESPEED = 3 YACCEL = 0.13 GRAVITY = 2 BLOCKSIZE = 30 pygame.init() screen = pygame.display.set_mode((WINDOWW, WINDOWH), 0, 32) mainClock = pygame.time.Clock() testLevel = [ (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,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,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,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,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,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,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,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,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,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,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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,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,), (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,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,0,0,0,0,0,0,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,), (1,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,1,1,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,), (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,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,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,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,), (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,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,), (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,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,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,), (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,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,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,)] def createblock(length, height, color): tmpblock = pygame.Surface((length, height)) tmpblock.fill(color) tmpblock.convert() return tmpblock def terminate(): # Used to shut down the software pygame.quit() sys.exit() def add_level(lvl, bSize): # Creates the level based on a map (lvl) and the size of blocks bList = [] # List of every block bListDisp = [] # List of every block to display bTypeList = [] # List with corresponding type of block(wall, air, etc.) for y in range(len(lvl)): for x in range(len(lvl[0])): if lvl[y][x] == 0: # If the block type on lvl[y][x] is '0', write "air" down in the type list bTypeList.append("air") elif lvl[y][x] == 1: # If the block type on lvl[y][x] is '1', write "wall" down in the type list bTypeList.append("solid") bList.append(pygame.Rect((bSize * x), (bSize * y), bSize, bSize)) #Append every block that is registered bListDisp.append(pygame.Rect((bSize * x), (bSize * y), bSize, bSize)) #Append every block to display that is registered return bList, bListDisp, bTypeList player = pygame.Rect((WINDOWW/2), (WINDOWH - BLOCKSIZE*3), PLAYERW, PLAYERH) wallblock = createblock(BLOCKSIZE, BLOCKSIZE,(20,0,50)) lastTime = pygame.time.get_ticks() isGrounded = False vx = 0 vy = 0 allLevels = [testLevel] # A list containing all lvls(only one for now) maxLevel = len(allLevels) # Checks which level is the last currLevel = allLevels[0] # Current level(start with the first lvl) blockList, blockListDisp, blockTypeList = add_level(currLevel, BLOCKSIZE) # A list with every block and another list with the blocks types thrusters = True jumping = False falling = True while True: """COLLISION""" collision = False for i in range(len(blockTypeList)): if blockTypeList[i] == "solid": if player.colliderect(blockList[i]): collision = True if vx > 0 and not falling: player.right = blockListDisp[i].left vx = 0 print('Collide Right') if vx < 0 and not falling: player.left = blockListDisp[i].right vx = 0 print('Collide Left') if vy > 0: player.bottom = blockListDisp[i].top isGrounded = True falling = False vy = 0 print('Collide Bottom') if vy < 0: player.top = blockListDisp[i].bottom vy = 0 print('Collide Top') else: player.bottom += 1 if player.colliderect(blockList[i]): collision = True #isGrounded = True #falling = False player.bottom -= 1 if not collision: falling = True isGrounded = False # Input pressedKeys = pygame.key.get_pressed() # Checks which keys are being pressed timeDiff = pygame.time.get_ticks() - lastTime # Calculates time difference lastTime += timeDiff # Last time checked reset to current time # Shut-down if the ESC-key is pressed or the window is "crossed down" for event in pygame.event.get(): if event.type == QUIT or event.type == KEYDOWN and event.key == K_ESCAPE: terminate() """X-axis control""" if pressedKeys[ord('a')]: vx = -MOVESPEED if pressedKeys[ord('d')]: vx = MOVESPEED if not pressedKeys[ord('d')] and not pressedKeys[ord('a')]: vx = 0 """Y-axis control""" # Controls for jumping if pressedKeys[ord('w')] and thrusters == True: vy -= YACCEL * timeDiff; # Accelerate along the y-xis when "jumping", but not above/below max speed if vy <= -4: vy = -4 isGrounded = False # You are airborne jumping = True # You are jumping if event.type == KEYUP: # If you let go of the "jump"-button, stop jumping if event.key == ord('w') and vy < 0 and not isGrounded: jumping = False falling = True player.x += vx player.y += vy # Gravity if not isGrounded or falling: vy += 0.3 if vy > 80: vy = 80 screen.fill(BACKGROUNDCOLOR) for i in range(len(blockTypeList)): if blockTypeList[i] == "solid": screen.blit(wallblock, (blockListDisp[i].x, blockListDisp[i].y)) #blit the wall-block graphics pygame.draw.rect(screen, (0, 0, 0), player) pygame.display.update() mainClock.tick(FPS)
The trick is to keep track of camera coordinates and use these as an offset in your rendering code. It looks like you're doing you're rendering right at the end of the code you've posted, drawing each block with coord x,y to pixel x,y on the screen. As you say, shifting the level around isn't great. Instead, have your key inputs (or other camera moving device) change cameraX and cameraY variables, and then add (or subtract, depending which direction you want to go) these values from the block x and y values to change which pixels map to which blocks. I.e. change your rendering to: screen.blit(wallblock, (blockListDisp[i].x + cameraX, blockListDisp[i].y + cameraY)) This means if your camera moves to (10, 20) then you map your block at (5, 5) to (15, 25) on the screen, shifting your whole level across while your underlying model of the level stays the same. Make sense? You can also take this slightly further; if your camera is only being moved to follow your character you can make swap cameraX and cameraY in the above for some function of the character position, and have the whole thing just managed directly there.