Placing a sprite at a coordinate using a for loop - python
I am currently trying to place wall sprites at certain coordinates based on a string from a text file.
What I would like for it to do is read the textfile(getting the string from it). Then based on the character at the index of the string, place a wall sprite at certain coordinates. If it is not a certain character, then just skip placing a wall sprite and move on.
In this case, I want to use # to place a wall. It wont let me post images yet so ill draw it.
TEXTFILE:
###...
As you can see, I place 3 #'s to tell the script I want 3 walls and thats it.
The 3 dots after them are not #, so there should be no wall placed.
In this case it works, as it shows up in my game like such
"# = Wall"
"P = Player"
###
P
But when I try to make a hole in my wall using this kind of placement
TEXTFILE:
###...###
It shows up in my game like such, with no hole in between the two sections of #'s
"# = Wall"
"P = Player"
#########
P
Here is my full main script so far(minus classes), let me know if you need more information. Thanks again!
#I - Import and Initialize
import pygame, IceBackground, Player, Wall, IceBlock
pygame.init()
#D - Display Configuration
screen = pygame.display.set_mode((700, 600))
def main():
pygame.display.set_caption("Sliders")
#E - Entities(Just background for now)
background = pygame.Surface(screen.get_size())
background = background.convert()
background.fill((213, 220, 255))
screen.blit(background, (0, 0))
icebackground = IceBackground.IceBG()
player = Player.Box(90, 150)
iceblock = IceBlock.Box(90, 0)
coords = []
with open("TestLevel.txt") as file:
testLevel = file.read()
print(testLevel)
file.close()
strLength = len(testLevel)
xcoord = 0
ycoord = 0
for i in range(strLength):
print(i)
coordInsert = (xcoord, ycoord)
coords.insert(i, coordInsert)
if testLevel[i] == "#":
print("Wall placed!")
print(coordInsert)
print("\n")
walls = [Wall.Box(xcoord, ycoord) for xcoord, ycoord in coords]
elif testLevel[i] != "#":
print("Nothing placed")
print(coordInsert)
print("\n")
xcoord += 30
#iceblock = [IceBlock.Box(xcoord, ycoord) for xcoord, ycoord, in coords]
backgroundSprites = pygame.sprite.Group(icebackground)
wallSprites = pygame.sprite.Group(*walls)
playerSprites = pygame.sprite.Group(player)
#A - Assign values to key variables
clock = pygame.time.Clock()
keepGoing = True
#L - Set up the Main Loop
while keepGoing:
#T - Timer to set frame rate
clock.tick(60)
#E - Event Handling
for event in pygame.event.get():
if event.type == pygame.QUIT:
keepGoing = False
hitWalls = pygame.sprite.spritecollide(player, wallSprites, False)
if hitWalls:
if player.dx > 0:
player.dx = 0
player.rect.centerx = player.rect.centerx - 10
if player.dx < 0:
player.dx = 0
player.rect.centerx = player.rect.centerx + 10
if player.dy > 0:
player.dy = 0
player.rect.centery = player.rect.centery - 10
if player.dy < 0:
player.dy = 0
player.rect.centery = player.rect.centery + 10
#R - Refresh Display
backgroundSprites.update()
wallSprites.update()
playerSprites.update()
backgroundSprites.draw(screen)
wallSprites.draw(screen)
playerSprites.draw(screen)
pygame.display.flip()
if __name__ == "__main__":
main()
You forgot to increment ycoord by 30 when xcoord == 270. If you don't do so, all your sprite will be aligned.
Related
ZX81 BASIC to Pygame Conversion of "Dropout" Game
I based the code below on this article: http://kevman3d.blogspot.com/2015/07/basic-games-in-python-1982-would-be.html and on the ZX BASIC in this image: 10 LET P=0 20 LET T=P 30 FOR Z=1 T0 10 35 CLS 37 PRINT AT 12,0;T 40 LET R=INT (RND*17) 50 FOR Y=0 TO 10 60 PRINT AT Y,R;"O" 70 LET N=P(INKEY$="4")-(INKEY$="1") 80 IF N<0 OR N>15 THEN LET N=P 100 PRINT AT 11,P;" ";AT 11,N;"┗┛";AT Y,R;" " 110 LET P=N 120 NEXT Y 130 LET T=T+(P=R OR P+1=R) 150 NEXT Z 160 PRINT AT 12,0;"YOU SCORED ";T;"/10" 170 PAUSE 4E4 180 RUN I also shared it on Code Review Stack Exchange, and got a very helpful response refactoring it into high quality Python code complete with type hints. However, for my purposes I'm wanting to keep the level of knowledge required to make this work a little less advanced, including avoiding the use of OOP. I basically want to maintain the "spirit of ZX BASIC" but make the code "not awful." The use of functions is fine, as we were allowed GOSUB back in the day. I'm pretty dubious about the approach of using nested FOR loops inside the main game loop to make the game work, but at the same time I'm curious to see how well the BASIC paradigm maps onto the more event driven approach of Pygame, so I'd welcome any comments on the pros and cons of this approach. More specifically, Is there somewhere I can put the exit code if event.type == pygame.QUIT where it will work during game rounds, without having to repeat the code elsewhere? How would this game be implemented if I were to avoid the use of FOR loops / nested FOR loops? Are there any points of best practice for pygame/Python which I have violated? What improvements can you suggest, bearing in mind my purpose is to write good Pygame code while maintaining the "spirit" of the ZX81 games? Any input much appreciated. I'm also curious to see a full listing implementing some of the ideas arising from my initial attempt if anyone is willing to provide one. import pygame import random import sys # Define colors and other global constants BLACK = (0, 0, 0) WHITE = (255, 255, 255) TEXT_SIZE = 16 SCREEN_SIZE = (16 * TEXT_SIZE, 13 * TEXT_SIZE) NUM_ROUNDS = 5 def print_at_pos(row_num, col_num, item): """Blits text to row, col position.""" screen.blit(item, (col_num * TEXT_SIZE, row_num * TEXT_SIZE)) # Set up stuff pygame.init() screen = pygame.display.set_mode(SCREEN_SIZE) pygame.display.set_caption("Dropout") game_font = pygame.font.SysFont('consolas', TEXT_SIZE) # Create clock to manage how fast the screen updates clock = pygame.time.Clock() # initialize some game variables player_pos, new_player_pos, coin_row, score = 0, 0, 0, 0 # -------- Main Program Loop ----------- while True: score = 0 # Each value of i represents 1 round for i in range(NUM_ROUNDS): coin_col = random.randint(0, 15) # Each value of j represents one step in the coin's fall for j in range(11): pygame.event.get() pressed = pygame.key.get_pressed() if pressed[pygame.K_RIGHT]: new_player_pos = player_pos + 1 elif pressed[pygame.K_LEFT]: new_player_pos = player_pos - 1 if new_player_pos < 0 or new_player_pos > 15: new_player_pos = player_pos # --- Game logic player_pos = new_player_pos coin_row = j if player_pos + 1 == coin_col and j == 10: score += 1 # --- Drawing code # First clear screen screen.fill(WHITE) player_icon = game_font.render("|__|", True, BLACK, WHITE) print_at_pos(10, new_player_pos, player_icon) coin_text = game_font.render("O", True, BLACK, WHITE) print_at_pos(coin_row, coin_col, coin_text) score_text = game_font.render(f"SCORE: {score}", True, BLACK, WHITE) print_at_pos(12, 0, score_text) # --- Update the screen. pygame.display.flip() # --- Limit to 6 frames/sec maximum. Adjust to taste. clock.tick(8) msg_text = game_font.render("PRESS ANY KEY TO PLAY AGAIN", True, BLACK, WHITE) print_at_pos(5, 0, msg_text) pygame.display.flip() waiting = True while waiting: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit(0) if event.type == pygame.KEYDOWN: waiting = False
Here's my reorganisation of your code: import pygame import random # Define global constants TEXT_SIZE = 16 SCREEN_SIZE = (16 * TEXT_SIZE, 13 * TEXT_SIZE) NUM_ROUNDS = 5 def print_at_pos(row_num, col_num, item): """Blits text to row, col position.""" screen.blit(item, (col_num * TEXT_SIZE, row_num * TEXT_SIZE)) # Set up stuff pygame.init() screen = pygame.display.set_mode(SCREEN_SIZE) pygame.display.set_caption("Dropout") game_font = pygame.font.SysFont("consolas", TEXT_SIZE) # Create clock to manage how fast the screen updates clock = pygame.time.Clock() # draw the images player_icon = game_font.render("|__|", True, "black", "white") # if we don't specify a background color, it'll be transparent coin_text = game_font.render("O", True, "black") msg_text = game_font.render("PRESS ANY KEY TO PLAY AGAIN", True, "black", "white") # initialize some game variables waiting = False # start in game player_pos = 0 score = 0 game_round = 0 coin_row = 0 coin_col = random.randint(0, 15) running = True # For program exit # -------- Main Program Loop ----------- while running: # event handling for event in pygame.event.get(): if event.type == pygame.QUIT: running = False elif event.type == pygame.KEYDOWN: if waiting: waiting = False score = 0 # reset score elif event.key == pygame.K_LEFT: player_pos -= 1 elif event.key == pygame.K_RIGHT: player_pos += 1 # --- Game logic if waiting: # don't update the game state or redraw screen print_at_pos(5, 0, msg_text) else: coin_row += 1 # TODO: decouple from frame rate if -1 > player_pos: player_pos = -1 # so we can catch a coin at zero elif 15 < player_pos: player_pos = 15 # coin is in scoring position if coin_row == 10: if player_pos + 1 == coin_col: score += 1 elif coin_row > 10: # round is over coin_col = random.randint(0, 15) coin_row = 0 game_round+= 1 if game_round >= NUM_ROUNDS: waiting = True game_round = 0 # reset round counter # --- Drawing code screen.fill("white") # clear screen print_at_pos(10, player_pos, player_icon) print_at_pos(coin_row, coin_col, coin_text) score_text = game_font.render(f"SCORE: {score}", True, "black", "white") print_at_pos(12, 0, score_text) # --- Update the screen. pygame.display.flip() # --- Limit to 6 frames/sec maximum. Adjust to taste. clock.tick(6) pygame.quit() I've used a boolean waiting to allow for common event and game state handling that only moves during gameplay. For more complex interactions, you'll want a state machine. The coin movement is currently coupled to the frame rate, which is easy, but ideally you'd specify a rate/time interval, e.g. 200ms between row drops and then you could have a refresh rate similar to the monitor refresh rate.
I get an incorrect position from .get_rect()?
I'm currently working on a school project where I'm making a "hexcells" similar game in pygame and now I'm trying to blit an a new image if the user has clicked a current image. It will blit an image in the top left area, if clicked in the top left area, but not if I click any of the existing images. I told the program to print the coordinates from the images with help of the .get_rect() function, but it remains the same whereever I click and the coordinates aren't even where a image is. Can someone help me understand how this works and help me blit the new images on top of the existing images? Code below is not the entire document, however there is so much garbage/trash/unused code so I'd thought I spare you the time of looking at irrelevant code. Also sorry if the formatting is wrong or the information isn't enough, I tried my best. import pygame, sys from pygame.locals import * #Magic numbers fps = 30 winW = 640 winH = 480 boxSize = 40 gapSize = 75 boardW = 3 boardH = 3 xMargin = int((winW - (boardW * (boxSize + gapSize))) / 2) yMargin = int((winW - (boardW * (boxSize + gapSize))) / 2) #Lil bit o' color R G B NAVYBLUE = ( 60, 60, 100) correctCords = [[175,275,375],[375,275,175]] bgColor = NAVYBLUE unC = pygame.image.load("unC.png") cor = pygame.image.load("correct.png") inc = pygame.image.load("wrong.png") correct = "Correct" inCorrect = "Incorrect" def main(): global FPSCLOCK, DISPLAYSURF pygame.init() FPSCLOCK = pygame.time.Clock() DISPLAYSURF = pygame.display.set_mode((winW, winH)) mousex = 0 #stores x-coordinate of mouse event mousey = 0 #stores y-coordinate of mouse event pygame.display.set_caption("Branches") DISPLAYSURF.fill(bgColor) gridGame(inCorrect, correct,gapSize,xMargin,yMargin,boxSize) while True: mouseClicked = False for event in pygame.event.get(): if event.type == QUIT or (event.type == KEYUP and event.key == K_ESCAPE): pygame.quit() sys.exit() elif event.type == MOUSEMOTION: mousex,mousey = event.pos elif event.type == MOUSEBUTTONUP: mousex, mousey = event.pos pos = pygame.mouse.get_pos() mouseClicked = True unCa = unC.get_rect() corA = cor.get_rect() print unCa print corA print pos if unCa.collidepoint(pos): DISPLAYSURF.blit(cor,(mousey,mousex)) """lada = unC.get_rect() lada = if mousex and mousey == lada: for x in correctCords: for y in x: for z in x: if mousey and mousex == z and y: DISPLAYSURF.blit(cor,(mousey,mousex)) print lada""" pygame.display.update() FPSCLOCK.tick(fps) def gridGame(inCorrect, correct,gapSize,xMargin,yMargin,boxSize): grid = [] cordX = [] cordY = [] correctRecs = [] #cordinates = [] #cordinates.append([]) #cordinates.append([]) #cordinates.append([]) #this is basically getBoard() all over again #This part will arrange the actual backend grid for row in range(3): grid.append([]) #cordinates[0].append(gapSize+(row+1)*100) #cordinates[1].append(gapSize+(row+1)*100) #cordinates[2].append(gapSize+(row+1)*100) for column in range(3): grid[row].append(inCorrect) for row in range(3): cordX.append([]) for column in range(3): cordX[row].append(gapSize+(row+1)*100) for row in range(3): cordY.append([]) for column in range(3): cordY[row].append(gapSize+(column+1)*100) #print cordX[0][0], cordY[0][0] grid[0][2] = correct grid[1][1] = correct grid[2][0] = correct #Y-AXEL SKRIVS FoRST ([Y][X]) #print cordinates[2][1] DISPLAYSURF.blit(cor,(100,100)) #Let's draw it as well for row in range(3): for column in range(3): DISPLAYSURF.blit(unC,(gapSize+(row+1)*100,gapSize+(column+1)*100)) main() Also real sorry about the horrible variable naming and occasional swedish comments.
unCa = unC.get_rect() gives you only image size - so use it only once at start (before while True) - and later use the same unCa all the time to keep image position and change it. btw: better use more readable names - like unC_rect ie. # move 10 pixel to the right unC_rect.x += 10 # set new position unC_rect.x = 10 unC_rect.right = 100 unC_rect.topleft = (10, 200) unC_rect.center = (10, 200) # center on screen unC_rect.center = DISPLAYSURF.get_rect().center etc. And then use this rect to blit image blit(unC, unC_rect) and check collision with other rect if unC_rect.colliderect(other_rect): or with point - like mouse position elif event.type == MOUSEMOTION: if unC_rect.collidepoint(pygame.mouse.get_pos()): hover = True # shorter elif event.type == MOUSEMOTION: hover = unC_rect.collidepoint(pygame.mouse.get_pos()):
Pygame: Colliding Rectangle on multiple other rectangles
I am attempting to create a game in which a block moves back and forth until the player presses space. Upon which, the block jumps to the next line up and stops. Currently i am having problems with the collision code. The error being thrown up by the shell is: if doRectsOverlap(j['rect'], floors['line']): TypeError: list indices must be integers, not str I am stuck with understanding where my code has gone wrong. My knowledge of how python works is very limited. There is also code i have commented out to do with the floor moving dowards when the player jumps. it has been commented out until i can get the collisions working, but still included Code Below: import pygame, sys, time from pygame.locals import * def doRectsOverlap(rect1, rect2): for a, b in [(rect1, rect2), (rect2, rect1)]: # Check if a's corners are inside b if ((isPointInsideRect(a.left, a.top, b)) or (isPointInsideRect(a.left, a.bottom, b)) or (isPointInsideRect(a.right, a.top, b)) or (isPointInsideRect(a.right, a.bottom, b))): return True return False def isPointInsideRect(x, y, rect): if (x > rect.left) and (x < rect.right) and (y > rect.top) and (y < rect.bottom): return True else: return False # set up pygame pygame.init() mainClock = pygame.time.Clock() # set up the window WINDOWWIDTH = 480 WINDOWHEIGHT = 800 windowSurface = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT), 0, 32) pygame.display.set_caption('Jumper') #Directions LEFT = 4 RIGHT = 6 UP = 8 DOWN = 2 STILL = 5 #blocks location for jumping #BLOCKLOCY = 700 #Binary for stopping movement #STOPPER = 0 MOVESPEED = 1 # set up the colors BLACK = (0, 0, 0) RED = (255, 0, 0) GREEN = (0, 255, 0) BLUE = (0, 0, 255) j = {'rect':pygame.Rect(240, 700, 20, 20), 'color':GREEN, 'dir':LEFT, 'jump':STILL} f1 = {'line':pygame.Rect(0,720,480,2), 'color':GREEN, 'dir':STILL} f2 = {'line':pygame.Rect(0,650,480,2), 'color':GREEN, 'dir':STILL} floors = [f1,f2] # run the game loop while True: # check for the QUIT event for event in pygame.event.get(): if event.type == QUIT: pygame.quit() sys.exit() # draw the black background onto the surface windowSurface.fill(BLACK) # move the block data structure if j['dir'] == LEFT: j['rect'].left -= MOVESPEED if j['dir'] == RIGHT: j['rect'].left += MOVESPEED if j['jump'] == UP: j['rect'].bottom -= MOVESPEED #BLOCKLOCY -= MOVESPEED if j['rect'].left < 0: j['dir'] = RIGHT if j['rect'].left > WINDOWWIDTH-j['rect'].width: j['dir'] = LEFT if event.type == KEYDOWN: if event.key == K_SPACE: j['jump'] = UP if doRectsOverlap(j['rect'], floors['line']): j['jump'] = STILL #Floor controll code for moving level - not working currently # for f in floors: #if f['dir'] == DOWN: # f['line'].y += MOVESPEED # if event.type == KEYDOWN: # if event.key == K_SPACE: # f['dir'] = DOWN # if f['line'].top == BLOCKLOCY: # f['dir'] = STILL # STOPPER = 1 #if f['line'].bottom == BLOCKLOCY: # f['dir'] = STILL # STOPPER = 1 # draw the block onto the surface pygame.draw.rect(windowSurface, j['color'], j['rect']) pygame.draw.rect(windowSurface, f['color'], f['line']) # draw the window onto the screen pygame.display.update() mainClock.tick(40)
You are creating floors as a list: f1 = {'line':pygame.Rect(0,720,480,2), 'color':GREEN, 'dir':STILL} f2 = {'line':pygame.Rect(0,650,480,2), 'color':GREEN, 'dir':STILL} floors = [f1,f2] So when you call: if doRectsOverlap(j['rect'], floors['line']): j['jump'] = STILL You're message is telling you that you need an index as an int: for n in range(len(floors)): if doRectsOverlap(j['rect'], floors[n]['line']): j['jump'] = STILL
Python clicking game, updating score
import pygame from pygame.locals import * import random import time pygame.init() randomNumber = random.randint(1,600) randomNumber2 = random.randint(1,600) x = 0 text = "" squareCount = 0 beenHere = 0 # colours = (red, green, blue) BLACK = (0, 0, 0) RED = (255, 0, 0) GREEN = (0, 255, 0) WHITE = (255, 255, 255) LBLUE = (0, 123, 255) colour = RED # Make a window appear size = (700, 500) screen = pygame.display.set_mode(size) pygame.display.set_caption("Square Chase | Score: 0") screen.fill(LBLUE) pygame.display.flip() pygame.time.set_timer(USEREVENT + 1, 1500) done = False clock = pygame.time.Clock() while done == False: for event in pygame.event.get(): print(event) if event.type == pygame.QUIT: done = True if event.type == USEREVENT + 1: screen.fill(LBLUE) randomNumber = random.randint(1,625) randomNumber2 = random.randint(1,420) mySquare = pygame.draw.rect(screen,colour,(randomNumber,randomNumber2,50,50),5) squareCount = squareCount + 1 if squareCount == 50: done == true pygame.display.flip() if event.type == pygame.MOUSEBUTTONDOWN: y, z = pygame.mouse.get_pos() is_inside = mySquare.collidepoint(y, z) if is_inside and colour == GREEN: x = x+1 text = str(x) pygame.display.set_caption("Square Chase | Score " + text) colour = RED elif is_inside: x = x+1 text = str(x) pygame.display.set_caption("Square Chase | Score " + text) colour = GREEN clock.tick(20) pygame.quit() I am aiming to create a game in which the user has to click on a square for their score to increase. They get 50 chances to do this. If they click in the square within the 1.5 seconds the colour of the square changes. The above code works apart from each time a new square is drawn the user could click on it say 5 times and their score will go up by 5. Any suggestions as to how to get the score to increase by just one? Thanks in advance.
Wish I could just use a comment but I have lack of reputation. I didn't look through all your code, but your description makes it sound like a simple flag would be able to help you. Once you recognize it has been clicked, increment the score AND set a boolean. So in essence you will want something along the lines of if clicked == false score = score+1 clicked = true You will want to clear the flag back to false once another block has appeared.
Wouldn't it be better, if a new square would show up after a successful keypress? That way the game would be more dynamic. It's easy to change this. Instead of using user events that are called every 1.5 sec, sum the value returned by clock.tick() and when their sum will be greater than 1500 ms, create a new rectangle. This is a better approach, because you can call the method that creates a new square and reset the timer right after a successful keypress. Some code to get you started: def new_rect(screen,colour): screen.fill(LBLUE) randomNumber = random.randint(1,625) randomNumber2 = random.randint(1,420) mySquare = pygame.draw.rect(screen,colour,(randomNumber,randomNumber2,50,50),5) return mySquare done = False clock = pygame.time.Clock() timer_var = 0 while done == False: if(timer_var > 1500): timer_var = 0 mySquare = new_rect(screen,color) squareCount = squareCount + 1 if squareCount == 50: done == true for event in pygame.event.get(): if event.type == pygame.MOUSEBUTTONDOWN: y, z = pygame.mouse.get_pos() is_inside = mySquare.collidepoint(y, z) if is_inside: x = x+1 text = str(x) pygame.display.set_caption("Square Chase | Score " + text) timer_var = 0 new_rect(screen,colour) squareCount = squareCount + 1 if squareCount == 50: done == true if colour == GREEN: colour = RED else: colour = GREEN timer_var += clock.tick(20) pygame.quit()
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.