Animating your graphics

After creating your best art work for the refrigerator art project, it is time for something more fun. Let’s try to make something move on the screen.

This is a great way to experiment with loops and decisions in Python!

Creating the window

As usual, we need a window to draw in:

from graphics import *

def main():
    win = GraphWin('Pool Table', 600, 400)
    win.setBackground('yellow')
    win.getMouse()

main()

The new line, win.setBackground('yellow') makes the entire window background color yellow, in this case. Remember that objects you draw will show this background unless you fill them with a color.

Here is what you should see:

../_images/pool1.png

Adding a ball

Now, we need a ball, which is just a circle we will place in the middle of the screen. To make finding this spot easier, let’s use a few constants in this modification

from graphics import *

WIDTH = 600
HEIGHT = 400
RADIUS = 25

def main():
    win = GraphWin('Pool Table', WIDTH, HEIGHT)
    win.setBackground('yellow')

    ball = Circle(Point(WIDTH/2, HEIGHT/2), RADIUS)
    ball.setFill('red')
    ball.draw(win)

    win.getMouse()

main()

In this example, we have put a call to the Point function inside the call to the Circle function. We could have used a variable to do this, but this saves a bit of typing and works just fine. It is important that you understand how this works.

The Circle function expects a Point variable as its first parameter. In previous examples, we called the Point function and saved the result in a variable. By placing the call to Point in the parameter list for Circle, the value returned by ``Point is simply passed directly into the Circle function. That last value, 25, is the radius of the circle we want to draw. Phew!

Here is what we get now:

../_images/pool2.png

Moving the ball

Now for the fun part, making the ball move is incredibly easy, thanks to the smart design of the graphics code. We have a method available for each object we create (like the ball) that makes it move! Here is the code:

from graphics import *
import time

WIDTH = 600
HEIGHT = 400
RADIUS = 25

def main():
    win = GraphWin('Pool Table', WIDTH, HEIGHT)
    win.yUp()
    win.setBackground('yellow')

    ball = Circle(Point(WIDTH/2, HEIGHT/2), RADIUS)
    ball.setFill('red')
    ball.draw(win)

    win.getMouse()
    for i in range(50):
        ball.move(3,0)
        time.sleep(0.05)

    win.getMouse()

main()

Look closely at this code. We are using a new library here, called time. This library has one important function in it, one that will effectively puts your program to “sleep” for a short period of time. In this example, we are asking the program to sleep for one half of a second every time we move the ball.

Note

I have placed a call to the win.getMouse() function to stop the program just before the loop. You will need to click once to start the motion, and again to end the program.

Running this will make the ball slide across the screen. Cool!

Look closely at what we just did. We set up a counted loop using the for loop statement. We set up a simple variable named i whose sole purpose is to keep track of how many times we have run through the loop. Inside the body of the loop, we simply called the move method that the ball knows about and asked it to move a small distance in the X,Y direction. Since we set the Y amount to zero, the ball slides to the right. When the loops ends, the movement stops.

This use of the for loop is fine when you know that you want to stop looping at some point. There are times when we do not know when we want to stop. We will get to that soon!

Moving at an angle

Let’s add in a few more constants to make this a bit easier:

from graphics import *
import time

WIDTH = 600
HEIGHT = 400
RADIUS = 25
DX = 3
DY = 3
SPEED = 1/20

def main():
    win = GraphWin('Pool Table', WIDTH, HEIGHT)
    win.setBackground('yellow')

    ball = Circle(Point(WIDTH/2, HEIGHT/2), RADIUS)
    ball.setFill('red')
    ball.draw(win)

    win.getMouse()  # click the mouse to continue

    for i in range(50):
        ball.move(DX,DY)
        time.sleep(SPEED)

    win.getMouse()  # click the mouse to end the program

main()

This time we have set up constants for the amount in the X and Y direction we want the ball to move each time we pass through the loop (DX and DY). The delay at the end is controlled by the SPEED constant.

Now, the ball moves at an angle toward the bottom right of the screen. Playing with those DX and DY values will make it move at other angles. Try it and see!

Boing!

Suppose this ball is a model of a real pool ball, and the window we are drawing in is the pool table. As the ball moves to the right, eventually it will hits the edge of the window. What should happen? Well to figure this out, we need to figure out two things

Have we hit the wall yet?

This one is harder than it should be, but makes sense after a bit of thinking.

Remember that we are working with those funny object critters in all this. The Circle object has a method that will tell you where the center currently is. If we call that method, getCenter(), we will get back another object which is one of those Point objects. The Point object can tell you where it is using the getX() and getY() methods. Sounds complicated, but it is not so bad. Here is the code we need:

center = ball.getCenter()
current_x = center.getX()
current_y = center.getY()

See, it was not so bad! It does help to study the reference material for the graphics module. Here is the PDF file:

The Circle object we have created has another object at the center, a Point. The Circle object knows how to tell you where the center is. If we call the getCenter() method, it will return a Point that is at the current center of the Circle. We can call the getX() method to fetch the actual coordinate value.

What do we do to “bounce”?

This one is simple, we move away from the wall. If moving toward the wall was done by setting DX to a positive value, all we need to do is make it negative. Try this:

if current_x >= WIDTH - RADIUS:
    DX = -DX

Warning

Remember our discussion on global and local variables. If we ask Python to use this line inside a function (like main in this case), Python will create a new local variable with the same name as the global variable we really want it to use. The cure for this is to add this line inside main (after the “def” line):

global DX, DY

This tells Python that the names DX and DY we want to use in this function are the global ones.