Adding gravity to a bouncing ball using vectors

I have a gravity vector (in the form [r, theta]) which I add to the ball's velocity vector. For some reason, the ball does not return to the same height after bouncing, but instead slowly loses height sporadically. I'm guessing there are some rounding errors or something in the calculation I'm using, but I can't isolate the problem.

Here is my code. You need both files and pygame to run it. Sorry if it's a little confusing. I can comment on something else if you want.

I added a marker whenever the ball reaches its maximum height, so you guys what I mean. I want the ball to return to a level height every time it bounces.

I took some unnecessary code. The complete program is under the pastebin links.


https://pastebin.com/FyejMCmg - PhysicsSim

import pygame, sys, math, tools, random, time
from pygame.locals import *

clock = pygame.time.Clock()
lines = []

class Particle:
    def __init__(self,screen,colour, mass, loc, vel):
        self.screen = screen
        self.colour = colour

        self.mass = mass

        self.x = loc[0]
        self.y = loc[1]
        self.location = self.x,self.y
        self.speed = vel[0]
        self.angle = vel[1]


    def update(self):
        global lines

        # add gravity
        self.speed,self.angle = tools.add_vectors2([self.speed,self.angle], tools.GRAVITY)

        # update position
        dt = clock.tick(60)
        self.x += self.speed * tools.SCALE * math.cos(self.angle) * dt
        self.y -= self.speed * tools.SCALE * math.sin(self.angle) * dt
        self.location = int(self.x),int(self.y)

        # border checking
        do = False
        n=[]
        if ((self.y+self.mass) > tools.SCREEN_HEIGHT):
            self.y = tools.SCREEN_HEIGHT-self.mass
            n = [0,1]
            do = True

        # adds position to array so max height so max height can be recorded
        if (self.speed==0):
            lines.append([self.screen, self.location, self.mass])

        # bounce
        if do:
            #init, convert everything to cartesian
            v = tools.polarToCartesian([self.speed, self.angle])

            #final -> initial minus twice the projection onto n, where n is the normal to the surface
            a = tools.scalarP(2*abs(tools.dotP(v,n)),n) #vector to be added to v
            v = tools.add_vectors(v,a)

            self.angle = tools.cartesianToPolar(v)[1] # does not set magnitude


        # drawing
        pygame.draw.circle(self.screen, self.colour, self.location, self.mass, 0)

# draws max height line
def draw_line(l):
    screen = l[0]
    location = l[1]
    radius = l[2]
    pygame.draw.line(screen, tools.BLACK, [location[0] + 15, location[1]-radius],[location[0] - 15, location[1]-radius])

def main():
    pygame.init()

    DISPLAY = pygame.display.set_mode(tools.SCREEN_SIZE,0,32)
    DISPLAY.fill(tools.WHITE)

    particles = []
    particles.append(Particle(DISPLAY, tools.GREEN, 10, [100,100], [0,0]))

    done = False
    while not done:
        global lines
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()

        DISPLAY.fill(tools.WHITE)
        for i in particles:
            i.update()

        for l in lines:
            draw_line(l)

        pygame.display.update()

main()

      


https://pastebin.com/Epgqka31 - tools

import math

#colours
WHITE =     (255, 255, 255)
BLUE =      (  0,   0, 255)
GREEN =     (  0, 255,   0)
RED =       ( 255,  0,   0)
BLACK =     (   0,  0,   0)

COLOURS = [WHITE,BLUE,GREEN,RED,BLACK]

#screen
SCREEN_SIZE = SCREEN_WIDTH,SCREEN_HEIGHT = 1000,700

#vectors
GRAVITY = [5.0, 3*math.pi/2] # not 9.8 because it seems too high
SCALE = 0.01

# converts polar coordinates to cartesian coordinates in R2
def polarToCartesian(v):
    return [v[0]*math.cos(v[1]), v[0]*math.sin(v[1])]

# converts cartesian coordinates to polar coordinates in R2
def cartesianToPolar(v):
    return [math.sqrt(v[0]**2 + v[1]**2), math.atan2(v[1],v[0])]

# dots two cartesian vectors in R2
def dotP(v1, v2):
    return v1[0]*v2[0] + v1[1]*v2[1]

# multiplies cartesian vector v by scalar s in Rn
def scalarP(s,v):
    v_=[]
    for i in v:
        v_.append(s*i)
    return v_

# returns the sum of two cartesian vectors in R2
def add_vectors(v1, v2):
    return [v1[0]+v2[0], v1[1]+v2[1]]

# returns the sum of two polar vectors in R2, equations from https://math.stackexchange.com/questions/1365622/adding-two-polar-vectors
def add_vectors2(v1,v2):
    r1,r2,t1,t2 = v1[0],v2[0],v1[1],v2[1]
    return [math.sqrt(r1**2 + r2**2 + 2*r1*r2*math.cos(t2-t1)), t1 + math.atan2(r2*math.sin(t2 - t1), r1 + r2*math.cos(t2 - t1))]

      

+3


source to share


1 answer


Your time interval is dt = clock.tick(60)

not constant. If you change it to dt = 60

, your program will work as expected.



Have a look at the Verlet Algorithm and embed it in your code. You are on the right track!

+5


source







All Articles