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

Categories