Pygame--pygame can't run a pingpong collision game - python

#!/usr/bin/python
# -*- coding: utf-8 -*-
import pygame
import sys
class MyBallClass(pygame.sprite.Sprite):
def __init__(self, image_file, speed, location):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location
self.speed = speed
def move(self):
global points, score_text
self.rect = self.rect.move(self.speed)
if self.rect.left < 0 or self.rect.right > screen.get_width():
self.speed[0] = -self.speed[0]
if self.rect.top <= 0:
self.speed[1] = -self.speed[1]
points += 1
score_text = font.render(str(points), 1, (0, 0, 0))
class MyPaddleClass(pygame.sprite.Sprite):
def __init__(self, location=[0, 0]):
pygame.sprite.Sprite.__init__(self)
image_surface = pygame.surface.Surface([100, 20])
image_surface.fill([0, 0, 0])
self.image = image_surface.convert()
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location
pygame.init()
screen = pygame.display.set_mode([640, 480])
clock = pygame.time.Clock()
ball_speed = [3, 4]
myball = MyBallClass("E:\\python file\\blackball.jpg", ball_speed, [50, 50])
ballgroup = pygame.sprite.Group(myball)
paddle = MyPaddleClass([270, 400])
lives = 3
points = 0
font = pygame.font.Font(None, 50)
score_text = font.render(str(points), 1, (0, 0, 0))
textpos = [10, 10]
done = False
while 1:
clock.tick(30)
screen.fill([255, 255, 255])
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.MOUSEMOTION:
paddle.rect.centerx = event.pos[0]
if pygame.sprite.spritecollide(paddle, ballgroup, False):
myball.speed[1] = -myball.speed[1]
myball.move()
if not done:
screen.blit(myball.image, myball.rect)
screen.blit(paddle.image, paddle.rect)
screen.blit(score_text, textpos)
for i in range(lives):
width = screen.get_width()
screen.blit(myball.image, [width - 40 * i, 20])
pygame.display.flip()
if myball.rect.top <= screen.get_rect().bottom:
# In get_rect(), you cannot leave out brackets
lives -= 1
if lives == 0:
final_text1 = "Game over!"
final_text2 = 'your final score is' + str(points)
ft1_font = pygame.font.Font(None, 70)
ft2_font = pygame.font.Font(None, 50)
ft1_surface = font.render(final_text1, 1, (0, 0, 0))
ft2_surface = font.render(final_text2, 1, (0, 0, 0))
screen.blit(ft1_surface, [screen.get_width() / 2, 100])
screen.blit(ft2_surface, [screen.get_width() / 2, 200])
pygame.display.flip()
done = True
else:
pygame.time.delay(1000)
myball.rect.topleft = [50, 50]
frame_rate = clock.get_fps()
print(frame_rate)
Here is the pygame window picture of my code:(http://i.stack.imgur.com/SFyBJ.jpg)
Every time I run it, I don't have the time to control the paddle, then it shows game is over. I have been searching for a long time but can't find out why. It seems like the blackball is moving so fast that the game is over in about one second. But I already set the speed to a reasonable range, I am so confused. Can anyone help?

I could rebuild your game, and fixed the problem.
Try inverse condition here
if myball.rect.top >= screen.get_rect().bottom:
and it works fine: I can hit the ball with the bat, make it bounce, ...
I don't know why it took me so long ot figure it out: you lose the game if ball goes off the screen by the bottom. For that, top y must be greater than the bottom of the window (computer screen coordinates are 0,0 from upper left)

Related

Collison and Resetting in PyGame [duplicate]

This question already has answers here:
How do I detect collision in pygame?
(5 answers)
Closed 1 year ago.
My problem: I am trying to create a vertical ball drop game, and I am testing for collision, which does work. But how would I reset my ball when it hits the hoop? Instead of it detecting and then hitting the bottom of the screen which results in a Game over screen because you lose. Any help is appreciated.
import time, random
from pygame.locals import *
import pygame, sys
pygame.init()
FPS = 60
FramePerSec = pygame.time.Clock()
BLUE = (0, 0, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
SCREEN_WIDTH = 400
SCREEN_HEIGHT = 600
SCREEN_BOTTOM = 0
SPEED = 5
SCORE = 0
font = pygame.font.SysFont("Verdana", 60)
font_small = pygame.font.SysFont("Verdana", 20)
game_over = font.render("Game Over", True, BLACK)
background = pygame.image.load("background.jpg")
DISPLAYSURF = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
DISPLAYSURF.fill(WHITE)
pygame.display.set_caption("Ball Drop")
class Ball(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load("Ball.png")
self.rect = self.image.get_rect()
self.rect.center = (random.randint(40, SCREEN_WIDTH - 40), 0)
def move(self):
global SCORE
self.rect.move_ip(0, SPEED)
if (self.rect.bottom > 600):
self.rect.top = 0
self.rect.center = (random.randint(30, 380), 0)
# Need to check PNG for hit detection
class Basket(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load("Basket.png")
self.rect = self.image.get_rect()
self.rect.center = (160, 520)
def move(self):
pressed_keys = pygame.key.get_pressed()
if(self.rect.x >= (SCREEN_WIDTH - 145)):
self.rect.x -= 5;
elif(self.rect.x <= -5):
self.rect.x += 5;
else:
if pressed_keys[pygame.K_a]:
self.rect.move_ip(-SPEED, 0)
if pressed_keys[pygame.K_d]:
self.rect.move_ip(SPEED, 0)
class Wall(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load("wall.png")
self.rect = self.image.get_rect()
self.rect.center = (0, 670)
B2 = Basket()
B1 = Ball()
W1 = Wall()
balls = pygame.sprite.Group()
balls.add(B1)
# Need to fix wall sprite group
walls = pygame.sprite.Group()
walls.add(W1)
all_sprites = pygame.sprite.Group()
all_sprites.add(B2)
all_sprites.add(B1)
INC_SPEED = pygame.USEREVENT + 1
pygame.time.set_timer(INC_SPEED, 1000)
while True:
for event in pygame.event.get():
if event.type == INC_SPEED:
SPEED += 0.3
if event.type == QUIT:
pygame.quit()
sys.exit()
DISPLAYSURF.blit(background, (0, 0))
scores = font_small.render(str(SCORE), True, BLACK)
DISPLAYSURF.blit(scores, (10, 10))
for entity in all_sprites:
DISPLAYSURF.blit(entity.image, entity.rect)
entity.move()
# NEed to fix collison and Counting stats
if pygame.sprite.spritecollideany(W1, balls):
DISPLAYSURF.fill(RED)
DISPLAYSURF.blit(game_over, (30, 250))
pygame.display.update()
for entity in all_sprites:
entity.kill()
time.sleep(2)
pygame.quit()
sys.exit()
if pygame.sprite.spritecollideany(B2, balls):
print("Hit")
SCORE += 1
pygame.display.update()
pygame.display.update()
FramePerSec.tick(FPS)
pygame.sprite.spritecollideany() returns the hit Sprite (ball) object. Change the position of this ball:
while True:
# [...]
ball_hit = pygame.sprite.spritecollideany(B2, balls)
if ball_hit:
ball_hit.rect.center = (random.randint(30, 380), 0)
SCORE += 1
print("Hit")
# [...]

Border collision only works once in pygame

I'm making a snake game in python, and I've almost finished, but I'm struggling to solve an issue. In the main class in the collision method, I wrote some code to detect the snake collision with the borders of the screen, and if the statement is true, the snake will return to its original state. However, when I run the program the collision detection only works once. Afterwards, the snake is able to simply move off the screen. Thanks in advance.
import pygame
import random
pygame.init()
clock = pygame.time.Clock()
# Screen setup
screen_width = 600
screen_height = 600
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("Snake")
cellSize = 20
# Colors
black = (0, 0, 0)
white = (255, 255, 255)
blue = (255, 0, 0)
green = (0, 0, 255)
# Grid
def draw_grid():
# Vertical lines
for x in range(0, screen_height, cellSize):
pygame.draw.line(screen, white, (x, 0), (x, screen_height))
# Horizontal lines
for y in range(0, screen_width, cellSize):
pygame.draw.line(screen, white, (0, y), (screen_width, y))
# Maze
class MAZE:
def __init__(self):
self.rect_width = 20
self.rect_height = 20
self.rects = []
def draw_rects(self):
# Drawing Maze
pass
# Snake
class SNAKE(MAZE):
def __init__(self):
super().__init__()
self.width = 20
self.height = 20
self.speed = 20
self.initBody = [[3 * cellSize, 40], [4 * cellSize, 40], [5 * cellSize, 40]]
self.body = [[3 * cellSize, 40], [4 * cellSize, 40], [5 * cellSize, 40]]
self.rects = []
self.move = [0, 0]
self.grow = False
def draw_snake(self):
# Drawing snake body
for cor in self.body:
snake_rect = pygame.Rect(cor[0], cor[1], self.width, self.height)
self.rects.append(snake_rect)
pygame.draw.rect(screen, blue, snake_rect)
def move_snake(self):
# Key bindings
key = pygame.key.get_pressed()
if key[pygame.K_UP]:
self.move = [0, -1]
if key[pygame.K_DOWN]:
self.move = [0, 1]
if key[pygame.K_LEFT]:
self.move = [-1, 0]
if key[pygame.K_RIGHT]:
self.move = [1, 0]
if self.move[0] != 0 or self.move[1] != 0:
# Adding rects to the end of snake body
new_rect = self.body[-1][:]
new_rect[0] += self.move[0] * cellSize
new_rect[1] += self.move[1] * cellSize
self.body.append(new_rect)
# Removing rects from tail
if self.grow == False:
self.body.pop(0)
self.grow = False
# Food
class FOOD_1:
def __init__(self):
self.width = 20
self.height = 20
self.x = 4 * cellSize
self.y = 4 * cellSize
self.food_rect = pygame.Rect(self.x, self.y, self.width, self.height)
def draw_food_1(self):
pygame.draw.rect(screen, green, self.food_rect)
maze = MAZE()
snake = SNAKE()
food_1 = FOOD_1()
class MAIN():
def __init__(self):
self.maze = MAZE()
self.snake = SNAKE()
self.food_1 = FOOD_1()
def draw(self):
self.maze.draw_rects()
self.snake.draw_snake()
self.food_1.draw_food_1()
def move(self):
self.snake.move_snake()
def collision(self):
# Snake and food collision
for rect in self.snake.rects:
if rect.colliderect(self.food_1.food_rect):
self.food_1.x = random.randint(0, 29) * cellSize
self.food_1.y = random.randint(0, 29) * cellSize
self.food_1.food_rect.topleft = (self.food_1.x, self.food_1.y)
self.snake.grow = True
# Border collisions
if self.snake.body[-1][0] <= 0:
self.snake.body = self.snake.initBody
elif self.snake.body[-1][0] >= 600:
self.snake.body = self.snake.initBody
elif self.snake.body[-1][1] <= 0:
self.snake.body = self.snake.initBody
elif self.snake.body[-1][1] >= 600:
self.snake.body = self.snake.initBody
main = MAIN()
# Main loop
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
# Drawing on screen
screen.fill(black)
main.draw()
draw_grid()
# Movement and Collisions
main.move()
main.collision()
pygame.display.flip()
clock.tick(10)
pygame.quit()
When you do self.snake.body = self.snake.initBody, you are not making a copy of initBody, you are defining body as being the same object as initBody, so when you modify one, the other is modified too...which happens while playing.
So after the first collision, initBody is modified at the same time as body (as they have become the same thing), so when another collision happens, the line self.snake.body = self.snake.initBody does nothing.
You need to replace it with self.snake.body = self.snake.initBody.copy() so that the original initBody remains intact.
Better you make a code like this in your loop.
if playerx = 0:
playerx = 0
if playerx = 600:
playerx = 600:
Use same method for also y axis.

How to make my 'game over' text stay on when collision is activated?

I making a small game with multiple falling cubes and the player(the square) has to avoid it. I manage to get the collision to work but the problem is when every time the circle and square collide it displays the text every time they collide. But I want the text to stay on when the circle and square first collide. Is there any way to do that?
import pygame
from pygame.locals import *
import os
import random
import math
import sys
import time
white = (255,255,255)
blue = (0,0,255)
gravity = 10
size =10
height = 500
width =600
varHeigth = height
ballNum = 5
eBall = []
apGame = pygame.display.set_mode((width, height))
pygame.display.set_caption("AP Project")
clock = pygame.time.Clock()
class Player(object):
def __init__(self):
red = (255, 0, 0)
move_x = 300
move_y = 400
self.rect = pygame.draw.rect(apGame,red, (move_x, move_y, 10, 10))
self.dist = 10
def handle_keys(self):
for e in pygame.event.get():
if e.type == pygame.QUIT:
pygame.quit();
exit()
key = pygame.key.get_pressed()
if key[pygame.K_LEFT]:
self.draw_rect(-1, 0)
elif key[pygame.K_RIGHT]:
self.draw_rect(1, 0)
elif key[pygame.K_ESCAPE]:
pygame.quit();
exit()
else:
self.draw_rect(0, 0)
def draw_rect(self, x, y):
red = (255, 0, 0)
black = (0, 0, 0)
'''apGame.fill(black)'''
self.rect = self.rect.move(x * self.dist, y * self.dist);
pygame.draw.rect(apGame, red , self.rect)
pygame.display.update()
def draw(self,surface):
red = (255, 0, 0)
move_x = 300
move_y = 400
pygame.draw.rect(apGame, red, (move_x, move_y, 10, 10))
#The game over text here
def show_go_screen():
pygame.font.init()
myfont = pygame.font.SysFont("Comic Sans MS", 30)
label = myfont.render("Game Over", 1, red)
apGame.blit(label, (300,100))
def instuct():
pygame.font.init()
myfont = pygame.font.SysFont("Comic Sans MS", 15)
label = myfont.render("Avoid The Circles", 1, red)
apGame.blit(label, (250,450))
'''game_over = False'''
move_x = 300
move_y = 400
red = (255, 0, 0)
black = (0, 0, 0)
player = Player()
clock = pygame.time.Clock()
'''apGame.fill(black)'''
player.draw(apGame)
pygame.display.update()
for q in range(ballNum):
x = random.randrange(0, width)
y = random.randrange(0, varHeigth)
eBall.append([x, y])
while True:
apGame.fill(black)
for i in range(len(eBall)):
ball_rect = pygame.draw.circle(apGame, blue, eBall[i], size)
#where the code is supposed to work
if player.rect.colliderect(ball_rect):
'''game_over = True'''
show_go_screen()
eBall[i][1] += 5
if eBall[i][1] > height:
y = random.randrange(-50, -10)
eBall[i][1] = y
x = random.randrange(0, width)
eBall[i][0] = x
instuct()
player.handle_keys()
pygame.display.flip()
clock.tick(30)
Whenever you want functionality to only run once, you should add a check outside of that function to make sure the code within that function only runs once. Here's an example of that using your code.
collidedOnce = False
if player.rect.colliderect(ball_rect):
if(collidedOnce == False):
collidedOnce = True #This makes the code only run once since it's setting this variable to be true within the condition
#game_over = True
show_go_screen()
eBall[i][1] += 5
if eBall[i][1] > height:
y = random.randrange(-50, -10)
eBall[i][1] = y
x = random.randrange(0, width)
eBall[i][0] = x
instuct()
player.handle_keys()
pygame.display.flip()
clock.tick(30)
Hope this helps

How to set a clicking clock in a pygame window

#!/usr/bin/python
# -*- coding: utf-8 -*-
import pygame
import sys
import datetime
import time
class TextPicture(pygame.sprite.Sprite):
def __init__(self, speed, location):
pygame.sprite.Sprite.__init__(self)
now = datetime.datetime.now()
text = now.strftime("%H:%M:%S")
font = pygame.font.Font(None, 40)
time_text = font.render(text, 1, [0, 0, 0]) # show now time
self.image = time_text
self.speed = speed
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location
def move(self):
self.rect = self.rect.move(self.speed)
if self.rect.left < 0 or self.rect.left > screen.get_width() - self.image.get_width():
self.speed[0] = -self.speed[0]
if self.rect.top < 0 or self.rect.top > screen.get_height() - self.image.get_height():
self.speed[1] = -self.speed[1]
pygame.init()
screen = pygame.display.set_mode([640, 480])
my_time_picture = TextPicture([1, 1], [50, 50])
while 1:
screen.fill([255, 255, 255])
my_time_picture.move()
screen.blit(my_time_picture.image, my_time_picture.rect)
pygame.display.flip()
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
I am designing a clock which can move in the screen. But what my code can do now is to show a invariant time. I want to clicking and count the time.
My invariable clock picture
And I want to make my clock more beautiful, but don't know how. I will be super grateful if anyone can make any suggestions.
You could do it by using pygame.time.set_timer() to make "tick events" be generated which cause the clock's image to be updated when encountered in the event processing loop.
To make implementing this easier, an update() method could be added to the DigitalClock class (which is what I renamed your generic TextPicture class) which only updates the image, but leaving the current location alone:
import datetime
import sys
import time
import pygame
class DigitalClock(pygame.sprite.Sprite):
def __init__(self, speed, location):
pygame.sprite.Sprite.__init__(self)
self.speed = speed
self.font = pygame.font.Font(None, 40)
self.rect = pygame.Rect(location, (0, 0)) # placeholder
self.update()
def update(self):
location = self.rect.left, self.rect.top # save position
time_text = datetime.datetime.now().strftime("%H:%M:%S")
self.image = self.font.render(time_text, 1, [0, 0, 0])
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location # restore position
def move(self):
self.rect = self.rect.move(self.speed)
if (self.rect.left < 0
or self.rect.left > screen.get_width()-self.image.get_width()):
self.speed[0] = -self.speed[0]
if (self.rect.top < 0
or self.rect.top > screen.get_height()-self.image.get_height()):
self.speed[1] = -self.speed[1]
Following that, you'd need to modify the processing to be something along these lines:
pygame.init()
framerate_clock = pygame.time.Clock()
screen = pygame.display.set_mode([640, 480])
my_digital_clock = DigitalClock([1, 1], [50, 50])
TICK_EVENT = pygame.USEREVENT + 1
pygame.time.set_timer(TICK_EVENT, 1000) # periodically create TICK_EVENT
while True:
for event in pygame.event.get():
if event.type == TICK_EVENT:
my_digital_clock.update() # call new method
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
screen.fill([255, 255, 255])
my_digital_clock.move()
screen.blit(my_digital_clock.image, my_digital_clock.rect)
framerate_clock.tick(60) # limit framerate
pygame.display.flip()
You could make it more beautiful by using a different font and color. Generally anything that made it look more realistic would be an improvement. It might be cool to make the colons between the digit characters blink on and off (using a similar technique).

pygame - blitting a moving background image causes extreme fps drop?

I'm making a Flappy Bird clone in PyGame and I'm trying to add a moving background image. I'm using a 800x600 image right now, and for some reason, when I blit the image, the performance suffers terribly. Is there something I'm doing wrong that is taking up a lot of resources?
import pygame, sys
import random
from pygame import *
pygame.init()
red = (255, 0, 0)
white = (255, 255, 255)
black = (0, 0, 0)
blue = (0, 0, 255)
green = (0, 255, 0)
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("flap it up")
clock = pygame.time.Clock()
MAX_VEL = 5
GAP = 175
WALLSPEED = -2
WALLEVENT = pygame.USEREVENT+1
SCOREEVENT = pygame.USEREVENT+2
pygame.time.set_timer(WALLEVENT, 5000)
pygame.time.set_timer(SCOREEVENT, 2500)
myfont = pygame.font.SysFont("monospace", 18)
class Player(object):
def __init__(self):
self.rect = pygame.Rect(384, 284, 32, 32)
self.xvel = 0
self.yvel = MAX_VEL
self.xcel = 0
self.ycel = 1
def move(self):
self.rect.top += self.yvel
if self.yvel < MAX_VEL:
self.yvel += self.ycel
else:
self.yvel = MAX_VEL
def jump(self):
self.yvel = -15
def draw(self):
pygame.draw.rect(screen, red, self.rect)
class Wall(object):
def __init__(self, y):
self.rect1 = pygame.Rect(800, y, 32, 600-y)
self.rect2 = pygame.Rect(800, 0, 32, y-GAP)
self.xvel = WALLSPEED
def move(self, walls):
self.rect1.x += self.xvel
self.rect2.x += self.xvel
if self.rect1.x < -31:
walls.remove(self)
def draw(self):
pygame.draw.rect(screen, green, self.rect1)
pygame.draw.rect(screen, green, self.rect2)
class BG(object):
def __init__(self, x):
self.x = x
self.xvel = WALLSPEED
self.img = pygame.image.load("bg.jpg")
def move(self, bg):
self.x += self.xvel
if self.x < -799:
bg.remove(self)
bg.append(BG(self.x+1600))
screen.blit(self.img, (self.x, 0))
def lose():
pygame.quit()
quit()
def main(player, walls, bg, score, playing):
while playing:
for event in pygame.event.get():
if event.type == KEYDOWN:
player.jump()
if event.type == WALLEVENT:
numbo = random.randrange(GAP, 600, 25)
walls.append(Wall(numbo))
if event.type == SCOREEVENT:
score += 1
for b in bg:
b.move(bg)
label = myfont.render("Score: " + str(score), 1, (0, 0, 0))
screen.blit(label, (30, 20))
player.move()
player.draw()
for w in walls:
w.move(walls)
w.draw()
if player.rect.colliderect(w.rect1) or player.rect.colliderect(w.rect2):
lose()
playing = False
clock.tick(60)
pygame.display.update()
player = Player()
walls = []
bg = []
score = 0
walls.append(Wall(300))
bg.append(BG(0))
bg.append(BG(800))
playing = True
main(player, walls, bg, score, playing)
Typically, this should not be a problem. You should try to set the FPS manually and see if there is a difference
# Create a clock
clock = pygame.time.Clock()
# Set FPS (frames per second)
clock.tick(50)

Categories