This question already has answers here:
How to detect collisions between two rectangular objects or images in pygame
(1 answer)
How do I detect collision in pygame?
(5 answers)
Closed 2 years ago.
I'm creating a game in pygame, but the collision isn't working between the player (a rect) and a wall (an image).
here's v190131a/__init__.py,
import os
import sys
import pygame
import time
import screeninfo
screendata = screeninfo.get_monitors()
screendata = screendata[0]
class Player(object):
def __init__(self):
self.rect = pygame.Rect(32, 32, 16, 16)
# self.offset = (screen.get_size()[0]-32, screen.get_size()[1]-32)
self.offset = (1, 1)
def move(self, dx, dy):
# Move each axis separately. Note that this checks for collisions both times.
if dx != 0:
self.move_single_axis(dx, 0)
if dy != 0:
self.move_single_axis(0, dy)
def move_single_axis(self, dx, dy):
# Move the rect
self.rect.x += dx
self.rect.y += dy
for wall in walls:
if self.rect.colliderect(wall.rect):
print(wall.rect)
if dx > 0: # Moving right; Hit the left side of the wall
self.rect.right = wall.obj.left
if dx < 0: # Moving left; Hit the right side of the wall
self.rect.left = wall.obj.right
if dy > 0: # Moving down; Hit the top side of the wall
self.rect.bottom = wall.obj.top
if dy < 0: # Moving up; Hit the bottom side of the wall
self.rect.top = wall.obj.bottom
for border in borders:
if self.rect.colliderect(border):
if dx > 0: # Moving right; Hit the left side of the wall
self.rect.right = border.left
if dx < 0: # Moving left; Hit the right side of the wall
self.rect.left = border.right
if dy > 0: # Moving down; Hit the top side of the wall
self.rect.bottom = border.top
if dy < 0: # Moving up; Hit the bottom side of the wall
self.rect.top = border.bottom
class Wall(object):
def __init__(self, pos):
walls.append(self)
self.obj = assets["whitebrick"]
self.rect = self.obj.get_rect()
self.pos = pos
borders = [pygame.Rect(0, 0, screendata.width, 16),
pygame.Rect(0, 16, 16, screendata.height),
pygame.Rect(0, screendata.height-64, screendata.width, 16),
pygame.Rect(screendata.width-16, 0, 16, screendata.height)
]
walls = []
screen = None
clock = None
player = None
assets = None
def assetgrab():
return {
"whitebrick": pygame.image.load("v190131a/assets/img/whitebrick.png").convert_alpha()
}
def init():
global screen, clock, player, assets
os.environ["SDL_VIDEO_CENTERED"] = "1"
pygame.init()
# Set up the display
pygame.display.set_caption("Get to the red square!")
screen = pygame.display.set_mode((0, 0), pygame.RESIZABLE)
clock = pygame.time.Clock()
player = Player() # Create the player
assets = assetgrab()
Wall((32, 256))
def event(evnt):
if evnt.type == pygame.QUIT:
sys.exit()
if evnt.type == pygame.KEYDOWN and evnt.key == pygame.K_ESCAPE:
sys.exit()
def mainloop():
global borders
# Move the player if an arrow key is pressed
key = pygame.key.get_pressed()
if key[pygame.K_LEFT]:
player.move(-5, 0)
if key[pygame.K_RIGHT]:
player.move(5, 0)
if key[pygame.K_UP]:
player.move(0, -5)
if key[pygame.K_DOWN]:
player.move(0, 5)
screen.get_size()
borders = [pygame.Rect(0, 0, screen.get_size()[0], 16),
pygame.Rect(0, 16, 16, screen.get_size()[1]),
pygame.Rect(0, screen.get_size()[1]-64, screen.get_size()[0], 16),
pygame.Rect(screen.get_size()[0]-16, 0, 16, screen.get_size()[1])
]
player.move(0, 5)
# Draw the scene
screen.fill((0, 0, 0))
pygame.draw.rect(screen, (255, 200, 0), player.rect)
for wall in walls:
screen.blit(wall.obj, wall.pos)
[pygame.draw.rect(screen, (0, 0, 0), x) for x in borders]
pygame.display.flip()
def end():
time.sleep(0.0166666667)
pass
Which then gets ran by core.py:
import pygame
import v190131a as core
core.init()
while True:
for event in pygame.event.get():
core.event(event)
core.mainloop()
core.end()
The player is supposed to land on the wall and stop, but instead, it passes right through. Is there something I'm missing; do I need to change the player to an image, is it an obvious logic error?
Help would be appreciated, thanks.
Related
What am trying to do is to create an Arkanoid game, where the bricks have 3 points of strength each and then they die. The issue is that instead of just the particular brick that gets hit, to lose the points, the whole brick_sprite group is loosing the points. And when one suppose to die, all the previous within the list up to that one dies. I think the issue is that it loops considering the update on line #240. Please check line 65 at def collision(self): under Brick Class. The issue is somewhere there.
"""This is a simple version of arkanoid game"""
import sys
import pygame
import random
# Set colors R G B
white = (255, 255, 255)
black = (0, 0, 0)
orange = (255, 100, 10)
light_blue = (0, 144, 255)
shadow = (192, 192, 192)
purple = (152, 0, 152)
# Display
display_height = 999
display_width = 444
pygame.display.set_caption = ("Arkanoid 1.0")
FPS = 60
# Movement speed
speed = display_width // 60
# Movements
left = (-speed, 0)
right = (speed, 0)
up = (0, speed)
diagonal_left = [-speed, -speed]
diagonal_right = [speed, -speed]
# Game objects dimentions
base_dimentions = (display_width // 5, display_height // 100)
[brick_width, brick_height] = [display_width // 20 * 2, display_height // 100]
brick_dimentions = [brick_width, brick_height]
ball_dimentions = (display_height // 100, display_height // 100)
# Initializing text font
pygame.font.init()
txt_font = pygame.font.SysFont("Score: ", display_height//44)
# Initializing sprite lists
all_sprites = pygame.sprite.Group()
brick_sprites = pygame.sprite.Group()
class Brick(pygame.sprite.Sprite):
def __init__(self, point_value, center):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface(brick_dimentions)
self.image.fill(purple)
self.rect = self.image.get_rect()
self.rect.center = center
self.point_value = point_value
def update(self):
self.collision()
def collision1(self): #This works no issue.
# If brick is hit, loses a point
collision = pygame.sprite.spritecollide(ball, brick_sprites, True)
return collision
def collision(self): #Here is the issue.
# If brick is hit, loses a point
collision = pygame.sprite.spritecollide(ball, brick_sprites, False)
if collision:
self.point_value -= 1
if self.point_value == 0:
self.kill() ## BUGGISH ##"""
class Ball(pygame.sprite.Sprite):
"""Initiates a moving ball and its' attributes"""
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface(ball_dimentions)
self.image.fill(light_blue)
self.rect = self.image.get_rect()
self.rect.center = self.init_position()
self.direction = random.choice([diagonal_left, diagonal_right])
self.score = 0
def update(self):
self.movement()
def init_position(self):
# Initialize position of the ball
init_position = (board.rect.center[0],
(board.rect.center[1] - (base_dimentions[1] / 2)
- (ball_dimentions[1] / 2)))
return init_position
def collision(self):
# If hit bricks
collision = pygame.sprite.spritecollideany(ball, brick_sprites)
if collision:
self.direction[1] *= -1
self.score += 1
enter code here
def movement(self):
self.containment()
self.rect[1] += self.direction[1]
self.rect[0] += self.direction[0]
self.deflect()
self.ball_loss()
self.collision()
def containment(self):
if self.rect.right >= display_width or self.rect.left <= 0:
self.direction[0] *= -1
if self.rect.top <= 0:
self.direction[1] *= -1
def ball_loss(self):
if self.rect.bottom >= display_height:
self.reset()
bricks_reset()
def reset(self):
self.rect.center = self.init_position()
self.direction[1] *= -1
self.score = 0
def deflect(self):
# If hit base_board, deflect
if (self.rect.bottom == board.rect.top and
(board.rect.left <= self.rect.left <= board.rect.right or
board.rect.left <= self.rect.right <= board.rect.right)):
self.direction[1] *= -1
self.board_ball_interaction()
def board_ball_interaction(self):
# When board is moving, effects balls direction/speed
keystate = pygame.key.get_pressed()
if keystate[pygame.K_LEFT] and board.rect.left > 0:
self.direction[0] -= speed // 2
elif keystate[pygame.K_RIGHT] and board.rect.right < display_width:
self.direction[0] += speed // 2
class Base_board(pygame.sprite.Sprite):
"""Initiates base_board class and it's attributes"""
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface(base_dimentions)
self.image.fill(orange)
self.rect = self.image.get_rect()
self.rect.center = (display_width // 2,
display_height - 2 * base_dimentions[1])
self.x_direction = 0
def update(self):
# Up-dates classes' position according to user's imput
self.x_direction = 0
self.movement()
self.rect.x += self.x_direction
def movement(self):
# Creates movement and constrains object within screen dimentions
keystate = pygame.key.get_pressed()
if keystate[pygame.K_LEFT]:
if not self.rect.left <= 0:
self.x_direction = -speed
elif keystate[pygame.K_RIGHT]:
if not self.rect.right >= display_width:
self.x_direction = speed
def shoot(self):
pass
def enlogate(self):
pass
def control():
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
# and adding all sprites on lists
board = Base_board()
ball = Ball()
all_sprites.add(board)
all_sprites.add(ball)
def bricks_list_creator():
# Creates and adds bricks into a list
i = 9
point_value = 2 ####
coordinates = [display_width // 20 + brick_width / 6, display_height // 20]
while i > 0:
brick = Brick(point_value, (coordinates)) ####
coordinates[0] += brick_width * 1.1
brick_sprites.add(brick)
i -= 1
return brick_sprites
def bricks_reset():
# Reset brick list
brick_sprites.empty()
bricks_list_creator()
return brick_sprites
def render_text(screen):
text = txt_font.render("Score: {0}".format(ball.score), 1, (0, 0, 0))
return screen.blit(text, (5, 10))
def render_main(screen):
all_sprites.draw(screen)
brick_sprites.draw(screen)
render_text(screen)
# Game main
def main():
pygame.init()
clock = pygame.time.Clock()
screen = pygame.display.set_mode((display_width, display_height))
bricks_list_creator()
while True:
# Events
clock.tick(FPS)
control()
# Update
brick_sprites.update()
all_sprites.update()
# Render
screen.fill(shadow)
render_main(screen)
pygame.display.flip()
pygame.display.update()
main()
I think the issue is in the update() of your Brick class calling the collision.
The sprite update function is typically used for changing the position or look of your sprite, and is called every frame. So it's not a good place to check for collisions.
A Brick only needs to know its point_value, it doesn't move (AFAIK).
class Brick(pygame.sprite.Sprite):
def __init__(self, point_value, center):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface(brick_dimentions)
self.image.fill(purple)
self.rect = self.image.get_rect()
self.rect.center = center
self.point_value = point_value
def takeHit( self, ball_sprite ):
# the ball has collided with *this* brick
self.point_value -= 1
if self.point_value == 0:
self.kill()
Then in Ball.collision() use the pygame.sprite.spritecollide() to get the list of Bricks the Ball has collided with, and reduce their hit points:
class Ball:
# [...]
def collision(self):
# calculate the list of bricks hit
hit_list = pygame.sprite.spritecollide( self, brick_sprites, False )
for brick in hit_list:
brick.takeHit() # may (or may not) kill the brick
Most of the time the hit_list is going to be a single Brick, but depending on the size of the ball, perhaps occasionally it's two bricks.
I am creating a platform game using pygame, in my game I have simulated gravity and platforms that can be jumped on but I'm having trouble getting the collision detection to work correctly. By collision detection I mean when my character sprite jumps I want him to bounce off of the bottom and sides of the platform that will be above him. Now my character sprite jumps through the platform and lands on top of it.
My code is as follows:
My main class:
import pygame
import random
from settings import *
from sprites import *
from camera import *
from os import path
class Game:
def __init__(self):
pygame.init() # initialises pygame
pygame.mixer.init()
self.screen = pygame.display.set_mode((WIDTH, HEIGHT)) # sets the width and height of the pygame window
pygame.display.set_caption(TITLE)
self.clock = pygame.time.Clock()
self.running = True
self.font_name = pygame.font.match_font(FONT_NAME)
self.load_data()
def load_data(self):
pass
def new(self):
self.all_sprites = pygame.sprite.Group()
self.platforms = pygame.sprite.Group()
self.player = Player(self)
self.all_sprites.add(self.player)
for plat in PLATFORM_LIST:
p = Platform(*plat)
self.all_sprites.add(p)
self.platforms.add(p)
self.camera = Camera(WIDTH, HEIGHT) # creates the camera with WIDTH and HEIGHT of the screen
self.run()
def run(self): # Game Loop - runs the game
self.playing = True
while self.playing:
self.clock.tick(FPS)
self.events()
self.update()
self.draw()
def update(self): # Game loop - update
self.all_sprites.update()
# collision with a platform
if self.player.vel.y > 0:
hits = pygame.sprite.spritecollide(self.player, self.platforms, False) # returns a list of platform sprites that hit the player
if hits:
self.player.pos.y = hits[0].rect.top
self.player.vel.y = 0
# screen moves with player
self.camera.update(self.player) # is the camera that tracks players movement
def events(self): # Game loop - events
for event in pygame.event.get():
if event.type == pygame.QUIT:
if self.playing:
self.playing = False
self.running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
self.player.jump()
def draw(self): # Game loop - draw
self.screen.fill(RED)
#self.all_sprites.draw(self.screen)
for sprite in self.all_sprites:
self.screen.blit(sprite.image, self.camera.apply(sprite)) # loops through the all_sprites group and blit's each sprite onto the screen
pygame.display.flip()
def start_screen(self):
pass
def game_over_screen(self):
pass
def wait_for_key(self):
pass
def draw_text(self,text, size, colour, x, y):
pass
g = Game()
g.start_screen()
while g.running:
g.new()
g.game_over_screen()
pygame.quit()
my sprite classes:
# will hold the sprite classes
import pygame
from settings import *
import random
vec = pygame.math.Vector2
class Player(pygame.sprite.Sprite):
def __init__(self, game):
pygame.sprite.Sprite.__init__(self)
self.game = game
self.image = pygame.Surface((30, 40))
self.image.fill(BLUE)
self.rect = self.image.get_rect()
self.rect.center = (WIDTH / 2, HEIGHT / 2)
self.pos = vec(WIDTH / 2, HEIGHT / 2)
self.vel = vec(0, 0)
self.acc = vec(0, 0)
def jump(self):
# jump only if on a platform
self.rect.x += 1
hits = pygame.sprite.spritecollide(self, self.game.platforms, False)
self.rect.x -= 1
if hits:
self.vel.y = -20
def update(self):
self.acc = vec(0, PLAYER_GRAV)
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
self.acc.x = -PLAYER_ACC
if keys[pygame.K_RIGHT]:
self.acc.x = PLAYER_ACC
# apply friction
self.acc.x += self.vel.x * PLAYER_FRICTION
# equations of motion
self.vel += self.acc
self.pos += self.vel + 0.5 * self.acc
# stop from running of the left side of the screen
if self.pos.x < 0:
self.pos.x = 0
self.rect.midbottom = self.pos
class Platform(pygame.sprite.Sprite):
def __init__(self, x, y, width, height):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((width, height))
self.image.fill(GREEN)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
camera class
import pygame
from settings import *
# A camera that keeps track of an offset that will be, how far we want to draw the screen which will include all objects on the screen. We are just shifting the drawing of our screen according to the offset. Camera needs to do two things, apply the offset and then update the movement of where the player is on the screen.
class Camera:
def __init__(self, width, height): # we will need to tell the camera how wide and high we want it to be
self.camera = pygame.Rect(0, 0, width, height) # is the rectangle we set to keep track of the screen/be the camera
self.width = width
self.height = height
def apply(self, entity): # method to apply the offset to the screen, by shifting the screen according to the movement of the entity within the camera screen
return entity.rect.move(self.camera.topleft)
def update(self, target): # method to update where the player/target has moved to, updates are done according to last known position of the target
# as the target moves the camera moves in the opposite direction of the target and stays within the center of the screen
x = -target.rect.x + int(WIDTH/2) # left to right
y = -target.rect.y + int(HEIGHT/2) # up and down
# limit scrolling to map size, keeps the 'camera' from going over the edges
x = min(0, x) # left
y = min(0, y) # top
y = max(-(self.height - HEIGHT), y) # bottom
self.camera = pygame.Rect(x, y, self.width, self.height) # adjusts the camera's rectangle with the new x and y
settings module:
# Game options/settings
TITLE = 'Platformer'
WIDTH = 900
HEIGHT = 500
FPS = 60
FONT_NAME = 'arial'
HS_FILE = 'highscore.txt'
SPRITESHEET = 'spritesheet_jumper.png'
# Game colours
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
# Starting Platforms:
PLATFORM_LIST = [(0, HEIGHT - 50, WIDTH, 50), (WIDTH / 2, HEIGHT * 1 / 2, 200, 30), (WIDTH + 150, HEIGHT - 50, WIDTH, 50)]
# player properties
PLAYER_ACC = 0.5
PLAYER_FRICTION = -0.12
PLAYER_GRAV = 0.8
P.S. added all my code for anyone that may need to see it.
Your question says "when you have simulated gravity" but i don't understand how that would affect the bouncing. The answer below should work with your applied gravity so i don't think gravity will be an issue. So first, to bounce, we will need to know which side of the player is colliding with a particular side of the platform. For this the following functions can be used.
#Checks if right side of the player is colliding with left side of platform
def rightCheck(rect1, rect2):
if rect1.x + rect1.width > rect2.x:
if (rect1.y > rect2.y and rect1.y < rect2.y + rect2.height) or (rect1.y + rect1.height < rect2.y + rect2.height and rect1.y + rect1.height> rect2.y):
if rect1.x < rect2.x:
return True
return False
#Checks if bottom side of the player is colliding with top side of platform
def botCheck(rect1, rect2):
if rect1.y + rect1.height > rect2.y:
if (rect1.x > rect2.x and rect1.x < rect2.x + rect2.width) or (rect1.x + rect1.width > rect2.x and rect1.x + rect1.width < rect2.x + rect2.width):
if rect1.y < rect2.y:
return True
return False
# NOTICE they take pygame.Rect as arguments
For your game you are probably going to need one for left of the player and right side of the platform well, but i don't have it since i copied these from my game. I am sure you can write one yourself :). So, moving on. The code below shows how right side of the player colliding with left side of the platform can be implemented. So you can do this with all the sides. Also, bounce function is probably the only one you are interested in, others i has to put there to make it work.
import pygame
win = pygame.display
D = win.set_mode((1200, 600))
def rightCheck(rect1, rect2):
if rect1.x + rect1.width > rect2.x:
if (rect1.y > rect2.y and rect1.y < rect2.y + rect2.height) or (rect1.y + rect1.height < rect2.y + rect2.height and rect1.y + rect1.height> rect2.y):
if rect1.x < rect2.x:
return True
return False
class Player:
def __init__(self, ):
self.pos = [10, 10]
self.surf = pygame.Surface((50, 50)).convert()
self.energy = 10 #how much the player bounces to the left by
self.trigger = False #Triggers the left push if it is true
self.rightCheck = rightCheck # this is the function to check collision
self.rvel = 0.5 # This is the movement speed
def move(self):
key = pygame.key.get_pressed()
if key[pygame.K_RIGHT]:
self.pos[0] += self.rvel
if key[pygame.K_LEFT]:
self.pos[0] -= 0.5
if key[pygame.K_UP]:
self.pos[1] -= 0.5
if key[pygame.K_DOWN]:
self.pos[1] += 0.5
def draw(self):
D.blit(self.surf, (self.pos[0], self.pos[1]))
def bounce(self, platRect):
selfRect = pygame.Rect(self.pos[0], self.pos[1], 50, 50)
if self.rightCheck(selfRect, platRect):
self.trigger = True
self.rvel = 0
else:
self.rvel = 0.5
if self.trigger:
self.pos[0] -= self.energy*0.1
self.rvel = 0.1
self.energy -= 0.05
if self.energy <= 0:
self.trigger = False
else:
self.energy = 10
p = Player()
while True:
pygame.event.get()
D.fill((255, 255, 255))
platformRect = pygame.Rect(300, 150, 600, 200)
pygame.draw.rect(D, (0, 0, 0), platformRect)
p.bounce(platformRect)
p.move()
p.draw()
p.bounce(platformRect)
win.flip()
It basically just checks if they are colliding and if they are pushes the player a little to the left.
I am making a networked game that involves generating random blocks. Players should not be able to move inside the blocks so I created a collision detection function to stop a player moving inside a block. The function is able to stop a player moving into a block from the left and the top but players can still move in from the right and bottom. I also have one more problem that ever since I generated the block the player moves extremely slow.
This is the code for the Player class:
class player:
def __init__(self, x, y, width, height, colour):
self.width = width # dimensions of player
self.height = height # dimensions of player
self.x = x # position on the screen
self.y = y # position on the screen
self.colour = colour # players colour
self.rect = (x, y, width, height) # all the players properties in one
self.speed = 2 # how far/fast you move with each key press
self.direction = ""
def draw(self, win):
pygame.draw.rect(win, self.colour, self.rect)
def move(self, platforms):
keys = pygame.key.get_pressed() # dictionary of keys - values of 0/1
if keys[pygame.K_LEFT]: # move left: minus from x position value
if self.x <= 5:
pass
else:
self.x -= self.speed
self.direction = "Left"
elif keys[pygame.K_RIGHT]: # move right: add to x position value
if self.x == 785:
pass
else:
self.x += self.speed
self.direction = "right"
elif keys[pygame.K_UP]: # move up: minus from y position value
if self.y <= 5:
pass
else:
self.y -= self.speed
self.direction = "up"
elif keys[pygame.K_DOWN]: # move down from
if self.y >= 785:
pass
else:
self.y += self.speed
self.direction = "down"
def update(self, platforms, direction):
self.collision_with_platform(platforms, direction)
self.rect = (self.x, self.y, self.width, self.height) # redefine where the player is
self.update(platforms, self.direction)
This is the collision detector:
def collision_with_platform(self, platforms, direction):
# evaluates if the player is inside a platform, moving the player to it's edge if so
for platform in platforms:
# checks if player's coordinates are inside the platform, for each platform
if platform.rect.left - 20 < self.x < platform.rect.left + 50:
if platform.rect.top - 20 < self.y < platform.rect.top + 50:
# if moving horizontally
if direction == "left" or direction == "right": # evaluates which side of the platform to move the player to
if direction == "left":
self.x = platform.rect.left + 10 # move to the right side
if direction == "right":
self.x = platform.rect.left - 10 # move to the left side
# if moving vertically
elif direction == "down" or direction == "up": # evaluates which side of the platform to move the player to
if direction == "down":
self.y = platform.rect.top - 10 # move to the top side
elif direction == "up":
self.y_pos = platform.rect.top + 50 # move to the bottom side
This is how I generate platforms:
class Platform:
# represents a platform which the player can stand on
def __init__(self, left, top, width, height, colour):
self.rect = pygame.Rect(left, top, width, height) # square representing a single platform
self.colour = colour # platform's colour
def generate_platforms(probability):
# generates platforms randomly
points, platforms = [], []
# generates a list of all possible platform coordinates
# format of points : [(0,0), (0,1), (0,2), ..., (1,0), (1,1), (1,2), ...]
for x in range(0, 600):
for y in range(0, 600):
points.append((x, y))
# randomly selects platforms to create out of list of all possible platforms
for point in points:
if random.randint(1, 100) <= probability: # probability of each individual platform being created
platforms.append(Platform(point[0] * 50, point[1] * 50, 50, 50, (0, 0, 225)))
return platforms # returns the list of generated platforms
This is the main game loop:
def main(): # asking server for updates, checking for events
run = True
n = Network()
startPos = read_pos(n.getPos())
p = player(startPos[0], startPos[1], 10, 10, (255, 0, 0)) # connect, get starting position
p2 = player(0, 0, 10, 10, (0, 0, 255))
platforms = generate_platforms(25)
clock = pygame.time.Clock()
while run:
clock.tick(60)
p2Pos = read_pos(n.send(make_pos((p.x, p.y))))
p2.x = p2Pos[0]
p2.y = p2Pos[1]
p2.update(platforms, p.direction)
for event in pygame.event.get():
if event.type == pygame.QUIT: # quitting condition
run = False
pygame.quit()
p.move(platforms) # move character based off what keys are being pressed
redrawWindow(win, p, p2, platforms)
The problem is you have way more platforms than you think, there is a 25% chance to have a platform on every pixel that is 50 x 50. you have a lot of overlapping platforms. I ran it and got a length of 62516 platforms. I think what you want it
for x in range(0, screen_Width//50):
for y in range(0, screen_Height//50):
if random.randint(1, 100) <= probability: # probability of each individual platform being created
platforms.append(Platform(x * 50, y * 50, 50, 50, (0, 0, 225)))
This only creates platforms every 50 pixels, so none overlap. Running this i got a length of 22, a lot lower and still looks about the same. This will speed up your program considerably. I was getting 1.5 fps but now maxed out fps at 60.
To fix your collisions.
Change your player rect to a pygame rect object
self.rect = pygame.Rect(x, y, width, height) # all the players properties in one
and change the rect when you move
if keys[pygame.K_LEFT]: # move left: minus from x position value
if self.x > 5:
self.x -= self.speed
self.rect.x -= self.speed
self.direction = "left" # lowercase l as everything else was lowercase
then for the collisions:
if self.rect.colliderect(platform.rect):
if direction == "left":
self.rect.left = platform.rect.right # move to the right side
elif direction == "right":
self.rect.right = platform.rect.left # move to the left side
# if moving vertically
elif direction == "down":
self.rect.bottom = platform.rect.top # move to the top side
elif direction == "up":
self.rect.top = platform.rect.bottom # move to the bottom side
also In player.update you dont need to call it again inside it. So just this will do
def update(self, platforms):
self.collision_with_platform(platforms, self.direction)
I'm building a game with pygame and I have a problem with this game, I don't know how to make the background and the platform scroll slowly to the right as my player sprite moves to the right.
I want the scrolling to happen in def shift_world().
Maybe someone can teach me how to add an image to the background as well. It would be great if you can utilize the libraries I am using. I have three files: The first for the game, the second for sprites, and the third for the settings.
main.py:
# Sarada's Blaze! - side scrolling platform shooting game
import pygame as pg
import random
import os
from settings import *
from sprites import *
class Game:
def __init__(self):
# initialize game window, etc
pg.init()
pg.mixer.init()
self.screen = pg.display.set_mode((WIDTH, HEIGHT))
pg.display.set_caption(TITLE)
self.clock = pg.time.Clock()
self.running = True
self.font_name = pg.font.match_font(FONT_NAME)
def new(self):
# start a new game
self.score = 0
self.all_sprites = pg.sprite.Group()
self.platforms = pg.sprite.Group()
self.player = Player(self)
self.all_sprites.add(self.player)
for plat in PLATFORM_LIST:
p = Platform(*plat)
self.all_sprites.add(p)
self.platforms.add(p)
self.run()
def run(self):
# Game loop
self.playing = True
while self.playing:
self.clock.tick(FPS)
self.events()
self.update()
self.draw()
def update(self):
# Game Loop - Update
self.all_sprites.update()
self.platforms.update()
# check if player hits a platform - only if falling
if self.player.vel.y 0:
hits = pg.sprite.spritecollide(self.player, self.platforms, False)
if hits:
self.player.pos.y = hits[0].rect.top
self.player.vel.y = 0
def events(self):
# Game Loop - events
for event in pg.event.get():
# check for closing window
if event.type == pg.QUIT:
if self.playing:
self.playing = False
self.running = False
if event.type == pg.KEYDOWN:
if event.key == pg.K_UP:
self.player.jump()
def draw(self):
# Game Loop - draw
self.screen.fill(bgcolor) # I want to change the background color to an image
self.all_sprites.draw(self.screen)
self.draw_text(str(self.score), 22, white, WIDTH / 2, 15)
# after drawing everything, flip the display
pg.display.flip()
# How far the world has been scrolled right
# This is where i want the side scroller stuff to happen
def shift_world(self):
# when the user moves left/right, i want to scroll everything
self.world_shift += shift_x
# i want all my platforms and the background to scroll when
# my player sprite moves closer to the right
for plat in PLATFORM_LIST:
self.platform.rect.x += shift_x
if self.pos.x = 500:
diff = self.pos.x - 500
self.pos.x = 500
self.shift_world(- diff)
if self.pos.x <= 120:
diff = 120 - self.pos.x
self.pos.x -= 120
self.shift_world(diff)
def show_start_screen(self):
# game splash/start screen
self.screen.fill(greenblue)
self.draw_text(TITLE, 48, red, WIDTH / 2, HEIGHT / 4)
self.draw_text("left arrow to move left, right arrow to move right, up arrow to jump", 22, blue, WIDTH / 2, HEIGHT / 2)
self.draw_text("Press any key to begin", 22, green, WIDTH / 2, HEIGHT * 3 /4)
pg.display.flip()
self.wait_for_key()
def show_go_screen(self):
# game over/continue
if not self.running:
return
self.screen.fill(greenblue)
self.draw_text("Game Over!", 48, red, WIDTH / 2, HEIGHT / 4)
self.draw_text("Score: " + str(self.score), 22, blue, WIDTH / 2, HEIGHT / 2)
self.draw_text("Press any key to continue", 22, green, WIDTH / 2, HEIGHT * 3 /4)
pg.display.flip()
self.wait_for_key()
def wait_for_key(self):
waiting = True
while waiting:
self.clock.tick(FPS)
for event in pg.event.get():
if event.type == pg.QUIT:
waiting = False
self.running = False
if event.type == pg.KEYUP:
waiting = False
def draw_text(self, text, size, color, x, y):
font = pg.font.Font(self.font_name, size)
text_surface = font.render(text, True, color)
text_rect = text_surface.get_rect()
text_rect.midtop = (x, y)
self.screen.blit(text_surface, text_rect)
g = Game()
g.show_start_screen()
while g.running:
g.new()
g.show_go_screen()
pg.quit()
sprites.py:
# Sprite classes for platform shooting game
import pygame as pg
import random
from settings import *
import os
vec = pg.math.Vector2
game_folder = os.path.dirname(__file__)
img_folder = os.path.join(game_folder, "img")
class Player(pg.sprite.Sprite):
def __init__(self, game):
pg.sprite.Sprite.__init__(self)
self.game = game
self.image = pg.image.load(os.path.join(img_folder, "sarada_shooting.png")).convert()
self.image.set_colorkey(black)
self.rect = self.image.get_rect()
self.rect.center = (WIDTH / 2, HEIGHT / 2)
self.pos = vec(WIDTH / 2, HEIGHT / 2)
self.vel =vec(0, 0)
self.acc = vec(0, 0)
def jump(self):
# jump only if standing on a platform
self.rect.x += 1
hits = pg.sprite.spritecollide(self, self.game.platforms, False)
self.rect.x -= 1
if hits:
self.vel.y = -PLAYER_JUMP
def update(self):
self.acc = vec(0, PLAYER_GRAV)
keys = pg.key.get_pressed()
if keys[pg.K_LEFT]:
self.acc.x = -PLAYER_ACC
if keys[pg.K_RIGHT]:
self.acc.x = PLAYER_ACC
# apply friction
self.acc.x += self.vel.x * PLAYER_FRICTION
# equations of motion
self.vel += self.acc
self.pos += self.vel + 0.5 * self.acc
# wrap around the sides of the screen
if self.pos.x WIDTH:
self.pos.x = 0
if self.pos.x < 0:
self.pos.x = WIDTH
self.rect.midbottom = self.pos
# i want the platform graphic changed
class Platform(pg.sprite.Sprite):
def __init__(self, x, y, w, h):
pg.sprite.Sprite.__init__(self)
self.image = pg.Surface((WIDTH, h))
self.image.fill(greenblue)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
settings.py:
# game options/settings
TITLE = "Sarada's Blaze"
WIDTH = 800
HEIGHT = 600
FPS = 60
FONT_NAME = 'times new roman'
# Player properties
PLAYER_ACC = 0.5
PLAYER_FRICTION = -0.20
PLAYER_GRAV = 0.8
PLAYER_JUMP = 20
# starting platforms
PLATFORM_LIST = [(0, HEIGHT - 40, WIDTH, 40)]
# define colors
white = (255, 255, 255)
black = (0, 0, 0)
red = (255, 0, 0)
green = (0, 255, 0)
blue = (0, 0, 255)
purple = (255, 0, 255) # ENEMY COLOR
greenblue = (0, 155, 155)
bgcolor = red
You need to iterate over the self.platforms list and subtract the player's vel.x from the x-position of every sprite in order to move them. Also, give your platform sprites a vector attribute for the position self.pos = vec(x, y) because the coordinates of pygame.Rects are truncated and turned into ints, so the movement will be inaccurate if your velocity vectors consist of floats.
def update(self):
# ...
self.shift_world()
def shift_world(self):
# Scroll when the player sprite moves closer to the right.
if self.player.pos.x >= 500:
self.player.pos.x = 500 # Stop at 500.
self.shift_platforms()
# Scroll when the player sprite moves closer to the left.
if self.player.pos.x <= 120:
self.player.pos.x = 120 # Stop at 120.
self.shift_platforms()
def shift_platforms(self):
for plat in self.platforms: # Iterate over the platform sprites.
plat.pos.x -= self.player.vel.x # Update the platform's pos vector.
plat.rect.x = plat.pos.x # Update the rect.
self.world_shift -= self.player.vel.x # For the background.
As for the background surface, you can use the self.world_shift attribute and subtract the self.player.vel.x in the shift_platforms method as well. Then blit it twice in the draw method and use the modulo operator to blit it at the correct x-positions.
I assume the background has the size of the screen and should be repeated.
# Load it once in the global scope.
BACKGROUND = pg.image.load(os.path.join(img_folder, "background.png")).convert()
def draw(self):
x = self.world_shift
self.screen.blit(BACKGROUND, (x % WIDTH, 0))
self.screen.blit(BACKGROUND, (x % WIDTH - WIDTH - 1, 0))
Can anyone show me how to add a rectangle wherever i click somewhere and make the ball bounce off it for example when i click somewhere on the screen it place a rectangle at that coordinate and when they make the ball collide with it the ball will bounce.
Also my is their a way of making it HD?
import sys
import pygame
import os
image_file = os.path.expanduser("ball.png")
delta = {
pygame.K_LEFT: (-20, 0),
pygame.K_RIGHT: (+20, 0),
pygame.K_UP: (0, -20),
pygame.K_DOWN: (0, +20),
}
gravity = +1
class Ball(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.speed = [0, 0]
area = pygame.display.get_surface().get_rect()
self.width, self.height = area.width, area.height
def update(self):
self.rect = self.rect.move(self.speed)
if self.rect.left < 0 or self.rect.right > self.width:
self.speed[0] = -self.speed[0]
if self.rect.top < 0 or self.rect.bottom > self.height:
self.speed[1] = -self.speed[1]
self.rect.left = clip(self.rect.left, 0, self.width)
self.rect.right = clip(self.rect.right, 0, self.width)
self.rect.top = clip(self.rect.top, 0, self.height)
self.rect.bottom = clip(self.rect.bottom, 0, self.height)
def clip(val, minval, maxval):
return min(max(val, minval), maxval)
class Main(object):
def __init__(self):
self.setup()
def setup(self):
pygame.init()
size = (self.width, self.height) = (640,360)
self.screen = pygame.display.set_mode((1000,1000),pygame.FULLSCREEN)
self.ball = Ball()
self.setup_background()
def setup_background(self):
self.background = pygame.Surface(self.screen.get_size())
self.background = self.background.convert()
self.background.fill((0, 0, 0))
self.screen.blit(self.background, (0, 0))
pygame.display.flip()
def draw(self):
self.screen.blit(self.background, (0, 0))
self.screen.blit(self.ball.image, self.ball.rect)
pygame.display.flip()
def event_loop(self):
ball = self.ball
friction = 1
while True:
for event in pygame.event.get():
if ((event.type == pygame.QUIT) or
(event.type == pygame.KEYDOWN and
event.key == pygame.K_ESCAPE)):
sys.exit()
elif event.type == pygame.KEYDOWN:
deltax, deltay = delta.get(event.key, (0, 0))
ball.speed[0] += deltax
ball.speed[1] += deltay
friction = 1
elif event.type == pygame.KEYUP:
friction = 0.99
ball.speed = [friction*s for s in ball.speed]
ball.speed[1] += gravity
ball.update()
self.draw()
pygame.time.delay(10)
if __name__ == '__main__':
app = Main()
app.event_loop()
You have to be able to perform several steps:
Detect when the mouse changes from unpressed to pressed state.
If that happens:
Add a Rectange object to a list (consists of x, y, width and height).
In each loop:
For each Rectange in the list: Check if it intersects with the ball. You can model the ball as a rectangle, which is a little bit easier, or as a circle. A circle touches a rectangle if a corner of the rectange is inside of it or if the center is below/above/beneath the rectangle and less than its radius away. (It's easier to explain with a picture.)
If the ball collides: Change the speed as if it had touched the corner of the screen.
Do you have further problems regarding one of these steps?
"Also my is their a way of making it HD?"
I was able to change the resolution to 1600 x 900. Each graphics card only supports a certain set of fullscreen resolutions and 1000x1000 didn't work for mine.