This question already has an answer here:
Sometimes the ball doesn't bounce off the paddle in pong game
(1 answer)
Closed 2 years ago.
I am recreating a basic pong game.. and I am unable to figure out how to make the ball more faster. I have tried modifying speed but that was unsuccessful. Also, when the ball hits the paddle it goes through the paddle instead of bouncing back. I am trying to use collision to allow the ball to bounce back.
# import the necessary modules
import pygame
import sys
#initialize pygame
pygame.init()
screenSize=(700,500)
screen=pygame.display.set_mode((screenSize), 0)
# set the size for the surface (screen)
screen_h = screen.get_height()
screen_w = screen.get_width()
screen = pygame.display.set_mode((screen_w,screen_h),0)
# set the caption for the screen
pygame.display.set_caption("Pong Game")
# define colours you will be using
WHITE = (255,255,255)
GREEN = (0,255,0)
RED = (255,0,0)
BLUE = (0,0,255)
BLACK = (0,0,0)
YELLOW = (255,255,0)
playerRect = pygame.Rect(100,100,50,50)
playerRect2 = pygame.Rect(300,300,50,50)
#initialize variables for player
#variables for first rectangle
R1x = 740
R1y = 300
R1w = 30
R1h = 132
R1dx = 0
R1dy = 0
#variables for second rectangle
R2x = 10
R2y = 300
R2w = 30
R2h = 132
R2dx = 0
R2dy = 0
#ball variables
bx = 100
by = 150
dby = 1
dbx = 1
br = 15
cy = screen.get_height()/2
cx = screen.get_width()/2
#speed
speed = 5
fontsize = 50
fontTitle = pygame.font.SysFont('arial',fontsize)
textTitle = fontTitle.render("Left paddle score ="+str(R1x),True, (YELLOW))
clock = pygame.time.Clock()
FPS = 300
# set main loop to True so it will run
main = True
# main loop
while main:
for event in pygame.event.get(): # check for any events (i.e key press, mouse click etc.)
if event.type ==pygame.QUIT: # check to see if it was "x" at top right of screen
main = False # set the "main" variable to False to exit while loop
if event.type ==pygame.KEYDOWN:
if event.key == pygame.K_UP:
R1dx = 0
R1dy = -speed
elif event.key == pygame.K_DOWN:
R1dx = 0
R1dy = speed
if event.key == pygame.K_w:
R2dx = 0
R2dy = -speed
elif event.key == pygame.K_s:
R2dx = 0
R2dy = speed
if event.type == pygame.KEYUP:
if event.key == pygame.K_UP or event.key == pygame.K_DOWN:
R1dx = 0
R1dy = 0
elif event.key == pygame.K_w or event.key == pygame.K_s:
R2dx = 0
R2dy = 0
clock.tick(FPS)
R1x = R1dx + R1x
R2x = R2dx + R2x
bx = dbx + bx
by = dby + by
if R1x >= screen.get_height() - 80 or R1x < 0:
R1dx = 0
if R2x >= screen.get_height() - 80 or R2x < 0:
R2dx = 0
if by >= screen.get_height() - br:
dby = -1
if by <= 15:
dby = 1
if bx >= R2x - br and by - R2x > 0 and by - R2x < 100:
dbx = -1
if bx <= R1x + br and by - R1x > 0 and by - R1x < 100:
dbx = 1
if bx == 1:
R2xscore = R1xscore + 1
pause = True
if bx == 799:
left_paddlescore = left_paddlescore + 1
pause = True
# check collision
collided = playerRect.colliderect(playerRect2)
if collided == True:
playerRect.x = oldX
playerRect.y = oldY
# move the x and y positions of the rectangles
R1y = max(0, min(screen_h-R1h, R1y + R1dy))
R2y = max(0, min(screen_h-R2h, R2y + R2dy))
R1x = max(0, min(screen_w-R1w, R1x + R1dx))
R2x = max(0, min(screen_w-R2w, R2x + R2dx))
screen.fill(BLACK)
playerRect.move_ip(R1dx,R1dy)
playerRect2.move_ip(R2dx,R2dy)
# draw the shapes, in this case the blue rectangles
pygame.draw.rect(screen, WHITE,(R1x, R1y, R1w, R1h),0)
pygame.draw.rect(screen, WHITE,(R2x, R2y, R2w, R2h),0)
pygame.draw.circle(screen, RED,(bx,by),br,0)
# we are using .flip() here, it basically works the same as .update()
# we will discuss this more in class (you can use either one)
pygame.display.flip()
# quit pygame and exit the program (i.e. close everything down)
pygame.quit()
sys.exit()
The movement of the ball has to be computed in the application loop, rather than the event loop. Use pygame.Rect obejcts for the paddles and the ball. Detect a collision between the ball and the paddles by colliderect(). If the ball hits the right paddle, then the right border of the ball has to be set by the left border of the paddle. If the ball hits the left paddle, then the left border of the ball has to be set by the right border of the paddle. e.g.:
while main:
for event in pygame.event.get():
if event.type == pygame.QUIT:
main = False
# [...]
clock.tick(FPS)
R1x = R1dx + R1x
R2x = R2dx + R2x
bx = bx + dbx * speed
by = by + dby * speed
# move the x and y positions of the rectangles
R1y = max(0, min(screen_h-R1h, R1y + R1dy))
R2y = max(0, min(screen_h-R2h, R2y + R2dy))
R1x = max(0, min(screen_w-R1w, R1x + R1dx))
R2x = max(0, min(screen_w-R2w, R2x + R2dx))
playerRect = pygame.Rect(R1x, R1y, R1w, R1h)
playerRect2 = pygame.Rect(R2x, R2y, R2w, R2h)
ballRect = pygame.Rect(bx-br, by-br, br*2, br*2)
if ballRect.top <= 0:
dby = abs(dby)
if ballRect.bottom >= screen.get_height():
dby = -abs(dby)
if ballRect.colliderect(playerRect2):
ballRect.left = playerRect2.right
dbx = abs(dbx)
if ballRect.colliderect(playerRect):
ballRect.right = playerRect.left
dbx = -abs(dbx)
bx, by = ballRect.center
screen.fill(BLACK)
# draw the shapes, in this case the blue rectangles
pygame.draw.rect(screen, WHITE, playerRect)
pygame.draw.rect(screen, WHITE, playerRect2)
pygame.draw.circle(screen, RED, ballRect.center, br)
# we are using .flip() here, it basically works the same as .update()
# we will discuss this more in class (you can use either one)
pygame.display.flip()
Related
Im replicating a basic pong game and my paddles are opposite. My left paddle moves with the arrows while the right paddle moves with wsad, so when you play the players play they have to move the opposite paddle that they are on the side of. Also, I'm trying to make the ball come back when its off the screen and I was playing around with it and now there's collision with the wall as well as the paddle causing it to be a never ending game instead of rounded pong.
# import the necessary modules
import pygame
import sys
import time
#initialize pygame
pygame.init()
screenSize=(700,500)
screen=pygame.display.set_mode((screenSize), 0)
# set the caption for the screen
pygame.display.set_caption("Meryem's Pong Game")
# define colours you will be using
WHITE = (255,255,255)
GREEN = (0,255,0)
RED = (255,0,0)
BLUE = (0,0,255)
BLACK = (0,0,0)
YELLOW = (255,255,0)
# set the size for the surface (screen)
screen_h = screen.get_height()
screen_w = screen.get_width()
cx = int(screen_w/2)
cy = int(screen_h/2)
#initialize variables for player
#variables for first rectangle
R1x = 660
R1y = 300
R1w = 10
R1h = 132
R1dx = 0
R1dy = 0
R1_score = 0
#variables for second rectangle
R2x = 10
R2y = 2
R2w = 10
R2h = 132
R2dx = 0
R2dy = 0
R2_score = 0
#ball variables
bx = cx
by = cy
dby = 3
dbx = 3
br = 5
cy = screen.get_height()/2
cx = screen.get_width()/2
# variable for scores
R1_score = 0
R2_score = 0
playerRect = pygame.Rect(R2x,R2y,R2w, R2h)
playerRect2 = pygame.Rect(R1x,R1y,R1w, R1h)
ballRect = pygame.Rect (cx,cy,30,30)
#speed
speed = 3
fontsize = 50
fontScore = pygame.font.SysFont('arial', 50)
fontScore = pygame.font.SysFont('arial', 50)
R1Score = fontScore.render(str(R1_score), True, (WHITE))
R2Score = fontScore.render(str(R2_score), True, (WHITE))
# speed of object "clock"
clock = pygame.time.Clock()
FPS_easy = 30 # set frames per second for easy level
FPS_medium = 80 # set frames per second for medium level
FPS_progressive = 100 # set frames per second for progressive level
# set main loop to True so it will run
main = True
# main loop
while main:
for event in pygame.event.get(): # check for any events (i.e key press, mouse click etc.)
if event.type ==pygame.QUIT: # check to see if it was "x" at top right of screen
main = False # set the "main" variable to False to exit while loop
if event.type ==pygame.KEYDOWN:
if event.key == pygame.K_UP:
R1dx = 0
R1dy = -speed
elif event.key == pygame.K_DOWN:
R1dx = 0
R1dy = speed
if event.key == pygame.K_w:
R2dx = 0
R2dy = -speed
elif event.key == pygame.K_s:
R2dx = 0
R2dy = speed
if event.type == pygame.KEYUP:
if event.key == pygame.K_UP or event.key == pygame.K_DOWN:
R1dx = 0
R1dy = 0
elif event.key == pygame.K_w or event.key == pygame.K_s:
R2dx = 0
R2dy = 0
playerRect.y = playerRect.y + R1dy
playerRect2.y = playerRect2.y + R2dy
ballRect.move_ip(dbx,dby)
#collision of ball
if ballRect.top <= 0:
dby = -dby
if ballRect.bottom >= screen_h:
dby = -dby
if ballRect.left <= 0:
dbx = -dbx
if ballRect.right >= screen_w:
dbx = -dbx
if ballRect.colliderect(playerRect2):
dbx = -dbx
if ballRect.colliderect(playerRect):
dbx = -dbx
screen.fill(BLACK)
# draw the shapes, in this case the blue rectangles
pygame.draw.rect(screen, WHITE,(playerRect),0)
pygame.draw.rect(screen, WHITE,(playerRect2),0)
pygame.draw.circle(screen, RED,ballRect.center,br,0)
screen.blit(R1Score, (280,10))
screen.blit(R2Score, (400,10))
# we are using .flip() here, it basically works the same as .update()
# we will discuss this more in class (you can use either one)
pygame.display.flip()
# quit pygame and exit the program (i.e. close everything down)
pygame.quit()
sys.exit()
fixed your issues:
# import the necessary modules
import pygame
import sys
import time
#initialize pygame
pygame.init()
screenSize=(700,500)
screen=pygame.display.set_mode((screenSize), 0)
# set the caption for the screen
pygame.display.set_caption("Meryem's Pong Game")
# define colours you will be using
WHITE = (255,255,255)
GREEN = (0,255,0)
RED = (255,0,0)
BLUE = (0,0,255)
BLACK = (0,0,0)
YELLOW = (255,255,0)
# set the size for the surface (screen)
screen_h = screen.get_height()
screen_w = screen.get_width()
cx = int(screen_w/2)
cy = int(screen_h/2)
#initialize variables for player
#variables for first rectangle
R1x = 660
R1y = 300
R1w = 10
R1h = 132
R1dx = 0
R1dy = 0
R1_score = 0
#variables for second rectangle
R2x = 10
R2y = 2
R2w = 10
R2h = 132
R2dx = 0
R2dy = 0
R2_score = 0
#ball variables
bx = cx
by = cy
dby = 1
dbx = 1
br = 5
cy = screen.get_height()/2
cx = screen.get_width()/2
# variable for scores
R1_score = 0
R2_score = 0
playerRect = pygame.Rect(R2x,R2y,R2w, R2h)
playerRect2 = pygame.Rect(R1x,R1y,R1w, R1h)
ballRect = pygame.Rect (cx,cy,30,30)
#speed
speed = 3
fontsize = 50
fontScore = pygame.font.SysFont('arial', 50)
fontScore = pygame.font.SysFont('arial', 50)
R1Score = fontScore.render(str(R1_score), True, (WHITE))
R2Score = fontScore.render(str(R2_score), True, (WHITE))
# speed of object "clock"
clock = pygame.time.Clock()
FPS_easy = 30 # set frames per second for easy level
FPS_medium = 80 # set frames per second for medium level
FPS_progressive = 100 # set frames per second for progressive level
# set main loop to True so it will run
main = True
# main loop
while main:
for event in pygame.event.get(): # check for any events (i.e key press, mouse click etc.)
if event.type ==pygame.QUIT: # check to see if it was "x" at top right of screen
main = False # set the "main" variable to False to exit while loop
if event.type ==pygame.KEYDOWN:
if event.key == pygame.K_UP:
R2dx = 0
R2dy = -speed
elif event.key == pygame.K_DOWN:
R2dx = 0
R2dy = speed
if event.key == pygame.K_w:
R1dx = 0
R1dy = -speed
elif event.key == pygame.K_s:
R1dx = 0
R1dy = speed
if event.type == pygame.KEYUP:
if event.key == pygame.K_UP or event.key == pygame.K_DOWN:
R2dx = 0
R2dy = 0
elif event.key == pygame.K_w or event.key == pygame.K_s:
R1dx = 0
R1dy = 0
playerRect.y = playerRect.y + R1dy
playerRect2.y = playerRect2.y + R2dy
ballRect.move_ip(dbx,dby)
#collision of ball
if ballRect.top <= 0:
dby = -dby
if ballRect.bottom >= screen_h:
dby = -dby
if ballRect.left <= 0:
dbx = -dbx
if ballRect.right >= screen_w:
dbx = -dbx
if ballRect.colliderect(playerRect2):
dbx = -dbx
if ballRect.colliderect(playerRect):
dbx = -dbx
screen.fill(BLACK)
# draw the shapes, in this case the blue rectangles
pygame.draw.rect(screen, WHITE,(playerRect),0)
pygame.draw.rect(screen, WHITE,(playerRect2),0)
pygame.draw.circle(screen, RED,ballRect.center,br,0)
screen.blit(R1Score, (280,10))
screen.blit(R2Score, (400,10))
# we are using .flip() here, it basically works the same as .update()
# we will discuss this more in class (you can use either one)
time.sleep(0.005)
pygame.display.flip()
# quit pygame and exit the program (i.e. close everything down)
pygame.quit()
sys.exit()
must say the code you gave isn't very pretty
You have confused R1x, R1y, R1w, R1h and R2x, R2y, R2w, R2h when you create the pygame.Rect objects playerRect respectively playerRect2
playerRect = pygame.Rect(R2x,R2y,R2w, R2h)
playerRect2 = pygame.Rect(R1x,R1y,R1w, R1h)
playerRect = pygame.Rect(R1x, R1y, R1w, R1h)
playerRect2 = pygame.Rect(R2x, R2y, R2w, R2h)
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()
So im new at pygame and coding my first project- a side scrolling shooter. The issue im having is with my bullets: when i press the space key, some of the bullets will show up but there are times when nothing happens, and no bullets spawn when i jump. Not quite sure how to go about fixing this issue- any ideas would be greatly appreciated.
Code is as follows:
import pygame
import math, random, sys, pygame.mixer
from pygame.locals import *
pygame.init()
pygame.mixer.pre_init(44100, -16, 2, 8192)
pygame.mixer.init()
jump = False
jump_offset = 0
jump_height = 250
k = pygame.key.get_pressed()
def events():
for event in pygame.event.get():
if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
pygame.quit()
sys.exit()
def do_jumping():
global jump_height
global jump
global jump_offset
if jump:
jump_offset += 3
if jump_offset >= jump_height:
jump = False
elif jump_offset > 0 and jump == False:
jump_offset -= 3
#Defining colours
BLACK = ( 0, 0, 0)
WHITE = ( 255, 255, 255)
#Window Settings
w = 1280
h = 720
half_w = w /2
half_h = h /2
AREA = w*h
#Initialising the window
pygame.init()
display = pygame.display.set_mode((w,h)) #Sets the size of the window
pygame.display.set_caption("Cattleman") #Sets the title of the window
Clock = pygame.time.Clock() #clockspeed for the game ie. 60fps
FPS = 600
#pygame.mouse.set_visible(True) #Allows the mouse to be shown in the game window.
background = pygame.image.load("background.png").convert()
backgroundWidth, backgroundHeight = background.get_rect().size
stageWidth = backgroundWidth*2 #sets the area which the player can move in
stagePosX = 0 #Records position of stage as the player moves
startScrollPosX = half_w
circleRadius = 25
circlePosX = circleRadius
playerPosX = circleRadius
playerPosY = 602
playerVelocityX = 0
playersprite = pygame.image.load("player_spriteR2.png").convert_alpha()
playersprite = pygame.transform.scale(playersprite, (130,130))
bullets = []
bulletSprite = pygame.image.load("Bullet1.png").convert_alpha()
bulletSprite = pygame.transform.scale(bulletSprite, (20,10))
#Sounds
#gunSounds = ["pew1.wav", "pew2.wav", "pew3.wav", "pew4.wav"]
#SOUNDS
shot = pygame.mixer.Sound("pew1.wav")
#------------------------MAIN PROGRAM LOOP------------------------#
while True:
events()
do_jumping()
k = pygame.key.get_pressed()
if k[K_RIGHT]:
playerVelocityX = 2 #Moves the player right
playersprite = pygame.image.load("player_spriteR2.png").convert_alpha()
playersprite = pygame.transform.scale(playersprite, (130,130))
if k[K_LEFT]:
playerVelocityX = -2 #Moves the player left
playersprite = pygame.image.load("player_spriteL2.png").convert_alpha()
playersprite = pygame.transform.scale(playersprite, (130,130))
if k[K_UP] and jump == False and jump_offset == 0:
jump = True
if not k[K_RIGHT] and not k[K_LEFT]:
playerVelocityX = 0 #If no input detected, the player does not move
if k[K_SPACE]:
for event in pygame.event.get():
bullets.append([circlePosX-100, playerPosY-20])
shot.play()
playerPosX += playerVelocityX
if playerPosX > stageWidth - circleRadius-25: playerPosX = stageWidth - circleRadius-25 #Checks if the player trie to go past the right boundary
if playerPosX < circleRadius+55:playerPosX = circleRadius+55 #Checks if the player tries to go past the left boundary
if playerPosX < startScrollPosX: circlePosX = playerPosX
elif playerPosX > stageWidth - startScrollPosX: circlePosX = playerPosX - stageWidth + w
else:
circlePosX = startScrollPosX
stagePosX += -playerVelocityX
for b in range(len(bullets)):
bullets[b][0] -= 3
for bullet in bullets[:]:
if bullet[0] < 0:
bullets.remove(bullet)
rel_x = stagePosX % backgroundWidth
display.blit(background,(rel_x - backgroundWidth, 0))
if rel_x < w:
display.blit(background, (rel_x, 0))
for bullet in bullets:
display.blit(bulletSprite, pygame.Rect(bullet[0], bullet[1], 0, 0,))
#pygame.draw.circle(display,WHITE, (int(circlePosX),playerPosY - jump_offset), circleRadius, 0)
display.blit(playersprite, (int(circlePosX-80),playerPosY-100 - jump_offset))
pygame.display.update()
Clock.tick(FPS)
display.fill(BLACK)
I have done this in my code that leaves multiple balls similar like bullets :
class Bullet():
def __init__(self, x, y):
# self.image = pygame.image.load("SingleBullet.png")
self.image = pygame.image.load("ball.png")
self.image = pygame.transform.scale(self.image, (25, 25))
self.rect = self.image.get_rect()
self.rect.centerx = x
self.rect.centery = y
self.is_alive = True
# --------------------
def update(self):
self.rect.y -= 15
if self.rect.y < 0:
self.is_alive = False
# --------------------
def draw(self, screen):
screen.blit(self.image, self.rect.topleft)
for getting keyboard :
def event_handler(self, event):
if event.type == KEYDOWN:
if event.key == K_LEFT:
self.move_x = -5
elif event.key == K_RIGHT:
self.move_x = 5
elif event.key == K_SPACE:
if len(self.shots) < self.max_shots:
self.shots.append(Bullet(self.rect.centerx, self.rect.top))
if event.type == KEYUP:
if event.key in (K_LEFT, K_RIGHT):
self.move_x = 0
I am trying to add a tail to my snake (at the very end use a triangle image rather than a square fill). While I think I got the code to work for the most part, I was seeing that if I changed directions, the last few seconds the tail would "disconnect" from the body. (tail points right and body going down leaves a gap). I tried to fix this by upping my FPS which seemed to work; however I wanted the snake speed to be the same as before and since I doubled the FPS I would have to 1/2 the speed. When I did that however, my collision detection was out of sync and if I slowed it down my body would draw over my face, and if I sped up I would have my body getting disconnected (block, space, block). I have tried it a few different ways so any help would be appreciated.
Please note that block_speed = 10, and if I manually type 10 it works, but if I change to 5 or 20, or if I change to a variable with value of 5 or 20 (say speed for example), the code does not work.
Code:
import pygame, sys
from pygame.locals import*
import time
import random
import os
pygame.init()
#GUI Settings
display_Width = 800
display_Height = 600
gameDisplay = pygame.display.set_mode((display_Width,display_Height))
pygame.display.set_caption("Gluttonous Snake")
gameicon = pygame.image.load('icon.png')
potatoimg = pygame.image.load('potato.png')
pygame.display.set_icon(gameicon)
FPS = 15
direction = "up"
#set path to where to .py/.exe is
abspath = os.path.abspath(__file__)
dname = os.path.dirname(abspath)
os.chdir(dname)
print(dname)
snakeheadimg = pygame.image.load('snakehead.png')
snaketailimg = pygame.image.load('snaketail.png')
appleimg = pygame.image.load('apple.png')
#define colors
white = (255,255,255)
black = (0,0,0)
red = (255,0,0)
green = (0,255,0)
blue = (0,0,255)
yellow = (255,255,0)
eggwhite = (255,255,204)
lightgrey = (242,242,242)
#Game Variables
block_size = 10
clock = pygame.time.Clock()
def game_intro():
intro = True
x = 500
y = 400
x_dir = "left"
while intro:
gameDisplay.fill(eggwhite)
gameDisplay.blit(potatoimg, (50, 25))
message_to_screen("Potato Productions Presents...", black, -100, size=45)
message_to_screen("Gluttonous Snake", green, -25, size=75)
message_to_screen("A game made by a potato to run on a potato", black, 50, size=25)
message_to_screen("Press C to Start!", red, 75, size=25)
gameDisplay.blit(gameicon, (x, y))
if x_dir == "left":
if x > 0:
x -= 10
else:
x_dir = "right"
else:
if x < 500:
x += 10
else:
x_dir = "left"
if x < 125 or x > 375:
y -= 9.66
else:
y += 10
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
intro = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_c:
intro = False
if event.key == pygame.K_q:
pygame.quit()
quit()
def pickFont(name,size):
font = pygame.font.SysFont(name, size, bold=False)
return font
#font size = 25
#font = pygame.font.SysFont("comicsansms",size=25)
def snake(snakelist):
# faster GPU method is
# gameDisplay.fill(red, rect=[200,200,50,50])
if direction == "left":
head = pygame.transform.rotate(snakeheadimg,90)
if direction == "right":
head = pygame.transform.rotate(snakeheadimg,270)
if direction == "down":
head = pygame.transform.rotate(snakeheadimg,180)
if direction == "up":
head = pygame.transform.rotate(snakeheadimg,0)
gameDisplay.blit(head,(snakelist[-1][0],snakelist[-1][1]))
#-1 because we are drawing that above
# for XnY in snakelist[:-1]:
# #gameDisplay.fill(green, rect=[lead_x, lead_y, block_size, block_size])
# gameDisplay.fill(green, rect=[XnY[0], XnY[1], block_size, block_size])
# -1 because we are drawing that above
if len(snakelist) >= 2:
for XnY in snakelist[1:-1]:
gameDisplay.fill(green, rect=[XnY[0], XnY[1], block_size, block_size])
if direction == "up":
tail = pygame.transform.rotate(snaketailimg, 180)
if snakelist[1][0] > snakelist[0][0]:
tail = pygame.transform.rotate(snaketailimg, 90)
elif snakelist[1][0] < snakelist[0][0]:
tail = pygame.transform.rotate(snaketailimg, 270)
elif snakelist[1][1] > snakelist[0][1]:
tail = pygame.transform.rotate(snaketailimg, 0)
elif snakelist[1][1] < snakelist[0][1]:
tail = pygame.transform.rotate(snaketailimg, 180)
gameDisplay.blit(tail, (snakelist[-len(snakelist)][0], snakelist[-len(snakelist)][1]))
def text_objects(text, color,size):
font = pickFont("comicsansms", size)
textSurface = font.render(text,True,color,size)
return textSurface, textSurface.get_rect()
def message_to_screen(msg,color,y_displace=0, size=25):
#True is anti-aliasing
textSurf, textRect = text_objects(msg, color, size)
textRect.center = (display_Width/2),(display_Height/2) + y_displace
gameDisplay.blit(textSurf,textRect)
def gameLoop():
# set up variables
global direction
gameExit = False
gameOver = False
lead_x = display_Width / 2
lead_y = display_Height / 2
coinflip = random.randint(0, 1)
if coinflip == 0:
coinflip = random.randint(0, 1)
if coinflip == 0:
lead_x_change = **10**
lead_y_change = 0
direction = "right"
else:
lead_x_change = -**10**
lead_y_change = 0
direction = "left"
else:
coinflip = random.randint(0, 1)
if coinflip == 0:
lead_x_change = 0
lead_y_change = **10**
direction = "down"
else:
lead_x_change = 0
lead_y_change = -**10**
direction = "up"
#lead_x_change = 0
#lead_y_change = 0
#the 10 is round to 10
randAppleX = random.randrange(0, display_Width - block_size, 10)
randAppleY = random.randrange(0, display_Height - block_size, 10)
snakelist = []
snakelength = 1
while not gameExit:
while gameOver == True:
gameDisplay.fill(white)
#message_to_screen("Game over \n Press C to play again or Q to quit", red)
message_to_screen("Game Over", red, y_displace=-50, size=75)
message_to_screen("Press C to play again or Q to quit",black,y_displace=50,size=25)
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_q:
gameExit = True
gameOver = False
if event.key == pygame.K_c:
gameLoop()
#gameOver = False
for event in pygame.event.get():
#shows every mouse move and key pressed
#print(event)
if event.type == pygame.QUIT:
gameExit = True
gameOver = False
#check for single depress of keys
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
#lead_x -= 10
#this is so they can't back over themselves
if lead_x_change != **block_size**:
lead_x_change = - **block_size**
lead_y_change = 0
direction = "left"
#elif is only tested if the ifs and elifs above it are not true
elif event.key == pygame.K_RIGHT:
#lead_x += 10
if lead_x_change != -**block_size**:
lead_x_change = **block_size**
lead_y_change = 0
direction = "right"
elif event.key == pygame.K_UP:
if lead_y_change != **block_size**:
lead_x_change = 0
lead_y_change = -**block_size**
direction = "up"
elif event.key == pygame.K_DOWN:
if lead_y_change != -**block_size**:
lead_x_change = 0
lead_y_change = **block_size**
direction = "down"
# user releases key
# if event.type == pygame.KEYUP:
# if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
# lead_x_change = 0
#Ends the game once the square has left the window
if lead_x >= (display_Width - block_size) or lead_x <= 0 or lead_y >= (display_Height - block_size) or lead_y <= 0:
print("snake left at " + str(lead_x)+","+str(lead_y))
lead_x_change = 0
lead_y_change = 0
gameOver = True
lead_x += lead_x_change
lead_y += lead_y_change
gameDisplay.fill(lightgrey)
snakehead = []
snakehead.append(lead_x)
snakehead.append(lead_y)
snakelist.append(snakehead)
if len(snakelist) > snakelength:
del snakelist[0]
#-1 because last element is the head
for eachSegement in snakelist[:-1]:
if eachSegement == snakehead:
print("snake eats itself")
gameOver = True
#draw snake first so if apple spawns on it I can still see it
snake(snakelist)
#gameDisplay.fill(red, rect=[randAppleX, randAppleY, block_size, block_size])
gameDisplay.blit(appleimg,(randAppleX, randAppleY))
pygame.display.update()
#better collison detection as part of the snake can go over part of the apple
# if lead_x >= randAppleX and lead_x + block_size < randAppleX + block_size or lead_x + block_size >= randAppleX and lead_x + block_size < randAppleX + block_size:
# if lead_y >= randAppleY and lead_y < randAppleY + block_size or lead_y + block_size >= randAppleY and lead_y + block_size < randAppleY + block_size:
if lead_x >= randAppleX:
if lead_x + block_size <= randAppleX + block_size:
if lead_y >= randAppleY:
if lead_y + block_size <= randAppleY + block_size:
print("nom nom nom")
randAppleX = random.randrange(0, display_Width - block_size, 10)
randAppleY = random.randrange(0, display_Height - block_size, 10)
snakelength += 1
#used to make FPS
clock.tick(FPS)
pygame.quit()
quit()
game_intro()
gameLoop()
Reply to answer provided:
Great thanks I will look into this. Were you able to figure out why I can't adjust the speed though? Seems weird it would draw the body over the head if I slow down the speed, or it will leave gaps if I speed it up. The part were I was adjusting the speed was in bold
You can probably smooth things a bit (and make your code clearer) by doing the following:
1 - store the tail (head) rotated images:
tail_left = pygame.transform.rotate(snaketailimg, 180) # choose appropriate rotation
tail_right = ...
tail_up = ...
tail_down = ...
2 - determine which direction the snake goes, and look up the images from a dict (for instance)
tail_oriented_images = {'left': tail_left, 'right': tail_right, ...}
...
tail_direction = get_tail_direction() # to be extracted
3- then replace the if cascade in snake(snakelist) with:
tail = tail_oriented_images[tail_direction]
4- Do the same for the head direction
I am making a game in which the player has to use a bowl to catch falling items. I have some images of items in a list and an image of a bowl that is used to catch the items. The items keep on falling and reset to the top of the screen if they reach the boundary (bottom edge). I got this logic done which allows the items to fall but I do not know how to detect when there is a collision between the bowl and item.
My code:
import math
import pygame
import random
pygame.init()
display_width = 800
display_height = 600
game_display = pygame.display.set_mode((display_width, display_height))
clock = pygame.time.Clock()
pygame.display.set_caption("Catch the Ball")
white = (255, 255, 255)
black = (0, 0, 0)
red = (255, 0, 0)
blue = (0, 255, 0)
player_img = pygame.image.load("Images/soup.png")
thing_imgs = [pygame.image.load('Images/muffin.png'), pygame.image.load('Images/dessert.png'),
pygame.image.load('Images/cheese.png'), pygame.image.load('Images/fruit.png')]
def player(x, y):
game_display.blit(player_img, (x, y))
def things(x, y, img):
game_display.blit(img, (x, y))
def game_loop():
running = True
x = display_width * 0.45
y = display_height * 0.8
x_change = 0
player_width = 64
player_height = 64
things_cor = [[random.randint(0, display_width), 32]]
things_added = [random.choice(thing_imgs)]
thing_height = 32
thing_width = 32
y_change = 5
caught = 0
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
x_change = -5
if event.key == pygame.K_RIGHT:
x_change = 5
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
x_change = 0
game_display.fill(white)
player(x, y)
x += x_change
for i in range(len(things_cor)):
thing_x, thing_y = things_cor[i]
things(thing_x, thing_y, things_added[i])
for i in range(len(things_cor)):
things_cor[i][1] += y_change
if things_cor[i][1] > display_height:
things_cor[i][1] = random.randint(-2000, -1000)
things_cor[i][0] = random.randint(0, display_width)
things_added[i] = random.choice(thing_imgs)
things_added.append(random.choice(thing_imgs))
if len(things_added) < 6:
things_cor.append(
[random.randint(0, display_width), -10])
if x < 0:
x = 0
elif x > display_width - player_width:
x = display_width - player_width
clock.tick(60)
pygame.display.update()
game_loop()
Use pygame.Rect objects and colliderect() to detect the collision between the bounding rectangles of 2 objects or 2 images:
rect1 = pygame.Rect(x1, y1, w1, h1)
rect2 = pygame.Rect(x2, y2, w2, h2)
if rect1.colliderect(rect2):
# [...]
If you have to images (pygame.Surface objects), the bounding rectangle of can be get by get_rect(), where the location of the Surface has to be set by an keyword argument, since the returned rectangle always starts at (0, 0):
(see Why is my collision test not working and why is the position of the rectangle of the image always wrong (0, 0)?)
def game_loop():
# [...]
while running:
# [...]
player_rect = player_img.get_rect(topleft = (x, y))
for i in range(len(things_cor)):
thing_rect = things_added[i].get_rect(topleft = things_cor[i])
if player_rect.colliderect(thing_rect):
print("hit")
player(x, y)
x += x_change
for i in range(len(things_cor)):
thing_x, thing_y = things_cor[i]
things(thing_x, thing_y, things_added[i])
Use pygame.time.get_ticks() to delay the start of the game for a certain time. pygame.time.get_ticks() return the number of milliseconds since pygame.init() was called. For instance:
def game_loop():
# [...]
while running:
passed_time = pygame.time.get_ticks() # passed time in milliseconds
start_time = 100 * 1000 # start time in milliseconds (100 seconds)
# [...]
# move player
if passed_time >= start_time:
x += x_change
if x < 0:
x = 0
elif x > display_width - player_width:
x = display_width - player_width
# move things
if passed_time >= start_time:
for i in range(len(things_cor)):
things_cor[i][1] += y_change
if things_cor[i][1] > display_height:
things_cor[i][1] = random.randint(-2000, -1000)
things_cor[i][0] = random.randint(0, display_width)
things_added[i] = random.choice(thing_imgs)
things_added.append(random.choice(thing_imgs))
if len(things_added) < 6:
things_cor.append(
[random.randint(0, display_width), -10])
# draw scene and update dispaly
game_display.fill(white)
player(x, y)
for i in range(len(things_cor)):
thing_x, thing_y = things_cor[i]
things(thing_x, thing_y, things_added[i])
pygame.display.update()
clock.tick(60)