Wall collision detection in pygame - python

I was working on a game, similar to Pong on pygame, and I had come across the problem of wall collision detection. It works with the two paddles, but does not work with the ball itself. The code makes sense, but it doesn't actually work.
import pygame, sys
from pygame.locals import *
x = 25
xc = 404
yc = 300
y = 225
x1 = 740
movey1 = 0
y1 = 225
movex = 0
movey = 0
movebx = 2
moveby = 2
WHITE = (255,255,255)
GREEN = (0,255,0)
BLUE = (0,0,128)
RANDOM = (255,0, 0)
BLACK = (0,0,0)
width = 600
pygame.init()
display = pygame.display.set_mode((800,600))
pygame.display.set_caption("Pong!")
img = pygame.image.load("pongbg.jpg")
pygame.mixer.music.load("stay.mp3")
pygame.mixer.music.play(0)
while True:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_m:
pygame.mixer.music.load("stay.mp3")
pygame.mixer.music.play(0)
if event.key == pygame.K_w:
movey = -2
pygame.display.flip()
if y <= 0 or y>=600:
print "hello"
movey = -movey
if event.key == pygame.K_s:
movey = 2
pygame.display.flip()
if y >= 600 or y <0:
print "hello"
movey = -movey
if event.key == pygame.K_UP:
movey1 = -2
if y1 <= 0 or y1> 600:
print "hello"
movey1 = -movey1
if event.key == pygame.K_DOWN:
movey1 = 1.5
if y1 <= 0 or y1> 600:
print "hello"
movey1 = -movey1
if yc < 0 or yc >= 600 or yc >= 500:
print "hello"
moveby = -moveby
if xc < 0 or xc > 800:
print "hello"
moveby = -moveby
if event.type == pygame.KEYUP:
movey1 = 0
movey = 0
if event.type == QUIT:
pygame.quit()
sys.exit()
x += movex
y += movey
y1 += movey1
xc+=movebx
yc+=moveby
#xc += movey
#yc +=movey1
pygame.display.flip()
display.blit(img, (0,0))
asdf = pygame.draw.rect(display, RANDOM, (x,y,34, 154))
ghjk = pygame.draw.rect(display,RANDOM, (x1,y1,34,154))
qwerty = pygame.draw.circle(display,GREEN, (xc,yc), 25,0)
pygame.display.flip()
pygame.display.update()
Everything else is pretty much done, I have looked around in stack overflow, but could not find a detailed answer.
Thanks,
AJ

Read up about pygame.rect and pygame.rect.colliderect
you could make the walls rects and put rects on the bumpers and detect when the bumper collides with the wall.
Sorry about the short answer.

You can use rects or you can use loads of if's statments.
you know the ball's x and the ball's y. you know the ball's width and height, and the paddles' too.
all you have to do is check if the ball's right (xc+25) is greater than the right paddle's left (x1). same with the left paddle.
with rects in pygame, you can use the function b.colliderect(a) which returns True if b and a collide.

I'm afraid you paddle detection does not work as well. My advice would be to divide your code into classes. This way the paddles will be informed of the intent of moving, but they will decide if it is possible to move. I have also noticed that you are flipping the screen inside the event loop. The event loop should not update the screen, only move the sprites around.
Here is something you can start with:
class Paddle:
def __init__(self,x,y):
self.vy = 0
self.y = y
self.x = x
def move(self):
if( self.vy == 1 and canMoveUp(self.y) or self.vy == -1 and canMoveDown(self.y):
self.y+=vy
def draw(self,screen):
screen.blit()
class Ball:
def __init__(self,x,y):
self.vx = 1
self.vy = 0
self.x = y
self.y = x
def move(self):
#check for collisions same as with paddles
def draw(self,screen):
screen.blit()

I can just say how my program with collision work:
import pygame
from pygame.locals import*
import sys
pygame.init()
screen = pygame.display.set_mode((1200, 700))
ticket1 = True
f = [0, 0]
pa = 600
pb = 650
a = 600
b = 650
color = (100, 180, 100)
while ticket1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
ticket1 = False
pygame.quit()
sys.exit()
screen.fill((255, 250, 245))
pygame.draw.circle(screen, color, (a, b), 50)
pa += f[0]
pb += f[1]
a = int(pa)
b = int(pb)
if a-50 < 0:
f[0] = -f[0]
a = 51
pa = 51
elif a+50 > 1200:
f[0] = -f[0]
a = 1149
pa = 1149
if b-50 < 0:
f[1] = -f[1]
b = 51
pb = 51
elif b+50 > 700:
f[1] = -f[1]
b = 650
pb = 650
If you think that this is bad answer this program works so maybe you have to write
if yc < 0 or yc >= 600 or yc >= 500:
print "hello"
moveby = -moveby
if xc < 0 or xc > 800:
print "hello"
moveby = -moveby
outside of:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
but I'm not sure.
For the paddles I think that answers before have good idea.

Related

Procedural Terrain generation is upside down in Pygame

import pygame
import blocks
import random
class World:
def __init__(self):
self.tile_list = []
TILESIZE = 20
minStoneHeight = 1
maxStoneHeight = 8
x = 0
y = 0
height = 10
width = 100
for x in range(width):
minHeight = height - 1
maxHeight = height + 2
height = random.randrange(minHeight, maxHeight)
minStoneSpawnDistance = height - minStoneHeight
maxStoneSpawnDistance = height - maxStoneHeight
totalStoneSpawnDistance = random.randrange(minStoneHeight, maxStoneHeight)
for y in range(height):
if y < totalStoneSpawnDistance:
t = blocks.stoneBlock(x*20, y*20 + 100)
self.tile_list.append(t)
else:
t = blocks.dirtBlock(x*20, y*20 + 100)
self.tile_list.append(t)
if(totalStoneSpawnDistance == height):
t = blocks.stoneBlock(x*20, y*20 + 100)
self.tile_list.append(t)
else:
self.tile_list.append(t)
t = blocks.stoneBlock(x*20, y*20+100)
def draw(self, screen):
for tile in self.tile_list:
tile.draw(screen)
This is the code I use for procedural terrain generation in pygame. I adapted it from a script in C# for Unity. The issue is that pygames grid orgin is in the topleft and unitys is at bottom left. I was wondering how I would get my terrain to not be upside down?
It s a matter of Indentation. You maust move the player in the application loop rather than the event loop:
running = True
while running:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
currentPlayerImg = playerImgBack
playerYSpeed = -5
if event.type == pygame.KEYUP:
if event.key == pygame.K_UP:
playerYSpeed = 0
#<--| INDENTATION
playerX += playerXSpeed
playerY += playerYSpeed
screen.blit(bgImg, (0, 0))
screen.blit(currentPlayerImg, (playerX, playerY))
pygame.display.update()

Trying to control the speed of my loop for Pong

I'm trying to get my pong game to run the loop at a certain fps, but I've tried a couple things and it hasn't worked and I wasn't taught how to use pygame speed/clock so I gotta figure it out on my own
I'm trying to make the loop run at a certain speed to make it look smoother, because if I edit the dx or the position it goes to when you change it, it looks chunky, so instead of a paddle 10 x down (looks chunky) i want to move it 1 x down a bunch of times so it moves down just as fast as 10 x but smoother
Full Code
import pygame #how to fix paddle widths from hitting ball
import sys
pygame.init()
screenSize = (800,600)
screen = pygame.display.set_mode((screenSize),0)
pygame.display.set_caption("HajarPongBasicCollision")
# colours
WHITE = (255,255,255)
BLACK = (0, 0, 0)
GREEN = (0,255,0)
RED = (255,0,0)
BLUE = (0,0,255)
PURPLE = (154, 136, 180)
screen.fill(BLACK)
pygame.display.update()
# retrieve screen measurements
screenw = screen.get_width()
screenh = screen.get_height()
# retrieve position of center of screen
centerx= 400 #tried not to use hard coded values but program gives me an error when i use screenw/2 ASK MR H TO HELP WITH NO HARD CODED VALUES (HCV)
centery= 300
# variables for first paddle
p1x = 10
p1y = 10
p1w = 10
p1h = 100
p1dy = 0
p1_score = 0
# variables for second paddle
p2w = 10
p2h = 100
p2x = screenw - 20
p2y = 10
p2dy = 0
p2_score = 0
# variable for ball
bx = 400 #HCV
by = 300 #HCV
br = 9
bdx = 1
bdy = 1
# speed of loop
fpsClock = pygame.time.Clock()
FPS = 60
go = True
while go:
fpsClock.tick(FPS)
for event in pygame.event.get():
if event.type ==pygame.QUIT:
go = False
elif event.type == pygame.KEYDOWN:
# control for the first paddle
if event.key == pygame.K_w:
p1dy = -1
elif event.key == pygame.K_s:
p1dy = 1
# controls for the second paddle
elif event.key == pygame.K_UP:
p2dy = -1
elif event.key == pygame.K_DOWN:
p2dy = 1
elif event.key == pygame.K_q:
go = False
# stops rectangles from going continously
if event.type == pygame.KEYUP:
if event.key == pygame.K_w or event.key == pygame.K_s:
p1dy = 0
if event.key == pygame.K_UP or event.key == pygame.K_DOWN:
p2dy = 0
# stops paddle one from going off the screen
if (p1y < 0):
p1dy = 0
p1y = 0
if (p1y + p1h > screenh):
p1dy = 0
p1y = screenh - p1h
# stops paddle two from going off the screen
if (p2y < 0):
p2dy = 0
p2y = 0
if (p2y + p2h > screenh):
p2dy = 0
p2y = screenh - p2h
# stops ball from going off the screen
if (bx + br >= screenw):
bx = centerx
by = centery
elif (bx <= br):
bx = centerx
by = centery
if (by + br >= screenh):
bdy = -bdy
elif (by <= br):
bdy = -bdy
# detects if ball hit paddles
if bx - br <= p1x + p1w and by >= p1y and by <= p1y + p1h:
bdx = -bdx
if bx + br >= p2x and by >= p2y and by <= p2y + p2h:
bdx = -bdx
# moves the rectangles
p1y = p1y + p1dy
p2y = p2y + p2dy
# moves the ball
bx = bx + bdx
by = by + bdy
# removes screen trail
screen.fill(BLACK)
# draws the rectangles
pygame.draw.rect(screen, WHITE,(p1x, p1y, p1w, p1h))
pygame.draw.rect(screen, WHITE,(p2x, p2y, p2w, p2h))
pygame.draw.circle(screen, WHITE, (bx, by), br, 0)
pygame.display.update()
pygame.quit()
sys.exit()
Uncouple the speed of your objects from the fps.
That way you don't need to change your fps if you want objects to move faster, you just increase the speed of the object.
and if a computer has performance issues and can't keep a reliable fps. Your game still runs at the same speed, it just shows less frames.
import time
...
# speed of objects
ball_speed = 50
paddle_speed = 200
...
last_update = time.time()
while True:
...
dt = time.time() - last_update
...
# moves the ball
bx += bdx * ball_speed * dt
...
pygame.draw.circle(screen, WHITE, (int(bx), int(by)), br, 0)
...
last_update = time.time()

How to implement jump in Pygame without sprites?

I'm new to programming and to Python as well as Pygame. As such, I'm not yet comfortable with sprites in Pygame. I'm trying to make a game where a block jumps whenever the spacebar is pressed - similar to Mario.
My code doesn't work as desired because whenever the spacebar is pressed, the block incrementally moves up (I've added a gravity component), instead of "jumping".
import pygame
pygame.init()
game_display = pygame.display.set_mode((800, 800))
# fixed variables at the start
x_pos = 400
y_pos = 400
current_speed = 15
def jump_coords(y_position, speed):
if speed >= 0:
#to move up, reduce the y-coordinate
y_position -= speed
return y_position
game_exit = False
# main loop
while not game_exit:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
y_pos = jump_coords(y_pos, current_speed)
# 1 represents gravity value
current_speed -= 1
rect_one = pygame.Rect(x_pos, y_pos, 10, 10)
pygame.draw.rect(game_display, (255, 0, 0), rect_one)
pygame.display.update()
I know that I have to somehow make y_pos keep updating in the while loop whilst speed >= 0 but I'm not sure how to implement it.
I made the minimal changes to your code to get the block to bounce:
import pygame
pygame.init()
game_display = pygame.display.set_mode((800, 800))
# fixed variables at the start
x_pos = 400
y_pos = 400
x_old = x_pos
y_old = y_pos
current_speed = 15
def jump_coords(y_position, speed):
# to move up, reduce the y-coordinate
y_position -= speed
if y_position > 400:
y_position = 400
global jump_flag
jump_flag = False
global current_speed
current_speed = 15
return y_position
game_exit = False
jump_flag = False
# main loop
while not game_exit:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
jump_flag = True
elif event.key == pygame.K_ESCAPE:
exit(0)
if jump_flag:
x_old = x_pos
y_old = y_pos
y_pos = jump_coords(y_pos, current_speed)
# 1 represents gravity value
current_speed -= 1
rect_old = pygame.Rect(x_old, y_old, 10, 10)
pygame.draw.rect(game_display, (0, 0, 0), rect_old)
rect_one = pygame.Rect(x_pos, y_pos, 10, 10)
pygame.draw.rect(game_display, (255, 0, 0), rect_one)
pygame.display.update()
The most important changes was the removal of the check for speed greater than zero. The speed has to go negative if the block is going to come back down. The next change was to save the old x and y coordinates so that we can draw a black square over the old position. I also made it possible to exit the program by pressing the Escape key.
I made this from scratch, I hope it's not too daunting!
import pygame,sys
pygame.init()
screen = pygame.display.set_mode((800, 800))
tm = 20 # Terminal Velocity
gravity = 1
class Player:
def __init__(self,speed,x,y):
self.speed = speed
self.x = x; self.y = y
self.yVelocity = 0
self.xVelocity = 0
def getKeys(self):
key = pygame.key.get_pressed()
if key[pygame.K_a]: self.xVelocity -= self.speed
if key[pygame.K_d]: self.xVelocity += self.speed
if key[pygame.K_SPACE]:
if isGround(self.x,self.y):
self.yVelocity -= 20
def move(self,dt):
if self.x < 0:
self.x = 0
if self.x > 800-15:
self.x = 800-15
if self.y < 0:
self.y = 0
if self.y > 800-10:
self.y = 800-10
self.x += self.xVelocity
self.y += self.yVelocity
if self.xVelocity != 0:
self.xVelocity /= 70*dt
if self.yVelocity < tm and not isBlocking(self.x,self.y+self.yVelocity):
self.yVelocity += gravity
if isBlocking(self.x,self.y):
self.yVelocity = 0
def draw(self):
screen.fill((255,0,0),(self.x,self.y,10,10))
def isBlocking(x,y):
if x < 0 or x > 800 or y < 0 or y > 800:
return True
elif y >= 400:
return True
else:
return False
def isGround(x,y):
if y >= 400:
return True
else:
return False
player = Player(1,400,400)
clock = pygame.time.Clock()
while True:
dt = clock.tick(60)/1000 # limit to 60 FPS.
screen.fill((0,0,0))
if pygame.event.poll().type == pygame.QUIT: pygame.quit(); sys.exit()
player.getKeys()
player.move(dt)
player.draw()
pygame.display.flip()
Hope it helps!

Why does my game crash in PYGAME every time I try to shoot

I am making a 2 player fighting game and whenever I try and press "v" the, the key to shoot my game seems to crash and not work. I am new to pygame and I am learning as I go. Any help is appreciated. Thanks!
import pygame
pygame.init()
#Sets up 8 bit colours
white = (255,255,255)
black = (0,0,0)
red = (255,0,0)
green = (0,255,0)
blue = (0,0,255)
lightblue = (180,235,255)
grassgreen =(20,200,50)
#Sets up pygame window
gameDisplay = pygame.display.set_mode((1000,600))
pygame.display.set_caption('Block Fighter')
#Variables
gameExit = False
x = 0
y = 0
x_change = 0
y_change = 0
isShooting = False
clock = pygame.time.Clock()
class Player:
def __init__(self, x_change, y_change, x, y):
self.x_change = 0
self.y_change = 0
self.x= 50
self.y= 480
def Left(self, x_change):
self.x_change = -5
def Right(self, x_change):
self.x_change =5
def Jump(self, y_change):
if (self.y == 480):
self.y_change = -70
return
p1 = Player(x_change, y_change, x, y)
class Bullet:
def __init__(self, x, y, Player, x_change):
self.x = p1.x
self.y = p1.y
self.x_change = 0
def Show(self, x_change, x , y, Player ):
pygame.draw.rect(gameDisplay, black , [p1.x,p1.y,5,5])
self.x_change = 10
b1 = Bullet(x, y, Player, x_change)
#Main Game Loop
while not gameExit:
for event in pygame.event.get():
if event.type == pygame.QUIT:
gameExit = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_a:
p1.Left(x_change)
if event.key == pygame.K_d:
p1.Right(x_change)
if event.key == pygame.K_w:
p1.Jump(y_change)
if event.key == pygame.K_v:
b1.Show(x, y, x_change, Player)
isShooting = True
while isShooting:
pygame.draw.rect(gameDisplay, black, [b1.x, b1.y , 5,5])
if b1.x > 1000:
pass
if event.type == pygame.KEYUP:
if event.key == pygame.K_a or event.key == pygame.K_d:
p1.x_change = 0
if p1.x <= 0:
p1.x = 0
if p1.x >= 500:
p1.x = 500
if p1.y > 480:
p1.y_change = 0
p1.y = 480
if p1.y < 480:
p1.y_change = 5
p1.x += p1.x_change
p1.y += p1.y_change
b1.x += b1.x_change
gameDisplay.fill(lightblue)
pygame.draw.rect(gameDisplay, grassgreen, [1000,600,-1000,-100])
pygame.draw.rect(gameDisplay, red, [p1.x,p1.y,20,20])
pygame.display.update()
clock.tick(40)
pygame.quit()
quit()
It looks like once isShooting is set to True, the program gets stuck in the while loop (while isShooting:). It seems like you need a condition in the while loop to set isShooting back to False if a certain conditions is met, for example, if the v key is released.
Just get rid of the while loop. while isShooting should be removed. The player should shoot at most one time per game loop - any more than that and they won't be able to see the effects of them firing. Plus, you create an infinite loop.
On another note, welcome to Stack Overflow! If you haven't yet, check out the tour here.

Having Trouble with Python Classes

So I am making a simple "Dodge the Meteor Game" with Python 27 and Pygame. So everything ran smoothly, until I wanted to make classes, so I could make multiple meteors without retyping the same code. After I did this, when I run it, it stops responding with no error message. Here is my code:
import pygame
from pygame.locals import *
import sys
import random
pygame.init()
width,height = 800,600
gameDisplay = pygame.display.set_mode((width,height))
pygame.display.set_caption("Fifteen Minute Game ")
gameStart = False
bg = pygame.image.load("C:\Users\DEREK\Desktop\Python\\space.jpg")
bg = pygame.transform.scale(bg,(900,600))
x = 300
y = 300
move_x = 0
move_y = 0
playerspeed = 3
pellet_x = random.randint(0,800)
pellet_y = random.randint(0,550)
player = pygame.draw.rect( gameDisplay, (255,255,255), (x,y,30,30) )
pellet = pygame.draw.rect( gameDisplay, (255,255,255), (pellet_x,pellet_y,15,15) )
count = 0
#Functions
def pelletxy():
global pellet_x, pellet_y
pellet_x = random.randint(0,770)
pellet_y = random.randint(0,570)
def collision(rect1,rect2):
global player, count, pellet
if rect1.colliderect(rect2):
if rect2 == pellet:
pelletxy()
count +=1
class Meteor():
def __init__(self):
self.meteor_x = random.randint(0,800)
self.meteor_y = 0
self.meteorfall = 3
self.meteor = pygame.draw.rect( gameDisplay, (255,255,255), (self.meteor_x,self.meteor_y,35,35) )
def collision(self,rect1,rect2):
if rect1.colliderect(rect2):
if rect2 == self.meteor:
print "Good Game"
print "MUA HAHAHAHA"
print ""
print "Your score:" + str(count)
pygame.quit()
sys.exit()
def meteorxy(self):
self.meteor_x = random.randint(0,800)
self.meteor_y = 0
def render(self):
self.meteor_y += self.meteorfall
self.meteor
if meteor_y > 600:
meteorxy()
m1 = Meteor()
#Game Loop
while gameStart:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
# Keyboard Movement
if event.type == pygame.KEYDOWN:
if event.key == K_UP:
move_y -= playerspeed
if event.key == K_DOWN:
move_y += playerspeed
if event.key == K_LEFT:
move_x -= playerspeed
if event.key == K_RIGHT:
move_x += playerspeed
if event.type == pygame.KEYUP:
if event.key == K_UP:
move_y = 0
if event.key == K_DOWN:
move_y = 0
if event.key == K_LEFT:
move_x = 0
if event.key == K_RIGHT:
move_x = 0
#Calculate new position
x = x + move_x
y = y + move_y
#Stop Movement on boundaries
if x > 830:
x = -30
elif x < -30:
x = 830
elif y < -30:
y = 630
elif y > 630:
y = -30
#Check Different Collision Scenarios
collision(player, pellet)
m1.collision(player, m1.meteor)
#Draw the things onto the screen
gameDisplay.blit(bg,(0,0))
player = pygame.draw.rect( gameDisplay, (255,255,255), (x,y,30,30) )
pellet_outline = pygame.draw.rect( gameDisplay, (255,255,255), ((pellet_x - 1), (pellet_y - 1), 17,17))
pellet = pygame.draw.rect( gameDisplay, (0,0,255), (pellet_x,pellet_y,15,15) )
m1.render
pygame.display.update()
I don't know what I'm doing wrong, but I know it is with the classes. Thanks in advance
Hobby Programmer, Derek
Well, it's probably because gameStart is always False. So you're never getting into the game loop.
You should get to know debugging. You can use pdb or any IDE like Eclipse. The important thing is that it can help you understand what code is being running.
if event.key == K_RIGHT:
move_x = 0
#Calculate new position
x = x + move_x
y = y + move_y
See how the indentation changes? In Python, indentation is very important. Because all of your code after the line 'move_x = 0' is not indented adequately, it is not part of your while loop; therefore, it does not get executed in the loop. Fix your indentation.

Categories