Newbie question about updating multiple objects ... beni.cherniavsky at
Fri Jun 9 18:25:03 CEST 2006

fivenastydisco at wrote:
> All,
Hello and welcome to Python.

> Apologies in advance: I'm new to Python and OO, and I'm sure this is a
> really simple question, but I just can't seem to crack it on my own, or
> through looking at examples. I'm playing around with VPython
> ( to try and teach myself about objects: I've
> got a box that I've put some balls in, and I'm trying to get said balls
> to move around and bounce off the walls of the box -- that's literally
> it. I can put multiple balls in the box, but I can't get them to update
> their position -- it works when I have one ball, but now that I have a
> list of balls, I can't get them to move.
> I've copied in the code I'm using below, as it's not very long -- can
> anyone help me?
Below is your code with the many impovements (including other
suggestions in this thread).  I've used double (``##``) comments to
indicate all changes.

Stylistic improvements aside, the biggest issue was that your code
didn't bounce the balls off the walls but just wrapped them to the
opposite wall.  The simplest example of bouncing appears in
Bouncing requires a separate velocity per ball (when one bounces, the
others keep direction).  Note that `b.velocity` has no signifacance to
the sphere object -- in Python you can add attributes to most objects
whenever you want.  OOP purists would say this is dirty but
"practicality beats purity".  You could also use a separate
`velocities[b]` dictionary if you prefer.

# <--START-->

## Say this at the top if you use division.   This fixes 3/2 to equal
## (3//2 == 1 if you really want the floor of the ratio.)
from __future__ import division

from visual import *
from random import uniform ## why limit to integral [a,b) values?

# set up a box to collide with
side_length = 100
minx = side_length * -10
maxx = side_length * 10
miny = side_length * -4
maxy = side_length * 4

numballs = 5 # number of balls
## List comprehensions simplify the ``b=[]; for...: b.append(...)``
balls = [sphere(pos=(uniform(minx, maxx), uniform(miny, maxy)),
                radius=50, for i in range(numballs)]

# create a wireframe of space
## Common spacing conventions: space after commas and around = in
## assignements but not = in keyword arguments
left = curve(pos=[(minx, miny), (minx, maxy)], color=color.white)
top = curve(pos=[(minx, maxy), (maxx, maxy)], color=color.white)
right = curve(pos=[(maxx, maxy), (maxx, miny)], color=color.white)
bottom = curve(pos=[(maxx, miny), (minx,miny)], color=color.white)

## Note that your `dt` was not equal to 1/rate, so you were not
## in real time.  I'll assume your velocity is correct but will
## the simulation speedup to make it less boring.
fps = 50  ## this should not be touched once you match it to your
speedup = 40.0  ## tune this to watch the process at different speeds
dt = speedup/fps  ## note that a big `dt` will increase simulation

## bouncing requires separate velocity per ball
for b in balls:
    ## removed z component since you want a 2D simulation (right?)
    b.velocity = vector(2, 0.2)

while True: ## the idiomatic way to say forever ...
    rate(fps)  ## see comment above
    for b in balls:  ## usually you don't need the indexes, only the

        ## First update, then bounce - this way you never display
        ## a ball outside the box.
        b.pos += (b.velocity * dt) ## += avoids repeating yourself

        # check it hasn't overrun a boundary
        ## if it has, reflect velocity, not wrap around the position
        if b.x - b.radius < minx:
            b.velocity.x *= -1
        if b.x + b.radius > maxx:
            b.velocity.x *= -1
        if b.y - b.radius < miny:
            b.velocity.y *= -1
        if b.y + b.radius > maxy:
            b.velocity.y *= -1

# <--END-->

The above is an approximation: when the ball has already penetrated the
wall to some depth, it only reverses the speed but leaves the ball in
the wall.  We are simulating a continious process by discrete steps.
If the ball is inside the wall it means the collision occured earlier
and the ball has already moved away for that distance.  (I'm ingnoring
ball and wall deformation here.  This penetration depends randomly on
`dt` so it's not the proper way to simulate deformation anyway.)
So we shoud do::

        if b.x - b.radius < minx:
            b.velocity.x *= -1
            #b.x = minx - (b.x - b.radius - minx) + b.radius
            b.x = (minx + b.radius)*2 - b.x

and similarly for other cases...

More information about the Python-list mailing list