Search code examples
pythonphysicsgame-physics

Ball Physics problem Python


I am trying to make a ball bounce within a box correctly, specifically handling corners at specific angles and handling a corner head on. I have a problem, because my ball keeps coming out of the box. I have this function that tells if my ball is out of the box and it handles corners and walls. The code is this:

    if ((self._x > self._Grid.getWidth()) or (self._x < 0)):
        print("RandomNode:outside paramaters: x! self._x = %s , self._velx = %s" % (self._x , self._velx))
    if ((self._y > self._Grid.getLength())  or (self._y < 0)):
        print("RandomNode:outside paramaters: y!")
    if ((self._velx + self._x) > self._Grid.getWidth()):
        diff = self._Grid.getWidth()-self._x
        self._velx *= -1
        if (diff == 0):
            self._x -= self._velx
        else:
            self._x+= diff
        tampered = True
        #print("eqn1: self._x = %s , self._y = %s , self._velx= %s, self._vely= %s" % (self._x, self._y, self._velx, self._vely))

    if (self._velx + self._x < 0):
        diff = self._x
        self._velx *= -1
        if (diff == 0):
            self._x += self._velx
        else:
            self._x-= diff
        tampered = True
        #print("eqn2: self._x = %s , self._y = %s , self._velx= %s, self._vely= %s" % (self._x, self._y, self._velx, self._vely))


    if ((self._vely + self._y) > self._Grid.getLength()):
        diff = self._Grid.getLength()-self._y
        self._vely *= -1
        if (diff == 0):
            self._y -= self._vely
            if (tampered == True):
                if ((self._velx * -1 == self._vely) or (self._velx == self._vely)):
                    self._x += self._velx
                    self._y += self._vely
                    #print("eqn31:self._x = %s , self._y = %s , self._velx= %s, self._vely= %s" % (self._x, self._y, self._velx, self._vely))
                else:
                    self._x += (self._velx - diff)
                    self._y += self._vely
                    #print("eqn32:self._x = %s , self._y = %s , self._velx= %s, self._vely= %s" % (self._x, self._y, self._velx, self._vely))
            else:
                tampered = True
                #print("eqn33:self._x = %s , self._y = %s , self._velx= %s, self._vely= %s" % (self._x, self._y, self._velx, self._vely))


        else:
            self._y+= diff
            if (tampered == True):
                if ((self._velx * -1 == self._vely) or (self._velx == self._vely)):
                    self._x += self._velx
                    self._y += self._vely
                    #print("eqn31:self._x = %s , self._y = %s , self._velx= %s, self._vely= %s" % (self._x, self._y, self._velx, self._vely))
                else:
                    self._x += (self._velx - diff)
                    self._y += self._vely
                    #print("eqn32:self._x = %s , self._y = %s , self._velx= %s, self._vely= %s" % (self._x, self._y, self._velx, self._vely))
            else:
                tampered = True
                #print("eqn33:self._x = %s , self._y = %s , self._velx= %s, self._vely= %s" % (self._x, self._y, self._velx, self._vely))

    if (self._vely + self._y < 0):
        diff = self._y
        self._vely *= -1
        if (diff == 0):
            self._y += self._vely
            if (tampered == True):
                if ((self._velx * -1 == self._vely) or (self._velx == self._vely)):
                    self._x += self._velx
                    self._y += self._vely
                    #print("eqn41:self._x = %s , self._y = %s , self._velx= %s, self._vely= %s" % (self._x, self._y, self._velx, self._vely))
                else:
                    self._x += (self._velx + diff)
                    self._y += self._vely
                    #print("eqn42:self._x = %s , self._y = %s , self._velx= %s, self._vely= %s" % (self._x, self._y, self._velx, self._vely))
            else:
                tampered = True
                #print("eqn43:self._x = %s , self._y = %s , self._velx= %s, self._vely= %s" % (self._x, self._y, self._velx, self._vely))

        else:
            self._y-= diff
            if (tampered == True):
                if ((self._velx * -1 == self._vely) or (self._velx == self._vely)):
                    self._x += self._velx
                    self._y += self._vely
                    #print("eqn41:self._x = %s , self._y = %s , self._velx= %s, self._vely= %s" % (self._x, self._y, self._velx, self._vely))
                else:
                    self._x += (self._velx + diff)
                    self._y += self._vely
                    #print("eqn42:self._x = %s , self._y = %s , self._velx= %s, self._vely= %s" % (self._x, self._y, self._velx, self._vely))
            else:
                tampered = True
                #print("eqn43:self._x = %s , self._y = %s , self._velx= %s, self._vely= %s" % (self._x, self._y, self._velx, self._vely))
    return tampered

I don't know why it is not working. x and y are obviously its coordinates. Velx and Vely are its x and y velocities. Tampered is a Boolean that prevents the ball from moving normally and only being moved within check.

Here is my question. What is wrong with this code? OR....is there a template written in python somewhere on the net or where ever, or a code that you have used that does the exact thing I am trying to handle and do? Please revise the code at will, just let me know. Any links to this already solved would be great as well. Thanks.


Solution

  • Rewrite this code. It is way too complex for the simple problem you're trying to solve.

    First, a 2D motion of a ball is just 2 1D problems. You can completely separate X and Y. For example, hitting a corner is completely equivalent of hitting a wall in X axis + hitting a wall in Y axis. Hitting a wall in X just reverses the X velocity (and possibly loses some energy if you want to simulate that). Hitting a wall in Y reverses Y velocity.

    Second, since the handling of X and Y is so similar, extract a method out of it

    def handle_axis_movement(location, velocity):
        "returns updated location and velocity"
        ...
    
    self.x, self.vel_x = handle_axis_movement(self.x, self.vel_x)
    self.y, self.vel_y = handle_axis_movement(self.y, self.vel_y)
    

    This will cut the amount of code (and bugs) in half.

    Third, you don't have to treat diff==0 and diff<0 seperately. Both cases mean the ball hit a wall, and should reverse its velocity. Then you should correct the location to account for the fact that it couldn't have passed through the wall.

    location += velocity
    if location > max_bound:
        location = max_bound - (location - max_bound)
        velocity *= -1
    if location < min_bound:
        location = min_bound - (location - min_bound)
        velocity *= -1