I was working on an old assignment (2010) for fun. Also because I was really stuck on it the first time and as of this year, programming has now clicked. Because of this, I have been doing little coding exercises and have been revisiting old assignments to see if I could easily write out the code. I have most of it down, but there is a little glitch.
from graphics import * from math import * import random W = 300 H = 300 ballList =  squareList =  #---------------------------------------------------------------- def simul(win, ballList): for step in range( 200 ): for i in range(len(ballList)): c, dx, dy = ballList[i] x = c.getCenter().getX() y = c.getCenter().getY() r = c.getRadius() if (x <= 0 + r ) or (x >= W - r): dx = -dx if (y <= 0 + r) or(y >= H - r): dy = -dy # stop the ball's movement if isInSquare(x, y, r): dx = 0 dy = 0 # make it bounce off the black box's bounds if hitBlackBox(x, y, r): dx = -dx dy = -dy (boolVal, ball1, ball2) = hasCollided(ballList) # create a tuple if boolVal == True: # change the dx and dy of each ball that satisfies condition # then reverse the dx, dy of collided balls c1, dx1, dy1 = ball1 c2, dx2, dy2 = ball2 # replace old ballList values with current ballList values for list in ballList: if ball1 in list: dx1 = -dx1 dy1 = -dy1 list.pop(1) list.insert(1, dx1) list.pop(2) list.insert(2, dy1) if ball2 in list: dx2 = -dx2 dy2 = -dy2 list.pop(1) list.insert(1, dx2) list.pop(2) list.insert(2, dy2) c1.move(dx1, dy1) c2.move(dx2, dy2) # boolVal = False # need something of the sort else: c.move(dx, dy) ballList[ i ] = [ c, dx, dy ] # update ball values if allInBoxes(ballList): end(win) def allInBoxes(ballList): counter = 0 for i in range(len(ballList)): c, dx, dy = ballList[i] if (dy == dx == 0): counter += 1 if counter == len(ballList): return True return False def hasCollided(ballList): for i in range(len(ballList)-1): for j in range(i+1, len(ballList)): c1 = ballList[i] c2 = ballList[j] # get the dx1-2, dy1-2 vals dx1 = ballList[i] dy1 = ballList[i] dx2 = ballList[j] dy2 = ballList[j] # get radius of i and j balls r1 = c1.getRadius() r2 = c2.getRadius() # now get the points P1 = Point(c1.getCenter().getX(), c1.getCenter().getY()) P2 = Point(c2.getCenter().getX(), c2.getCenter().getY()) #print "(", P1.getX(), ",",P1.getY(),")", "(" , P2.getX() , ",",P2.getY(),")", "This is P1, P2" dist = distance(P1, P2) if((r1 + r2) >= dist >= 0): ## if dist < (r1 + r2): ## largeDist = False ## while largeDist == False: ## if dist < (r1 + r2): ## if(dx1 < 0 and dx2 < 0) and (dy1 < 0 and dy2 < 0) or\ ## (dx1 > 0 and dx2 > 0) and (dy1 > 0 and dy2 > 0): ## #dist < (r1 + r2) ## # keep pushing by -dx1-2, -dy1-2 values ## c1.move(-dx1, -dy1) ## c2.move(dx2, dy2) ## P1 = Point(c1.getCenter().getX(), c1.getCenter().getY()) ## P2 = Point(c2.getCenter().getX(), c2.getCenter().getY()) ## dist = distance(P1, P2) ## else: ## largeDist = True return True, ballList[i], ballList[j] return False, 0, 0 def distance(P1, P2): return sqrt( pow( P1.getX() - P2.getX(), 2 ) + pow( P1.getY() - P2.getY(), 2 )) def isInSquare(x, y, r): for i in range(len(squareList)-1): # exclude black box sP1 = squareList[i].getP1() # (bOriginX, bOriginY) sP2 = squareList[i].getP2() # (bW, bH) if (sP2.getX() - r >= x >= sP1.getX() + r) and (sP2.getY() - r >= y >= sP1.getY() + r): return True def hitBlackBox(x, y, r): # get last box blackBox = squareList[-1] sP1 = blackBox.getP1() # (bOriginX, bOriginY) | square P1 sP2 = blackBox.getP2() # (bW, bH) | square P2 if (sP2.getX() + r >= x >= sP1.getX() - r) and (sP2.getY() + r >= y >= sP1.getY() - r): return True def drawSquare(win): for i in range(4): # create 3 white boxes with random coords bOriginX = random.randrange(0, W-50, 51) bOriginY = random.randrange(0, H-100, 51) bW = bOriginX + 50 # you have to include origin + width therefore 100-50 = 50 sBoxW bH = bOriginY + 50 square = Rectangle(Point(bOriginX, bOriginY), Point(bW, bH)) square.draw(win) square.setFill("white") squareList.append(square) squareList[-1].setFill("black") # Fill last square drawn with black # this commented portion can work too, remove the previous line, set loop range(3) ## blackSquare = Rectangle(Point(H-50,W-50), Point((W-50)+50, (H-50)+ 50)) ## blackSquare.draw(win) ## blackSquare.setFill("black") ## squareList.append(blackSquare) # ------------------------------------------- def end(win): waitForClick( win, "Click to End" ) win.close() #---------------------------------------------------------------- def waitForClick( win, message ): """ waitForClick: stops the GUI and displays a message. Returns when the user clicks the window. The message is erased.""" # wait for user to click mouse to start startMsg = Text( Point( win.getWidth()/2, win.getHeight()/2 ), message ) startMsg.draw( win ) # display message win.getMouse() # wait startMsg.undraw() # erase #---------------------------------------------------------------- def main(): win = GraphWin( "moving ball", W, H ) #--- define a ball position and velocity --- drawSquare(win) ballColor = ["red", "blue", "pink", "purple", "green"] for i in range(2): c = Circle( Point( random.randrange( 16, W-15, 20 ), random.randrange( H/3, 2*H/3, 20 ) ), 15 ) c.setFill(ballColor[i%len(ballColor)]) c.draw(win) ballList.append([c, 5 - random.randrange(10), 5 - random.randrange(10)]) for i in range(len(ballList)): # loop: no ball should be 0 dx and 0 dy if ballList[i] == ballList[i] == 0: ballList[i] == 1 waitForClick( win, "Click to Start" ) simul(win, ballList) end(win) main()
Graphics Module Link Here
The program loads some balls of varying colors. The program waits for the user to click to start, the balls bounce off the edge of the screen, each other, and a black box. There are 4 boxes on the screen loaded at random positions. Three of them stop the ball's movement if the ball is inside of the bounds of the box. The last one (black box) acts as an obstacle and the ball bounces off the black box upon collision. The program ends at the end of loop or if all balls are in a white box.
I am having problems with the collision detection function called hasCollided(). If the balls load on top of each other or manage to get that way, they jiggle back and fourth and the program spends the rest of the iterations trying to get them to push away from each other. I noticed this happens if the balls overlap with each other because the dx, dy increment values are too small to effectively push them away on the first iteration. The program spends all of it's time trying to get them unstuck but it moves the balls back by -dx1, -dy1 and -dx2, -dy2 then back to dx1, dy1, dx2, dy2 creating the jiggle.
I tried to create a while loop inside of hasCollided() for the condition of the balls either as being originally drawn to overlap or if they overlap naturally due based on how much they collide by. It didn't work out. See commented section. I realized my thinking isn't 100% right on the collision for both the black box (hitBlackBox()) and the ball collision function (hasCollided()) because the balls cannot get unstuck in situations where the ball overlaps with the object it must oppose. Can someone explain to me what I should have done instead?
This post has been edited by Hikaroshi: 01 September 2013 - 02:07 PM