This recipe is designed to show sample usage of the physics module. Once the program is running, the window can be dragged around the screen; and the balls will react accordingly. If an error is displayed on the screen, the window moved to show the rest of the error message.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | import Tkinter
import random
import time
import traceback
import physics
################################################################################
BALLS = 10 # NUMBER OF SIMULATED BALLS
BALL_RADIUS = 5 # RADIUS OF BALL IN PIXELS
START_SPACE = 20 # SIDE OFFSET IN PIXELS
SCREEN_WIDTH = 400 # WIDTH OF SCREEN IN PIXELS
SCREEN_HEIGHT = 400 # HEIGHT OF SCREEN IN PIXELS
WALL_SPACE = 50 # WIDTH OF WALLS IN PIXELS
FLOOR_SPACE = 25 # HEIGHT OF FLOOR IN PIXELS
BACKGROUND = 'white' # COLOR OF BACKGROUND
BALL_COLOR = 'red' # COLOR OF BALLS
FLOOR_COLOR = 'blue' # COLOR OF FLOOR
FORCE_COLOR = 'light green' # COLOR OF FOURCE FIELD
FPS = 60 # FRAMES PER SECOND
SPEED_LIMIT = 500 # PIXELS PER SECOND
WALL_FORCE = 500 # PIXELS PER SECOND
GRAV_RATE = 400 # PIXELS PER SECOND
FRIC_RATE = 0.875 # VELOCITY PER SECOND
################################################################################
def main():
'Setup and start demonstration.'
initialise()
Tkinter.mainloop()
def initialise():
'Build balls and prepare GUI.'
global balls, x, y, screen, lock, start, frame
balls = []
for ball in xrange(BALLS):
x = -START_SPACE if random.randint(0, 1) else START_SPACE + SCREEN_WIDTH
y = random.randint(BALL_RADIUS, SCREEN_HEIGHT - FLOOR_SPACE - BALL_RADIUS)
balls.append(physics.Ball(x, y, BALL_RADIUS))
root = Tkinter.Tk()
root.resizable(False, False)
root.title('Bouncy Balls')
x = (root.winfo_screenwidth() - SCREEN_WIDTH) / 2
y = (root.winfo_screenheight() - SCREEN_HEIGHT) / 2
root.geometry('%dx%d+%d+%d' % (SCREEN_WIDTH, SCREEN_HEIGHT, x, y))
root.bind_all('<Escape>', lambda event: event.widget.quit())
root.bind('<Configure>', move)
screen = Tkinter.Canvas(root, width=SCREEN_WIDTH, height=SCREEN_HEIGHT, background=BACKGROUND)
screen.after(1000 / FPS, update)
screen.after(10000 / FPS, unlock)
screen.pack()
floor_height = SCREEN_HEIGHT - FLOOR_SPACE + 2
screen.create_rectangle(0, 0, WALL_SPACE - 1, floor_height, fill=FORCE_COLOR)
screen.create_rectangle(SCREEN_WIDTH - WALL_SPACE + 1, 0, SCREEN_WIDTH, floor_height, fill=FORCE_COLOR)
screen.create_line(0, floor_height, SCREEN_WIDTH, floor_height, width=3, fill=FLOOR_COLOR)
lock = True
start = time.clock()
frame = 1.0
def move(event):
'Simulate movement of screen.'
global x, y
if not lock:
diff = physics.Vector(x - event.x, y - event.y)
screen.move('animate', diff.x, diff.y)
floor_height = SCREEN_HEIGHT - FLOOR_SPACE - BALL_RADIUS
for ball in balls:
ball.pos += diff
if ball.pos.y >= floor_height:
ball.vel.y += diff.y * FPS
floor(ball)
x, y = event.x, event.y
def update():
'Run physics and update screen.'
global frame
try:
for mutate in wall, floor, gravity, friction, governor:
for ball in balls:
mutate(ball)
for index, ball_1 in enumerate(balls[:-1]):
for ball_2 in balls[index+1:]:
ball_1.crash(ball_2)
for ball in balls:
ball.move(FPS)
screen.delete('animate')
for ball in balls:
x1 = ball.pos.x - ball.rad
y1 = ball.pos.y - ball.rad
x2 = ball.pos.x + ball.rad
y2 = ball.pos.y + ball.rad
screen.create_oval(x1, y1, x2, y2, fill=BALL_COLOR, tag='animate')
frame += 1
screen.after(int((start + frame / FPS - time.clock()) * 1000), update)
except:
screen.delete(Tkinter.ALL)
screen.create_text(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, text=traceback.format_exc(), font='Courier 10', fill='red', tag='animate')
def wall(ball):
'Simulate a wall.'
space = WALL_SPACE + BALL_RADIUS
force = float(WALL_FORCE) / FPS
if ball.pos.x <= space:
ball.vel.x += force
elif ball.pos.x >= SCREEN_WIDTH - space:
ball.vel.x -= force
def floor(ball):
'Simulate a floor.'
floor_height = SCREEN_HEIGHT - FLOOR_SPACE - BALL_RADIUS
if ball.pos.y >= floor_height:
ball.pos.y = floor_height
ball.vel.y *= -1
def gravity(ball):
'Simulate gravity.'
ball.vel.y += float(GRAV_RATE) / FPS
def friction(ball):
'Simulate friction.'
ball.vel *= FRIC_RATE ** (1.0 / FPS)
def governor(ball):
'Simulate speed governor.'
if abs(ball.vel) > SPEED_LIMIT:
ball.vel = ball.vel.unit() * SPEED_LIMIT
def unlock():
'Activate the "move" function.'
global lock
lock = False
################################################################################
if __name__ == '__main__':
main()
|
This recipe is a redesigned version of the program presented here: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/502241
Tags: programs