Collision between two circle in pygame [duplicate] - python-3.x

This question already has answers here:
Pygame how to let balls collide
(2 answers)
pygame Get the balls to bounce off each other
(1 answer)
Closed 2 years ago.
I am trying to create my first game, where I have successfully added a bouncing ball and another ball around the mouse-pointer. The collision between these two circles works fine except sometimes the circles intersects each other hugely and the collision does not occur in a single frame, and some strange behaviour thereafter. How do I fix this?
import pygame
from pygame.locals import *
import numpy as np
# Define the colors we will use in RGB format
BLACK = ( 0, 0, 0)
WHITE = (255, 255, 255)
BLUE = ( 0, 0, 255)
GREEN = ( 0, 255, 0)
RED = (255, 0, 0)
width = 800
height = 600
radius = 40
mRadius = 60
base = 20
g = 0.2
pygame.init()
screen = pygame.display.set_mode((width,height))
pygame.display.set_caption('Testing')
clock = pygame.time.Clock()
class Circle:
def __init__(self):
self.pos = np.array([100,100])
self.velocity = np.array([2,0])
def upgrade(self,mouse):
self.pos = np.add(self.velocity,self.pos)
if ((self.pos[0] > width - radius - 1) or (self.pos[0] < 0 + radius)):
self.velocity[0] *= -1
if self.pos[1] > height - radius - base - 1:
self.velocity[1] *= -1
#print(str(self.velocity[1])+"\t"+str(self.pos[1]))
else:
self.velocity = np.add(self.velocity,[0,g])
if ((radius + mRadius)**2 >= ((self.pos[0] - mouse[0])**2 +(self.pos[1] - mouse[1])**2)):
self.pos = np.subtract(self.pos,self.velocity)
lx = self.pos[0] - mouse[0]
ly = self.pos[1] - mouse[1]
A = (lx**2 - ly**2)/(lx**2 + ly**2)
B = 2*lx*ly/(lx**2 + ly**2)
M = np.array([[A,B],[B,(-1)*A]])
self.velocity = list((-1)*M.dot(self.velocity))
print(str(lx)+"\t"+str(ly)+"\t"+str(mouse))
pygame.draw.circle(screen, GREEN, [int(self.pos[0]),int(self.pos[1])], radius)
return True
def main():
mouse = [100,100]
running = True
ball = Circle()
while running:
(mouse[0],mouse[1]) = pygame.mouse.get_pos()
screen.fill(WHITE)
pygame.draw.circle(screen, BLUE, mouse, mRadius)
running = ball.upgrade(mouse)
pygame.draw.rect(screen,RED,(0,height-base,width,base))
pygame.display.update()
clock.tick(120)
for event in pygame.event.get():
if event.type == QUIT:
running = False
if event.type == KEYDOWN and event.key == K_ESCAPE:
running = False
pygame.display.quit()
if __name__ == '__main__':
main()
Here is the github link

Related

I am not sure how I add gravity to my game?

I am confused on how I add gravity to my platformer game. I am using pygame for this project. I am relatively new to coding, but anyways here's my code.
import pygame
pygame.init()
win = pygame.display.set_mode((800, 500))
pygame.display.set_caption("Platformer")
x = 200
y = 200
width = 20
height = 20
vel = 5
run = True
FPS = 30
fpsClock = pygame.time.Clock()
while run:
pygame.time.delay(30)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
if keys[pygame.K_a] and x > 0:
x -= vel
if keys[pygame.K_d] and x < 800 - width:
x += vel
if keys[pygame.K_w] and y > 0:
y -= vel
if keys[pygame.K_s] and y < 500 - height:
y += vel
win.fill((0, 0, 0))
pygame.draw.rect(win, (255, 0, 0), (x, y, width, height))
pygame.display.update()
pygame.display.update()
fpsClock.tick(FPS)
I hope someone can help me get some sort of gravity function working, anything helps. (also what is the best way to make pixelated art, not very relevant to the question)
Gravity just means that the object is moved down a little in each frame:
gravity = 3
while run:
# [...]
y += gravity
if y > win.get_height() - 20:
y = win.get_height() - 20
I suggest to use a pygame.Rect object to represent the player:
import pygame
pygame.init()
win = pygame.display.set_mode((800, 500))
pygame.display.set_caption("Platformer")
player = pygame.Rect(200, 200, 20, 20)
vel = 5
gravity = 3
run = True
FPS = 30
fpsClock = pygame.time.Clock()
while run:
fpsClock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
player.x += (keys[pygame.K_d] - keys[pygame.K_a]) * vel
if player.left < 0:
player.left = 0
if player.right > win.get_width():
player.right = win.get_width()
player.y += gravity
if player.bottom > win.get_height():
player.bottom = win.get_height()
win.fill((0, 0, 0))
pygame.draw.rect(win, (255, 0, 0), player)
pygame.display.update()
pygame.quit()

Trying to get ball to return to middle after ball has left screen

I'm trying to recreate a game of pong just for fun.
Here is my full code as of right now.
import pygame, random, math
pygame.init()
#colours:-------------------------------------------------------------
R = random.randrange(1,255)
B = random.randrange(1,255)
G = random.randrange(1,255)
WHITE = (255, 255, 255)
GREEN = (39, 133, 20)
YELLOW = (252, 252, 25)
BLACK = (0, 0, 0)
BLUE = (30, 100, 225)
RED = (255,0,0)
RANDOM_COLOR = (R, B, G)
#Surface:-------------------------------------------------------------
width = 700
height = 600
size = (width, height)
screen = pygame.display.set_mode(size)
screen_rect = screen.get_rect()
pygame.display.set_caption("Pong Remake")
background = pygame.image.load("background.png").convert()
background = pygame.transform.scale(background, (width, height))
logo = pygame.image.load("logo.png").convert()
logo.set_colorkey((BLACK))
credits = pygame.image.load("credits.png")
credits.set_colorkey((BLACK))
#variables:-----------------------------------------------------------
clock = pygame.time.Clock()
done = False
text = pygame.font.Font(None,25)
display_instructions = True
instruction_page = 1
start_font = pygame.font.Font("C:\Windows\Fonts\BAUHS93.TTF", 35)
instruction_font = pygame.font.Font(None, 17)
win_lose_font = pygame.font.Font("C:\Windows\Fonts\BAUHS93.TTF",50)
score = pygame.font.Font(None, 100)
bounce = pygame.mixer.Sound("bounce.wav")
playerOne_score = 0
playerTwo_score = 0
playerOne = ""
playerTwo = ""
x = 350
y = 300
ball_rect = pygame.Rect(x,y,10,10)
paddleOne_rect = pygame.Rect(10, 250, 20, 60)
paddleTwo_rect = pygame.Rect(670, 250, 20, 60)
x_speed = random.randrange(5, 10)
y_speed = random.randrange(5,10)
def draw_background(screen, pic, x,y):
screen.blit(pic, (x,y))
#main loop
#INPUT v ---------------------------------------------------------
#Start Page
while not done and display_instructions:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == pygame.MOUSEBUTTONDOWN:
instruction_page += 1
if instruction_page == 2:
display_instructions = False
#Shows the start of the page
if instruction_page == 1:
draw_background(screen, logo, 100,-150)
draw_background(screen, credits, 100,50)
instruction_text = instruction_font.render("How to Play. The objective to this game is to score the ball on the other side before the opponent can.", False, WHITE)
instruction_text_three = instruction_font.render("First Player to get 10 points wins, Have Fun and Good Luck!", False, WHITE)
instruction_text_two = instruction_font.render("For Player One, use the a and the z keys to move up and down, For Player Two, use the k and m keys.", False, WHITE)
continue_text= start_font.render("Click to Play...",True, WHITE)
screen.blit(continue_text, [200, 400])
screen.blit(instruction_text, [0,500])
screen.blit(instruction_text_three, [0,532])
screen.blit(instruction_text_two,[0,516])
if instruction_page == 2:
display_instructions = False
clock.tick(60)
pygame.display.flip()
while not done:
click = False
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.MOUSEBUTTONDOWN:
click = True
#INPUT ^ =========================================================
#PROCESS v -------------------------------------------------------
str(playerOne_score)
str(playerTwo_score)
scoreOne = text.render("Player One:" + str(playerOne_score), False, WHITE)
scoreTwo = text.render("Player Two:" + str(playerTwo_score), False, WHITE)
#moves paddles with keys on keyboar
key = pygame.key.get_pressed()
if key[pygame.K_a]: paddleOne_rect.move_ip(0, -10)
if key[pygame.K_z]: paddleOne_rect.move_ip(0, 10)
if key[pygame.K_k]: paddleTwo_rect.move_ip(0, -10)
if key[pygame.K_m]: paddleTwo_rect.move_ip(0, 10)
#makes sure paddles stay on screen
paddleOne_rect.clamp_ip(screen_rect)
paddleTwo_rect.clamp_ip(screen_rect)
ball_rect.move_ip(x_speed, y_speed)
if ball_rect.y + ball_rect.height> screen_rect.height or ball_rect.y < 0:
y_speed = y_speed * -1
bounce.play()
if ball_rect.collidelist([paddleOne_rect, paddleTwo_rect]) > -1:
x_speed = -x_speed
R = random.randrange(1,255)
B = random.randrange(1,255)
G = random.randrange(1,255)
bounce.play()
if ball_rect.x >= 700:
x_speed * -1
playerOne_score += 1
pygame.display.flip
if ball_rect.x <= 0:
x_speed * -1
playerTwo_score += 1
#PROCESS ^ =======================================================
#DRAWING GOES BELOW HERE v ------------------------------------
draw_background(screen, background, 0,0)
screen.blit(scoreOne, (0,0))
screen.blit(scoreTwo, (500,0))
pygame.draw.ellipse(screen, WHITE,ball_rect )
pygame.draw.rect(screen,RANDOM_COLOR, paddleOne_rect)
pygame.draw.rect(screen,RANDOM_COLOR, paddleTwo_rect)
pygame.draw.line(screen, WHITE, (350,0),(350,700), 1)
#DRAWING GOES ABOVE HERE ^ ------------------------------------
pygame.display.flip()
clock.tick(60)
pygame.quit()
What I am currently having problems with at the moment is when the ball goes off the screen, I want it to go back to the middle again as someone has scored a point. But I'm a bit stuck on what to do.
If you guys can help me out, that would be amazing!!
There is a lot of code here, so this does not follow specific variables you used, but I hope this helps.
1) Find the width of you screen
2) Take the x and y coordinates that you use to know where to draw the ball
3) Make an if statement that essentially says
(pseudocode)
if x > 1000
score1 += 1
x = 500
if x < 0
score2 += 1
x = 500
``
I hope this can set you on the right track, and I suggest checking out the pygame docs.
Cheers!

Why is the circle moving in a straight line instead of at an angle in pygame?

I am learning the basics of pygame in my free time at work. I wanted to move the bottom boundary of my program up, but when I changed the boundary collision condition, the ball moves in a straight line instead of at an angle.
When I delete the 525 part of the condition in the bounceCircle definition, it moves as expected. When I place it back, it moves in a horizontal line.
import pygame
import sys
import random
import math
# Initalize the game engine
pygame.init()
# Define common colors:
WHITE = (255, 255, 255)
BLUE = (0, 0, 255)
# Set window size, title, and background color
(width, height) = (900, 600)
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("Ball Playground")
screen.fill(WHITE)
# Used to manage how fast the screen updates
clock = pygame.time.Clock()
# Ball class
class Particles():
def __init__(self, position, radius):
self.x = position[0]
self.y = position[1]
self.radius = radius
self.color = (BLUE)
# thickness = 0 means filled
# thickness > 0 thicker border
# thickness < 0 nothing
self.thickness = 1
self.speed = 0
self.angle = 0
# Definition for drawing circle
def drawCircle(self):
pygame.draw.circle(screen, self.color, (int(self.x), int(self.y)), self.radius, self.thickness)
# Definition for moving the circle
def moveCircle(self):
self.x += math.sin(self.angle) * self.speed
self.y -= math.cos(self.angle) * self.speed
# Definition for bouncing off of surfaces
def bounceCircle(self):
if (self.x > width - self.radius) or (self.x < self.radius):
self.angle = - self.angle
elif (self.y > height - self.radius) or (self.y < 525 - self.radius):
self.angle = math.pi - self.angle
ball = Particles((450, 300), 40)
ball.speed = 2
ball.angle = random.uniform(0, math.pi*2)
# --------- Main Program Loop ----------
while True:
# --- Main Event Loop
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
pygame.display.update()
screen.fill(WHITE)
#--- Game Logic
ball.moveCircle()
ball.bounceCircle()
ball.drawCircle()
#--- Drawings
pygame.draw.line(screen, BLACK, [0, 525], [900, 525], 2)
# Prints tiny diaginal lines to mark surface
x1 = 0
x2 = 5
for i in range(0, width):
pygame.draw.line(screen, BLACK, [x1, 530], [x2, 525], 2)
x1 += 5
x2 += 5
pygame.display.flip()
clock.tick(60)
You need to edit the bounceCircle function to:
def bounceCircle(self):
if (self.x + self.radius > width ) or (self.x - self.radius < 0):
self.angle = - self.angle
elif (self.y + self.radius > (525)) or (self.y - self.radius < 0):
self.angle = math.pi - self.angle
Whole Code (fixed a few bugs):
import pygame
import sys
import random
import math
# Initalize the game engine
pygame.init()
# Define common colors:
WHITE = (255, 255, 255)
BLUE = (0, 0, 255)
# Set window size, title, and background color
(width, height) = (900, 600)
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("Ball Playground")
screen.fill(WHITE)
# Used to manage how fast the screen updates
clock = pygame.time.Clock()
# Ball class
class Particles():
def __init__(self, position, radius):
self.x = position[0]
self.y = position[1]
self.radius = radius
self.color = (BLUE)
# thickness = 0 means filled
# thickness > 0 thicker border
# thickness < 0 nothing
self.thickness = 1
self.speed = 0
self.angle = 0
# Definition for drawing circle
def drawCircle(self):
pygame.draw.circle(screen, self.color, (int(self.x), int(self.y)), self.radius, self.thickness)
# Definition for moving the circle
def moveCircle(self):
self.x += math.sin(self.angle) * self.speed
self.y -= math.cos(self.angle) * self.speed
print(self.angle, self.x, self.y)
# Definition for bouncing off of surfaces
def bounceCircle(self):
if (self.x > width - self.radius) or (self.x < self.radius):
self.angle = - self.angle
elif (self.y + self.radius > (height-100)) or (self.y - self.radius < 0):
self.angle = math.pi - self.angle
ball = Particles((450, 300), 40)
ball.speed = 20
ball.angle = random.uniform(0, math.pi*2)
print(ball.angle)
# --------- Main Program Loop ----------
while True:
# --- Main Event Loop
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
pygame.display.update()
screen.fill(WHITE)
#--- Game Logic
ball.moveCircle()
ball.bounceCircle()
ball.drawCircle()
#--- Drawings
pygame.draw.line(screen, BLUE, [0, 525], [900, 525], 2)
# Prints tiny diaginal lines to mark surface
x1 = 0
x2 = 5
for i in range(0, width):
pygame.draw.line(screen, BLUE, [x1, 530], [x2, 525], 2)
x1 += 5
x2 += 5
pygame.display.flip()
clock.tick(60)

What is the most efficient way of drawing a grid with Pygame?

Is there a significant difference in term of performance drawing one rectangle for every cell of the grid compared to drawing the grid using horizontal and vertical lines ?
Yes, using lines drawing is one or more order of magnitude faster than drawing rectangles. The code below times both methods.
import pygame
import os
import time
TITLE = "Draw grid"
WINDOWS_LOCATION = '100,100'
WIDTH = 710
HEIGHT = 710
FPS = 1
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
CELL_SIZE = 15
GRID_COORD_MARGIN_SIZE = 20
class Grid():
def __init__(self, surface, cellSize, axisLabels):
self.surface = surface
self.colNb = surface.get_width() // cellSize
self.lineNb = surface.get_height() // cellSize
self.cellSize = cellSize
self.axisLabels = axisLabels
self.grid = [[0 for i in range(self.colNb)] for j in range(self.lineNb)]
self.font = pygame.font.SysFont('arial', 12, False)
def drawUseRect(self):
for li in range(self.lineNb):
liCoord = GRID_COORD_MARGIN_SIZE + li * CELL_SIZE
if self.axisLabels:
if li < 10:
ident = ' '
else:
ident = ' '
text = self.font.render(ident + str(li), 1, (0, 0, 0))
self.surface.blit(text, (0, liCoord))
for co in range(self.colNb):
colCoord = GRID_COORD_MARGIN_SIZE + co * CELL_SIZE
if self.axisLabels:
if co < 10:
ident = ' '
else:
ident = ' '
text = self.font.render(ident + str(co), 1, (0, 0, 0))
self.surface.blit(text, (colCoord, 1))
pygame.draw.rect(self.surface, BLACK, pygame.Rect(liCoord, colCoord, CELL_SIZE, CELL_SIZE), 1)
def drawUseLine(self):
for li in range(self.lineNb):
liCoord = GRID_COORD_MARGIN_SIZE + li * CELL_SIZE
if self.axisLabels:
if li < 10:
ident = ' '
else:
ident = ' '
text = self.font.render(ident + str(li), 1, (0, 0, 0))
self.surface.blit(text, (0, liCoord))
pygame.draw.line(self.surface, BLACK, (GRID_COORD_MARGIN_SIZE, liCoord), (self.surface.get_width(), liCoord))
for co in range(self.colNb):
colCoord = GRID_COORD_MARGIN_SIZE + co * CELL_SIZE
if self.axisLabels:
if co < 10:
ident = ' '
else:
ident = ' '
text = self.font.render(ident + str(co), 1, (0, 0, 0))
self.surface.blit(text, (colCoord, 1))
pygame.draw.line(self.surface, BLACK, (colCoord, GRID_COORD_MARGIN_SIZE), (colCoord,self.surface.get_height()))
# setting Pygame window position (ok on Windows ...)
os.environ['SDL_VIDEO_WINDOW_POS'] = WINDOWS_LOCATION
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption(TITLE)
clock = pygame.time.Clock()
running = True
axisLabels = False
grid = Grid(surface=screen, cellSize=CELL_SIZE, axisLabels=axisLabels)
while running:
clock.tick(FPS)
for event in pygame.event.get():
# check for closing window
if event.type == pygame.QUIT:
running = False
screen.fill(WHITE)
start = time.time()
grid.drawUseRect()
print("draw using rect: {}".format(time.time() - start))
start = time.time()
grid.drawUseLine()
print("draw using line: {}".format(time.time() - start))
# *after* drawing everything, flip the display
pygame.display.flip()
pygame.quit()
Here's a example of the timing output on a Windows 10 tablet pc:
draw using rect: 0.04449152946472168
draw using line: 0.0008492469787597656
draw using rect: 0.012907981872558594
draw using line: 0.0
draw using rect: 0.027329683303833008
draw using line: 0.002046346664428711
draw using rect: 0.019585847854614258
draw using line: 0.0009150505065917969
draw using rect: 0.022649049758911133
draw using line: 0.0009458065032958984
draw using rect: 0.022264480590820312
draw using line: 0.0017306804656982422
draw using rect: 0.022340059280395508
draw using line: 0.0009708404541015625
draw using rect: 0.04112124443054199
draw using line: 0.0019001960754394531
And here's a comparison of the visual result of drawing the grid (variable axisLabel set to True):

My player sprite keeps on disappearing if it lands on the platform

I am trying to build my first Platformer Game. So far, I have make moving left and right but unfortunately, I have encountered a error that I have not been able to fix when I implemented collisions and gravity. My player keeps on dissolving like spider man if it lands on the platform. The character is still existent, and lands on the platform, but unfortunately, he becomes invisible. There is no error message, and I suspect is has to do with the collision check.
hits = pygame.sprite.spritecollide(object, allPlatforms, False)
if hits:
object.rect.y = hits[0].rect.top + 1
object.vy = 0
print(object.rect.midbottom)
It prints out the Players location in the code, and the player is still existent and movable, but it just doesn't show. Is there something that I did that makes the character vanish?
import pygame
import random
WIDTH = 500
HEIGHT = 400
FPS = 30
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
playerImage = "blockBandit/BlockBandit.png"
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((50, 50))
self.image = pygame.image.load(playerImage).convert()
self.rect = self.image.get_rect()
self.rect.center = (WIDTH / 2, HEIGHT / 2)
self.vx = 0
self.vy = 0
class Platform(pygame.sprite.Sprite):
def __init__(self, x, y, w, h):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((w, h))
self.image.fill(GREEN)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
pygame.init()
pygame.mixer.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Block Bandit")
clock = pygame.time.Clock()
allPlatforms = pygame.sprite.Group()
all_sprites = pygame.sprite.Group()
player = Player()
all_sprites.add(player)
p1 = Platform(0, HEIGHT - 40, WIDTH, 40)
all_sprites.add(p1)
allPlatforms.add(p1)
def moveCharacter(object):
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
object.vx += -2
if keys[pygame.K_RIGHT]:
object.vx += 2
object.vx = object.vx * 0.9
if (abs(object.vx) < 1):
object.vx = 0
if (abs(object.vx) > 10):
if(object.vx < 0):
object.vx = -10
else:
object.vx = 10
object.vy = object.vy + 1
object.rect.x += object.vx
object.rect.y += object.vy
hits = pygame.sprite.spritecollide(object, allPlatforms, False)
if hits:
object.rect.y = hits[0].rect.top + 1
object.vy = 0
print(object.rect.midbottom)
running = True
while running:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
moveCharacter(player)
#Update State
all_sprites.update()
#Render
screen.fill(BLACK)
all_sprites.draw(screen)
#screen.blit(player.icon, (20, 40))
pygame.display.flip()
pygame.quit()
Am I doing something wrong? Thanks!
The y attribute of the rect is the same as the top coordinate, so you're setting the top of the player sprite to the top of the platform sprite here object.rect.y = hits[0].rect.top + 1. And if the platform comes later in the sprite group, it will be blitted after the player and the player won't be visible.
Just change that line to object.rect.bottom = hits[0].rect.top + 1.

Resources