Scrolling backgrounds with map-based collision detection Pygame - python
I am creating a game, that uses a map for collision, this all worked until I attempted to add scrolling to the game. It is supposed to make the background move instead of the player, which it does, but the problem lies within the collision detection.
The size of each collision point goes from 16 by 16, to 1 by 1.
This has proven to be quite problematic, and any help with fixing it will be greatly appreciated
I have tried changed to player's collision values, screen sizes, but in the end I am not sure what the exact cause of this problem is, my best guess would be that my removal of the player's movement and instead having it remain at the same position on the screen, has lead to problem with how the collision is suppose to act with the change in position, but this doesn't explain the shrinking of the collision points.
import pygame, sys
clock = pygame.time.Clock()
from pygame.locals import *
pygame.init() # initiates pygame
pygame.display.set_caption('The Game')
WINDOW_SIZE = (600,400)
screen = pygame.display.set_mode(WINDOW_SIZE,0,32)
display = pygame.Surface((300,200))
moving_right = False
moving_left = False
moving_up = False
moving_down = False
game_map = [['2','2','2','2','2','2','2','2','2','2','2','2','2','2','2','2','2','2','2'],
['2','2','2','2','2','2','2','2','2','2','2','2','2','2','2','2','2','2','2'],
['2','2','2','2','2','2','2','2','2','2','2','2','2','2','2','2','2','2','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','0','0','0','0','0','0','0','0','0','0'],
['0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'],
['0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'],
['0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'],
['0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'],
['0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'],
['2','2','2','2','2','2','2','2','2','2','2','2','2','2','2','2','2','2','2'],
['2','2','2','2','2','2','2','2','2','2','2','2','2','2','2','2','2','2','2'],
['0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0']]
player_rect = pygame.Rect(100,100,16,16)
black = (45,45,45)
black1 = (60,60,60)
def collision_test(rect,tiles):
hit_list = []
for tile in tiles:
if rect.colliderect(tile):
hit_list.append(tile)
return hit_list
def move(rect,movement,tiles):
collision_types = {'top':False,'bottom':False,'right':False,'left':False}
rect.x += movement[0]
hit_list = collision_test(rect,tiles)
for tile in hit_list:
if movement[0] > 0:
rect.right = tile.left
collision_types['right'] = True
elif movement[0] < 0:
rect.left = tile.right
collision_types['left'] = True
rect.y += movement[1]
hit_list = collision_test(rect,tiles)
for tile in hit_list:
if movement[1] > 0:
rect.bottom = tile.top
collision_types['bottom'] = True
elif movement[1] < 0:
rect.top = tile.bottom
collision_types['top'] = True
return rect, collision_types
while True:
display.fill((155,155,155))
tile_rects = []
y = 0
for layer in game_map:
x = 0
for tile in layer: # tiles
if tile == '2':
pygame.draw.rect(display,black,(x*16-player_rect.x,y*16-player_rect.y,16,16))
if tile != '0':
tile_rects.append(pygame.Rect(x*16-player_rect.x,y*16-player_rect.y,16,16))
x += 1
y += 1
player_movement = [0,0]
if moving_right == True:
player_movement[0] += 2
if moving_left == True:
player_movement[0] -= 2
if moving_up == True:
player_movement[1] -= 2
if moving_down == True:
player_movement[1] += 2
player_rect,collisions = move(player_rect,player_movement,tile_rects)
pygame.draw.rect(display,black1,(142,100,16,16)) #replacing the 142 and 100 with the rect.x and y, and remove the rect.x and y from the tiles, will make it work like the original
for event in pygame.event.get(): # event loop
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if event.key == K_RIGHT:
moving_right = True
if event.key == K_LEFT:
moving_left = True
if event.key == K_UP:
moving_up = True
if event.key == K_DOWN:
moving_down = True
if event.type == KEYUP:
if event.key == K_RIGHT:
moving_right = False
if event.key == K_LEFT:
moving_left = False
if event.key == K_UP:
moving_up = False
if event.key == K_DOWN:
moving_down = False
screen.blit(pygame.transform.scale(display,WINDOW_SIZE),(0,0))
pygame.display.update()
clock.tick(60)
My expected result was for the collision points not to change and remain 16 by 16, unfortunately, instead the collision points for the tiles has shrank to 1 by 1, leading to many problems.
Your code is a bit hard to follow, but I've understood where the problem is.
In your move function you change the coordinates of player_rect to make it move. But then when you draw it (pygame.draw.rect(display,black1,(142,100,16,16))) you draw it at the center. So there is a mismatch between what you draw and what is tested for movement.
Honestly, I cannot find a way to solve it without changing a lot of code.
If you want to keep the backgroung scrolling, consider to not move the player but to move the tiles in the opposite direction. I mean, if the player goes left, move the tiles right, and so on.
Currently you are creating the tiles each iteration. I would recommend to create them outside the main loop, and in the loop move them (editing the coordinates of their rects).
Related
Ball doesn't move when space is pressed
Does anybody see the problem in this code? Trying to make the ball/bullet come out of the left paddle and disappear once it hits the right paddle but when I try running the code I don't see the ball in general. I think its hidden behind the left paddle but its not moving either when I'm space. I am trying to make a game where the left player would should bullets and the right player would try to dodge them. Now I'm stuck at the bullet. Please help. Thanks! import pygame pygame.init() screen_width = 1280 screen_height = 720 window = pygame.display.set_mode((screen_width, screen_height),0,0) pygame.display.set_caption("PongGame") clock = pygame.time.Clock() #background background = pygame.image.load("C:/Users/teamb/Desktop/PythonWorkspace/myGame/background.png") #paddle image paddle = pygame.image.load("C:/Users/teamb/Desktop/PythonWorkspace/myGame/paddle.png") paddle_size = paddle.get_rect().size #left paddle left_paddle_width = paddle_size[0] left_paddle_height = paddle_size[1] left_paddle_xpos = 10 left_paddle_ypos = (screen_height/2 - left_paddle_height/2) to_x = 0 to_y = 0 character_speed = 10 #right paddle right_paddle_width = paddle_size[0] right_paddle_height = paddle_size[1] right_paddle_xpos = screen_width - right_paddle_width - 10 right_paddle_ypos = (screen_height/2 - left_paddle_height/2) to_y_2 = 0 # ball bullet = pygame.image.load("C:/Users/teamb/Desktop/PythonWorkspace/myGame/ball.png") bullet_size = bullet.get_rect().size bullet_width = bullet_size[0] #make the bullet be able to shoot multiple at the same time bullets = [] #bulllet speed bullet_speed = 15 running = True while running: dt = clock.tick(60) for event in pygame.event.get(): if event.type == pygame.QUIT: running = False #left paddle if event.type == pygame.KEYDOWN: if event.key == pygame.K_w: to_y -= character_speed elif event.key == pygame.K_s: to_y += character_speed elif event.key == pygame.K_SPACE: bullet_ypos = left_paddle_ypos bullet_xpos = left_paddle_xpos bullets.append([bullet_xpos, bullet_ypos]) if event.type == pygame.KEYUP: if event.key == pygame.K_w or event.key == pygame.K_s: to_y = 0 #right paddle if event.type == pygame.KEYDOWN: if event.key == pygame.K_UP: to_y_2 -= character_speed elif event.key == pygame.K_DOWN: to_y_2 += character_speed if event.type == pygame.KEYUP: if event.key == pygame.K_UP or event.key == pygame.K_DOWN: to_y_2 = 0 left_paddle_ypos += to_y right_paddle_ypos += to_y_2 bullets = [ [w[0] - bullet_speed, w[1]] for w in bullets] #shoot the bullet bullets = [ [w[0], w[1]] for w in bullets if w[0] == right_paddle_xpos and w[1] == right_paddle_ypos] #barrier for the paddles to not go outside the screen. if left_paddle_ypos < 0: left_paddle_ypos = 0 if left_paddle_ypos > screen_height - left_paddle_height: left_paddle_ypos = screen_height - left_paddle_height window.blit(background,(0,0)) window.blit(paddle, (left_paddle_xpos,left_paddle_ypos)) window.blit(paddle, (right_paddle_xpos, right_paddle_ypos)) for bullet_xpos, bullet_ypos in bullets: window.blit(bullet, (bullet_xpos, bullet_ypos)) pygame.display.update() pygame.quit()
Two changes required: Remove this line. It clears the bullet list before the bullets are shown. #bullets = [ [w[0], w[1]] for w in bullets if w[0] == right_paddle_xpos and w[1] == right_paddle_ypos] Change this line (- to +). It sends the bullets in the wrong direction. bullets = [ [w[0] + bullet_speed, w[1]] for w in bullets] #shoot the bullet
Trying to make the ball/bullet... disappear once it hits the right paddle So, I assume this is the line intended to have that effect: bullets = [ [w[0], w[1]] for w in bullets if w[0] == right_paddle_xpos and w[1] == right_paddle_ypos] Read it more carefully: for each of the bullet positions, we will check if it is positioned exactly where the right paddle is (i.e., top-left corners coincide!), and if so, keep that one unmodified; everything else is discarded. You need to use proper collision logic, and you need to keep bullets that have not collided with the absorber. Incidentally: elif event.key == pygame.K_SPACE: bullet_ypos = left_paddle_ypos bullet_xpos = left_paddle_xpos bullets.append([bullet_xpos, bullet_ypos]) How exactly did you want new bullets to be positioned, relative to the left paddle? How does this code say they will be positioned? bullets = [ [w[0] - bullet_speed, w[1]] for w in bullets] #shoot the bullet Which way do you want the bullet to move, if its speed is positive? Which way will it actually move, according to this logic?
PongGame paddle moves but previously drawn paddle doesnt' erase
The left paddle works fine it moves up and down no problem. But the right is okay when I don't move but when I do it moves to the directions I coded to but doesn't erase the previously drawn location which in the end just draws a straight line. Trying to make my first ever game with no tutorial and I'm kind of stuck Here's the code import pygame pygame.init() screen_width = 1280 screen_height = 720 window = pygame.display.set_mode((screen_width, screen_height),0,0) pygame.display.set_caption("PongGame") clock = pygame.time.Clock() #background background = pygame.image.load("C:/Users/teamb/Desktop/PythonWorkspace/myGame/background.png") #paddle image paddle = pygame.image.load("C:/Users/teamb/Desktop/PythonWorkspace/myGame/paddle.png") paddle_size = paddle.get_rect().size #left paddle left_paddle_width = paddle_size[0] left_paddle_height = paddle_size[1] left_paddle_xpos = 10 left_paddle_ypos = (screen_height/2 - left_paddle_height/2) to_x = 0 to_y = 0 character_speed = 10 #right paddle right_paddle_width = paddle_size[0] right_paddle_height = paddle_size[1] right_paddle_xpos = screen_width - right_paddle_width - 10 right_paddle_ypos = (screen_height/2 - left_paddle_height/2) to_y_2 = 0 # ball ball = pygame.image.load("C:/Users/teamb/Desktop/PythonWorkspace/myGame/ball.png") running = True while running: dt = clock.tick(60) print("fps : " + str(clock.get_fps())) for event in pygame.event.get(): if event.type == pygame.QUIT: running = False #left paddle if event.type == pygame.KEYDOWN: if event.key == pygame.K_w: to_y -= character_speed elif event.key == pygame.K_s: to_y += character_speed if event.type == pygame.KEYUP: if event.key == pygame.K_w or event.key == pygame.K_s: to_y = 0 #right paddle if event.type == pygame.KEYDOWN: if event.key == pygame.K_UP: to_y_2 -= character_speed elif event.key == pygame.K_DOWN: to_y_2 += character_speed if event.type == pygame.KEYUP: if event.key == pygame.K_UP or event.key == pygame.K_DOWN: to_y_2 = 0 left_paddle_ypos += to_y right_paddle_ypos += to_y_2 if left_paddle_ypos < 0: left_paddle_ypos = 0 if left_paddle_ypos > screen_height - left_paddle_height: left_paddle_ypos = screen_height - left_paddle_height window.blit(background,(0,0)) window.blit(paddle, (left_paddle_xpos,left_paddle_ypos)) window.blit(paddle, (right_paddle_xpos, right_paddle_ypos)) window.blit(ball, (640, 360)) pygame.display.update() pygame.quit()
Moste likely the background Surface (background) has a smaller width than the display Surface (window). Clear the display before drawing the scene: while running: # [...] window.fill(0) # <--- clear display window.blit(background,(0,0)) window.blit(paddle, (left_paddle_xpos,left_paddle_ypos)) window.blit(paddle, (right_paddle_xpos, right_paddle_ypos)) window.blit(ball, (640, 360)) pygame.display.update() Or scale the background image by pygame.transform.smoothscale: background = pygame.image.load("C:/Users/teamb/Desktop/PythonWorkspace/myGame/background.png") background = pygame.transform.smoothscale(background, window.get_size())
This line is redrawing your background on every movement: window.blit(background,(0,0)) However, I suppose your background image is actually smaller than your play field, so you are not redrawing the background on that side. You can either make your background image larger, or draw it again using an offset, or even draw it stretched. The idea is cover the entire play field when you are drawing the background. It is worth pointing out this is not necessarily the most optimized way to do that. Ideally, you would redraw only the parts that had changed, but this is way more complex. Drawing the entire background is an easy fix.
Multiplayer Following Camera in Pygame
For a school project I am building a recreation of Among Us in python with Pygame. I have already set up all the server and client side code and that's all working fine. I'm now in the process of making the camera follow the player. Only I can't get it to work. My idea was: when a player moves, everything in his surroundings has to move in the opposite direction. But when you have a multiplayer game this doesn't work. Because then the other player moves as well which breaks the system. If anyone has any idea how to make such a code, please let me know. Thank you in advance
You don't have to move the background and object rects around the player, you can just move the player around and have a scroll offset value that keeps track of how much the blitted objects have to be offset. You don't apply the scroll value to the rect position because the rect position isn't relative to the window like the blit objects are. Here is an example of how you could achieve this. import pygame, sys clock = pygame.time.Clock() from pygame.locals import * pygame.init() pygame.display.set_caption("Scrolling example") WINDOW_SIZE = (600, 400) screen = pygame.display.set_mode(WINDOW_SIZE, 0, 32) scroll = [0, 0] player = pygame.Rect(100, 100, 10, 10) up = False down = False left = False right = False blocks = [pygame.Rect(250,250,50,50)] while True: screen.fill((0, 0, 0)) scroll[0] += (player.x - scroll[0] - (WINDOW_SIZE[0]/2)) // 20 scroll[1] += (player.y - scroll[1] - (WINDOW_SIZE[1]/2)) // 20 player_movement = [0, 0] if right == True: player_movement[0] += 2 if left == True: player_movement[0] -= 2 if up == True: player_movement[1] -= 2 if down == True: player_movement[1] += 2 player.x += player_movement[0] player.y += player_movement[1] player_scroll_rect = player.copy() player_scroll_rect.x -= scroll[0] player_scroll_rect.y -= scroll[1] pygame.draw.rect(screen, (255,255,255), player_scroll_rect) for block in blocks: scroll_block = block.copy() scroll_block.x = scroll_block.x - scroll[0] scroll_block.y = scroll_block.y - scroll[1] pygame.draw.rect(screen, (0,0,255), scroll_block) for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() if event.type == KEYDOWN: if event.key == K_RIGHT: right = True if event.key == K_LEFT: left = True if event.key == K_UP: up = True if event.key == K_DOWN: down = True if event.type == KEYUP: if event.key == K_RIGHT: right = False if event.key == K_LEFT: left = False if event.key == K_UP: up = False if event.key == K_DOWN: down = False pygame.display.update() clock.tick(60) If you want a solution for using images, ask me. Also, you can find more about scrolling here: https://www.youtube.com/watch?v=5q7tmIlXROg
How do I create a border in Pygame so my character doesn't walk over an image? [duplicate]
This question already has answers here: Not letting the character move out of the window (2 answers) Closed 2 years ago. I need a rectangular border around an image so my character stops walking through it. It would be nice if my character stopped at certain coordinates instead of walking over the image. I've tried to create a border with my 'x' and 'y' coordinates, but the border seems to stretch across the screen. import pygame from pygame.locals import * pygame.init() SCREEN_WIDTH = 800 SCREEN_HEIGHT = 700 screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT,)) pygame.display.set_caption("Zombie Hunters") background = pygame.image.load("background.jpg").convert() background = pygame.transform.scale(background, (SCREEN_WIDTH,SCREEN_HEIGHT)) player = pygame.image.load("character no gun.png").convert_alpha() player = pygame.transform.scale(player, (270, 270)) # these are the coordinates to move the player x, y = 0, 0 MOVE_RIGHT = 1 MOVE_LEFT = 2 MOVE_UP = 3 MOVE_DOWN = 4 direction = 0 speed = 1 #House barrier barrier_xlocation = 345 barrier_ylocation = 80 barrier_width = 190 barrier_height = 260 while True: for event in pygame.event.get(): if event.type == QUIT: exit() if event.type == KEYDOWN: if event.key == ord('q'): pygame.quit() exit() if event.key == K_LEFT: direction = MOVE_LEFT if event.key == K_RIGHT: direction = MOVE_RIGHT if event.key == K_UP: direction = MOVE_UP if event.key == K_DOWN: direction = MOVE_DOWN elif event.type == KEYUP: if event.key == K_LEFT: direction = 0 if event.key == K_RIGHT: direction = 0 if event.key == K_UP: direction = 0 if event.key == K_DOWN: direction = 0 if(direction == MOVE_LEFT): x-= speed if(direction == MOVE_RIGHT): x+= speed if(direction == MOVE_UP): y-= speed if(direction == MOVE_DOWN): y += speed #Background screen.blit(background, (0, 0)) #House border pygame.draw.rect(screen, (255,0,0), (barrier_xlocation,barrier_ylocation,barrier_width,barrier_height), 2) #Player hitbox pygame.draw.rect(screen, (255,0,0), (x + 117,y + 105, 50,50),2) screen.blit(player, (x,y)) pygame.display.update() I don't get any error messages, but I need to create a border around the house.
Use pygame.Rect objects and .colliderect() to check for the collision of two rectangles. Store the current position of the player. After the player position has change, check for a collision with the barrier. When the player and the barrier are colliding, then reset the position of the player. The size of as [pygame.Surface] object can be get by .get_size(): # store current position px, py = x, y # change position if(direction == MOVE_LEFT): x-= speed if(direction == MOVE_RIGHT): x+= speed if(direction == MOVE_UP): y-= speed if(direction == MOVE_DOWN): y += speed # set player and barrier rectangle playerRect = pygame.Rect(x, y, *player.get_size()) barrierRect = pygame.Rect( barrier_xlocation, barrier_ylocation, barrier_width, barrier_height) # check for collision if playerRect.colliderect(barrierRect): # reset position x, y = px, py
Trying to figure out a way to allow my image to move in opposite direction when colliding with object
A bit long, but I am not looking for a copy and paste answer. I just want to know a good way on how to approach this. Code pasted for visual preference. In better terms. My image is colliding with another image, this object is representing the tile (floor/roof/wall/etc). I place these tiles in different locations throughout my screen. I store these positions using rect objects and storing them into a list known as terrain. So I iterate through my list and when I encounter a collision with my hero and some terrain object, stop movement. I now realize that this is a problem. When this happens and I do collide, I can no longer move my object, it is stuck. So how can I get my object to still move around, BUT when it has collided with some object. It is not allowed to continue to move further in that same direction. Here are is the main piece for movement controlling: The moveDown statement is the part where I try to implement it. floorCollusion is a list of 5 False booleans. #0 represents - no collision #1 " " - top face has collided #2 " " - right face has collided #3 " " - bottom face has collided #4 " " - left face has collided I see I have binary operations. Either I move or I don't. I just can't figure another way to implement this. You don't need to add on to the code. A basic idea on how to see this would be appreciated. moveDown = False moveUp = False moveLeft = False moveRight = False #when key down either move the hero or quit game elif event.type == KEYDOWN: if event.key in (K_UP, K_w): moveDown = False moveUp = True playerObj['jumping'] = True ####### elif event.key in (K_DOWN,K_s): moveUp = False moveDown = True elif event.key in (K_LEFT, K_a): moveRight = False moveLeft = True #if the image is not facing left, display the left img if (playerObj['facing']) != LEFT: playerObj['surface'] = L_HERO_IMG playerObj['facing'] = LEFT elif event.key in (K_RIGHT, K_d): moveLeft = False moveRight = True #if the image is not facing right, display the right img if (playerObj['facing']) != RIGHT: playerObj['surface'] = R_HERO_IMG playerObj['facing'] = RIGHT elif event.key == K_ESCAPE: terminate() #testing hit point system elif event.key == K_SPACE: playerObj['health'] -= 3 if playerObj['health'] <= 0: gameOver = True #when key is lifted stop movement elif event.type == KEYUP: if event.key in (K_UP, K_w): moveUp = False elif event.key in (K_DOWN, K_s): moveDown = False elif event.key in (K_LEFT,K_a): moveLeft = False elif event.key in (K_RIGHT,K_d): moveRight = False #moving the hero if not gameOver: if moveLeft: playerObj['x'] -= MOVERATE if moveRight: playerObj['x'] += MOVERATE #if the player pressed key up or 'w' and hero isn't jump #the hero will seems to have a jumping affect if moveUp: playerObj['y'] -= MOVERATE if moveDown: playerObj['y'] += MOVERATE for i in range(len(terrain)): tile = terrain[i] if playerObj['rect'].colliderect(tile): floorCollusion[2] = True break else: floorCollusion[2] = False if floorCollusion[2]: playerObj['x'] -= MOVERATE