Animating Many Objects
The resources on this page were originally created by Dr. Aaron Bradley of Summit Middle School. I've done some reformatting to add clarity but that is about it. Enjoy!
|
Games and animations become much more interesting when
You will be most successful if you have already completed and understood Python as a Sophisticated Calculator, which introduces lists, Getting Started with Games, which introduces graphics and animation, and Exploration: A Simple Game of Paddleball, which shows how to make a moving circle look like its bouncing off the walls of the window. Indeed, I will assume that you know (or know where to look to find) how to import the sgfx and random modules and how to create a basic window. Let's get started! |
A Lot of Moving Sprites
Recall that "sprite" is a term for a moving object in a video game. Let's start by creating a lot of sprites, namely, circles. To create and hold a lot of them, we will uses lists.
Now, create lists to hold the balls:
# Create lists to hold balls (circles) and their velocities.
balls = [ ]
vxs = [ ]
vys = [ ]
What makes the circles into "balls" is that they move and bounce, and what makes them move is that they have velocities, just like the one ball has a velocity given by vx andvy in Exploration: A Simple Game of Paddleball.
Next, let's fill the lists with sprites. I'll make 20 of them:
#import the random module
import random
#create a window and name it 'w'
w = Window(background="white", title="Sprites")
# Create a bunch of moving balls.
for i in range(20):
# Create one ball:
# Choose its initial location randomly.
x = random.randint(100, w.XMax-100)
y = random.randint(100, w.YMax-100)
# Choose its radius randomly.
r = random.randint(3, 30)
# Create a circle and add it to the list.
balls.append(w.circle(x, y, r))
# Choose its velocity randomly...
vx = random.randint(-5, 5)
vy = random.randint(-5, 5)
# ... and add it to the lists.
vxs.append(vx)
vys.append(vy)
Finally, let's animate them:
def animate():
# For each ball...
for i in range(len(balls)):
# Move the ball.
w.move(balls[i], vxs[i], vys[i])
Run the program. What happens?
Recall that "sprite" is a term for a moving object in a video game. Let's start by creating a lot of sprites, namely, circles. To create and hold a lot of them, we will uses lists.
Now, create lists to hold the balls:
# Create lists to hold balls (circles) and their velocities.
balls = [ ]
vxs = [ ]
vys = [ ]
What makes the circles into "balls" is that they move and bounce, and what makes them move is that they have velocities, just like the one ball has a velocity given by vx andvy in Exploration: A Simple Game of Paddleball.
Next, let's fill the lists with sprites. I'll make 20 of them:
#import the random module
import random
#create a window and name it 'w'
w = Window(background="white", title="Sprites")
# Create a bunch of moving balls.
for i in range(20):
# Create one ball:
# Choose its initial location randomly.
x = random.randint(100, w.XMax-100)
y = random.randint(100, w.YMax-100)
# Choose its radius randomly.
r = random.randint(3, 30)
# Create a circle and add it to the list.
balls.append(w.circle(x, y, r))
# Choose its velocity randomly...
vx = random.randint(-5, 5)
vy = random.randint(-5, 5)
# ... and add it to the lists.
vxs.append(vx)
vys.append(vy)
Finally, let's animate them:
def animate():
# For each ball...
for i in range(len(balls)):
# Move the ball.
w.move(balls[i], vxs[i], vys[i])
Run the program. What happens?
Now in Color!
One very unsatisfying aspect of the resulting program is that all of the circles are red. A palette of colors would look nicer, but we need to assign colors randomly, just as we assigned initial locations and radii randomly.
To do so, make a palette, that is, a list of colors, somewhere before the loop that creates the balls:
# A list of possible colors for the balls.
colors = ['red', 'orange', 'yellow', 'green', 'blue', 'purple', 'violet']
Now let's modify the loop itself. Before creating the circle, choose a color randomly using the choice(list) function of the random module, which selects an element of the list (uniformly) randomly:
# Choose its color randomly.
c = random.choice(colors)
# Create a circle and add it to the list.
balls.append(w.circle(x, y, r, color=c))
Run the program. That's much better, isn't it?
One very unsatisfying aspect of the resulting program is that all of the circles are red. A palette of colors would look nicer, but we need to assign colors randomly, just as we assigned initial locations and radii randomly.
To do so, make a palette, that is, a list of colors, somewhere before the loop that creates the balls:
# A list of possible colors for the balls.
colors = ['red', 'orange', 'yellow', 'green', 'blue', 'purple', 'violet']
Now let's modify the loop itself. Before creating the circle, choose a color randomly using the choice(list) function of the random module, which selects an element of the list (uniformly) randomly:
# Choose its color randomly.
c = random.choice(colors)
# Create a circle and add it to the list.
balls.append(w.circle(x, y, r, color=c))
Run the program. That's much better, isn't it?
Walling in the Balls
Unfortunately, the balls seem to escape, and then all we're left with is a blank window. It's time to add the bouncing logic to the animate function. The logic is the same as in Exploration: A Simple Game of Paddleball---we just need to apply it to each ball in the list.
Since you've implemented bouncing before, try modifying the previous code to work on lists for this program. Notice how in the current version of animate, the ith ball and its velocities are accessed via balls[i], vxs[i], and vys[i].
Did you come up with something like the following?
def animate():
# For each ball...
for i in range(len(balls)):
# Is the ball about to go off-screen?
x1, y1 = w.upperLeft(balls[i])
x2, y2 = w.lowerRight(balls[i])
if x1 <= 0 or x2 >= w.XMax:
vxs[i] = -vxs[i]
if y1 >= w.YMax or y2 <= 0:
vys[i] = -vys[i]
# Move the ball.
w.move(balls[i], vxs[i], vys[i])
Now run the program. Cool, huh?
Unfortunately, the balls seem to escape, and then all we're left with is a blank window. It's time to add the bouncing logic to the animate function. The logic is the same as in Exploration: A Simple Game of Paddleball---we just need to apply it to each ball in the list.
Since you've implemented bouncing before, try modifying the previous code to work on lists for this program. Notice how in the current version of animate, the ith ball and its velocities are accessed via balls[i], vxs[i], and vys[i].
Did you come up with something like the following?
def animate():
# For each ball...
for i in range(len(balls)):
# Is the ball about to go off-screen?
x1, y1 = w.upperLeft(balls[i])
x2, y2 = w.lowerRight(balls[i])
if x1 <= 0 or x2 >= w.XMax:
vxs[i] = -vxs[i]
if y1 >= w.YMax or y2 <= 0:
vys[i] = -vys[i]
# Move the ball.
w.move(balls[i], vxs[i], vys[i])
Now run the program. Cool, huh?
Extensions
See how many of the following challenges you can accomplish!
See how many of the following challenges you can accomplish!
- Implement a key that stops and restarts all motion.
- Implement a key to randomly reassign colors. Use w.setColor(balls[i], color).
- Implement a key to add another circle to the melee.
- Make the choice of shapes random as well. You've seen rectangles; there are also triangles, which are created like circles: use w.triangle(x, y, r).
- Implement a color infection: if two balls overlap and one is red, the other turns red as well. To determine if two balls overlap, check if their centers are closer than the sum of their two radii using the "distance formula":
x1, y1 = w.center(balls[i])
r1 = w.radius(balls[i])
x2, y2 = w.center(balls[j])
r2 = w.radius(balls[j])
if math.sqrt((x1-x2)**2 + (y1-y2)**2) <= r1 + r2:
# they overlap, so do something - Make a game of paddleball with multiple balls, where the game resets only when all balls are off-screen.