# Unity like RigidBody physics in PyGame

I am trying to make game but first I want to make simple simulator. The goal is to have a ball fall onto a surface and then roll off the edge.

Here is my code:

import os,sys, math,pygame, pygame.mixer
from pygame.locals import *
import random
import euclid
import pdb
import math

#Defining some basic colors

black = 0, 0, 0
white = 255, 255, 255
red = 255, 0, 0
green = 0, 255, 0
blue = 0, 0, 255

colors = [black, red, green, blue]

gravity = euclid.Vector2(0.0,80.0)
drag = 0.1
initial_velocity = 20

#Defining the screen size
screen_size = screen_width, screen_height = 600, 400

class MyCircle:
def __init__(self, position , size, color = (255, 255, 255),velocity=euclid.Vector2(0,0), accel=euclid.Vector2(0,0),width = 1):
self.position=position
self.size=size
self.color = color
self.width = width
self.velocity=velocity
self.accel = accel
#__set_initial_velocity()

def display(self):
rx, ry = int(self.position.x), int(self.position.y)
pygame.draw.circle(screen,self.color,(rx,ry), self.size,self.width)

def move(self):
self.position += self.velocity * dtime + 0.5*(self.accel * (dtime **2))
self.velocity += self.accel * dtime
#self.velocity -= self.velocity * drag * dtime
#self.bounce()

def change_velocity(self, velocity):
self.velocity = velocity

def bounce(self):
if self.position.x <= self.size:
self.position.x = 2*self.size-self.position.x
self.velocity = self.velocity.reflect(euclid.Vector2(1,0))
elif self.position.x >= screen_width - self.size:
self.position.x = 2*(screen_width - self.size) - self.position.x
self.velocity = self.velocity.reflect(euclid.Vector2(1,0))
if self.position.y <= self.size:
self.position.y = 2*self.size - self.position.y
self.velocity = self.velocity.reflect(euclid.Vector(0,1))
elif self.position.y >= screen_height - self.size:
self.position.y = 2*(screen_height - self.size) - self.position.y
self.velocity = self.velocity.reflect(euclid.Vector2(0,1))

#Equation for distance between surfaces
#d(t) = |A(t) - B(t)| - (Ra + Rb)
def surface_distance(self, other, time):
posA = self.position + self.velocity * time + 0.5*(self.accel * (time**2))
posB = other.position + other.velocity * time + 0.5*(other.accel * (time**2))
posAB = abs(posA-posB)

def collide(self,other):
if self.surface_distance(other,dtime) <= 0:
collision_vector = self.position - other.position
collision_vector.normalize()
self.velocity = self.velocity.reflect(collision_vector)
other.velocity = other.velocity.reflect(collision_vector)

class MySurface():
def __init__(self, size, color = (255, 255, 255),angle=math.pi/2,width = 0):
self.theta=angle
self.position1=[50,100]
self.position2=[self.position1[0]+50*math.cos(self.position1[0]),
self.position1[1]+50*abs(math.sin(self.position1[1]))]
self.color = color
self.width = width
self.accel = euclid.Vector2(gravity.y*math.cos(self.theta),-1*gravity.y*math.sin(self.theta))
#pdb.set_trace()

def display(self):
line = pygame.draw.aaline(screen, (0,0,0), self.position1,self.position2,5)

def surface_distance(self,circ, time):
theta=self.theta
pos_ball = circ.position+circ.velocity*time+0.5*(circ.accel*(time**2))
#map coordinates to surface that the ball is going to contact
ball_y = self.position1[1]-(-1*pos_ball.x*math.sin(theta) + pos_ball.y*math.cos(theta))
#map the actual surface to its own coordinate system
return ydelta

def collide(self,circ,counter):
ydelta = self.surface_distance(circ,dtime)
if ydelta <= circ.size:
circ.accel=self.accel # readjust acceleration based on angle of line
if counter<1:
circ.velocity=euclid.Vector2(0,0)
counter=counter+1
else:
counter+=1
return counter
#     pdb.set_trace()

#Setting the display and getting surface object
screen = pygame.display.set_mode(screen_size)
#Getting clock object
clock = pygame.time.Clock()
#Setting a title to the window
pygame.display.set_caption('First Class!')

number_of_circles = 10
my_circles = []
#create ball
size = 10
color = colors[0]
my_circle = MyCircle(euclid.Vector2(70,40), size, color, euclid.Vector2(0,0),gravity)
my_circle.velocity = euclid.Vector2(0,10)
my_circle.velocity.normalize()

#Create surface
my_surface = MySurface(screen,color,angle)

direction_tick = 0.0
counter = 0;
#Defining variables for fps and continued running
fps_limit = 60
run_me = True
while run_me:
#Limit the framerate
clock.tick(fps_limit)

for event in pygame.event.get():
if event.type == pygame.QUIT:
run_me = False

#Limit the framerate
dtime_ms = clock.tick(fps_limit)
dtime = dtime_ms/1000.0

#Clear the screen
screen.lock()
screen.fill(white)

counter=my_surface.collide(my_circle,counter)
my_circle.move()
my_circle.display()
my_surface.display()
screen.unlock()
#Display everything on the screen
pygame.display.flip()

#Quit the game
pygame.quit()
sys.exit()


Everything starts out fine as shown below:

Once you let the game go on the ball keeps going in the same direction:

Now I know I can adjust acceleration vectors again once the ball reaches the edge of the surface, but is there something in pygame that already does the appropriate physics for me?

I.E. In Unity there is a concept of RigidBody and the ball hits the RigidBody and rolls off of it and once it reaches the edge of the body the ball changes acceleration.

Here is the link for euclid: https://github.com/xocoatzin/pyeuclid/blob/master/euclid.py

• Sounds to me like your gravity acceleration is too weak, so it's not deflecting the ball's path as much as you expect after it rolls off the edge. Have you tried increasing your acceleration due to gravity? – DMGregory Jan 7 '19 at 12:05
• You should use pyBox2D – Juanpa Jan 7 '19 at 17:28
• Based on your title, it sounds like you are switching from Unity. Out of curiosity, may I ask why? – Evorlor Jan 8 '19 at 10:07
• @Juanpa I will look into pyBox2D – ahat Jan 9 '19 at 15:54
• @Evorlor I am switching from Unity because: 1) When I am designing my game I want to make various levels by simulating different solutions using physics simulation (I don't know if unity can already do that?) 2) I run into issues when using Linux on Unity 3)I know Python pretty well so it would be a easier to work with (but I could be wrong). I can try Unity again if I can simulate various solutions for different levels using a physics and try to get around #2 #3. – ahat Jan 9 '19 at 16:04