More Graphics: Assets and Looping - Runpython-IntroProgramming/Course-Syllabus GitHub Wiki

This tutorial will show you how to add the following features to your ggame based applications:

  • Image-based sprite assets.
  • Sound assets.
  • Respond to keyboard input.
  • Respond to mouse button.
  • Movement.

Fork the Tutorial Repository

Visit the ggame-tutorials repository and fork a copy to your own Github account. This repository consists only of assets that we will use for the final ggame tutorials.

After forking, add a new Python source file (press the + link for your new repository) and name it tutorial3.py. You can insert a comment at the top of the file with your name and the name of the file then commit it. Copy the URL for the file and load it up in Runpython.

Now start by pasting in some "boilerplate" code for our tutorial. This should look familiar:

from ggame import App, RectangleAsset, ImageAsset, SoundAsset
from ggame import LineStyle, Color, Sprite, Sound

myapp = App()
myapp.run()

If you test your program now it should open a blank window in your editor tab, but that's about all.

Retrieve Graphics Area Size

In the past tutorial and graphics assignments we have used all of the available space in the browser for our graphics. For this one, we would like to know how big that space is and use that size to create a colored background for the app. Insert the following lines after the myapp = App() line, and before the myapp.run() line:

# define colors and line style
green = Color(0x00ff00, 1)
black = Color(0, 1)
noline = LineStyle(0, black)
# a rectangle asset and sprite to use as background
bg_asset = RectangleAsset(myapp.width, myapp.height, noline, green)
bg = Sprite(bg_asset, (0,0))

Once we have created the myapp object, we can use two of its properties, width and height, in our program. Commit, then run your app to see the difference. You should see that the entire graphics window is green.

Image Assets

Next, add the following lines to your program, after the other asset and sprite lines, but before the myapp.run() line:

# A ball! This is already in the ggame-tutorials repository
ball_asset = ImageAsset("images/orb-150545_640.png")
ball = Sprite(ball_asset, (0, 0))
# Original image is too big. Scale it to 1/10 its original size
ball.scale = 0.1
# custom attributes
ball.direction = 1
ball.go = True

Step by step, here is what we have added:

  • The ImageAsset function creates an image asset from an image file already in the ggame-tutorials repository. If you go back to the Github pages for your forked repository you can click on the images link in the file list to see all the images included in the repository.
  • The Sprite function makes a sprite from the image asset, just the way it did in the earlier tutorials and assignments.
  • Since the image asset we are using is a very large image, the ball.scale = 0.1 line will shrink the displayed image on your screen to 1/10 its original size.
  • The next two lines actually create two attributes of the ball sprite that don't already exist in Sprite objects. The first is a number (direction) that will represent the direction the sprite is moving. The second is a boolean (True or False) attribute (go) that will indicate whether the sprite is moving.

Make Your Own Function

So far you have been using functions in Python. A function is a name that refers to a collection of Python code that you can run at any time. For example, the print function executes code that displays text on the computer console. To use a function, you type the name of the function, followed by (). If the function accepts additional information (like the print function does), then you insert that information inside the (). If you are expected to insert more than one piece of information, then you separate items with commas.

Now it is time to create your own functions! Insert the following code in your tutorial (immediately after the code you already added, but before the myapp.run() line):

# reverse - change the ball direction
def reverse(b):
    b.direction *= -1

This code uses the special keyword def to define a new function. The name of the function is reverse and it accepts one parameter, b. The name b is used only inside the definition of the function. When you call the function reverse, you typically insert a different name in the parentheses. In our case, we intend to call the reverse function and give it the name of our sprite (ball). The reverse does one small thing: it takes whatever the value of the ball's direction is and multiplies it by -1.

If you create a function that should return a value then use Python's return keyword (e.g. return 99).

Note: you have created simple functions already using the lambda statement. The def keyword allows you to create functions with multiple lines. All of the code inside the function must be indented.

Execute Code Repeatedly

The execution of a graphical computer game implies an infinite loop. The graphics display is changed and redrawn many times per second and each time this happens computer code needs to run in order to figure out how the screen should be redrawn. In a ggame program, the code that does this is known as the step function.

Next, add the following function to your tutorial code (after the reverse function, but just before the myapp.run() line):

# Set up function for handling screen refresh
def step():
    if ball.go:
        ball.x += ball.direction
        if ball.x + ball.width > myapp.width or ball.x < 0:
            ball.x -= ball.direction
            reverse(ball)

This function is intended to be run once every time the screen updates. It checks to see if the ball should be moving (ball.go is True), increments the x position of the ball with the value of ball.dir, then checks to see if the ball has moved to the edge of the game screen. Do you see how this function doesn't need to know exactly how many pixels wide the screen is?

Finally, if the function decides that the ball needs to change direction, it calls the reverse function that we created earlier.

Before we can try the program, we have to tell myapp that our step function exists. Change the last line of your program from myapp.run() to myapp.run(step). Remember: step is the name of the function you just created; the application will call your step function over and over again.

Now run it!

Handle User Input

Computer games are much more fun if you get to control them in some way, so let's add mouse and keyboard controls. Insert the following function definitions after your step function, but before the myapp.run(step) line:

# Handle the space key
def spaceKey(event):
    ball.go = not ball.go

# Handle the "reverse" key
def reverseKey(event):
    reverse(ball)

# Handle the mouse click
def mouseClick(event):
    ball.x = event.x
    ball.y = event.y

These functions are supposed to be executed when the "space" and "reverse" keys are pressed, and when the mouse button is pressed. However, we need to tell the App about them in order for them to work. Insert the following code before the myapp.run(step) line:

# Set up event handlers for the app
myapp.listenKeyEvent('keydown', 'space', spaceKey)
myapp.listenKeyEvent('keydown', 'r', reverseKey)
myapp.listenMouseEvent('click', mouseClick)

The listenKeyEvent method of the application will tell it to "listen" for buttons to be pressed ('keydown'), and which specific key to listen for ('space' and 'r') on the keyboard. If the keys are pressed then the applicaiton will call the functions spaceKey and reverseKey, respectively. Note: the functions you create to handle keypresses must accept an event argument.

The listenMouseEvent method listens for the user to do something with the mouse. In this case, we're only waiting for the user to 'click' the mouse button. In response, the application will call the mouseClick function that we created.

Note: you can call your mouse and keyboard event handler functions whatever you want.

Run the program and experiment with space, r and mouse button. Do you understand how the program works?

Sounds

For the last piece, we will add a couple of sounds to the game. Insert the following lines near where your other assets are created:

# Sounds
pew1_asset = SoundAsset("sounds/pew1.mp3")
pew1 = Sound(pew1_asset)
pop_asset = SoundAsset("sounds/reappear.mp3")
pop = Sound(pop_asset)

Then add the line, pew1.play(), inside the mouseClick function. Add the line pop.play(), inside the reverse function.

Your final program should look something like this:

"""
tutorial3.py
by E. Dennison
"""
from ggame import App, RectangleAsset, ImageAsset, SoundAsset
from ggame import LineStyle, Color, Sprite, Sound

myapp = App()

# define colors and line style
green = Color(0x00ff00, 1)
black = Color(0, 1)
noline = LineStyle(0, black)
# a rectangle asset and sprite to use as background
bg_asset = RectangleAsset(myapp.width, myapp.height, noline, green)
bg = Sprite(bg_asset, (0,0))
# A ball! This is already in the ggame-tutorials repository
ball_asset = ImageAsset("images/orb-150545_640.png")
ball = Sprite(ball_asset, (0, 0))
# Original image is too big. Scale it to 1/10 its original size
ball.scale = 0.1
# custom attributes
ball.direction = 1
ball.go = True
# Sounds
pew1_asset = SoundAsset("sounds/pew1.mp3")
pew1 = Sound(pew1_asset)
pop_asset = SoundAsset("sounds/reappear.mp3")
pop = Sound(pop_asset)

# reverse - change the ball direction
def reverse(b):
    pop.play()
    b.direction *= -1

# Set up function for handling screen refresh
def step():
    if ball.go:
        ball.x += ball.direction
        if ball.x + ball.width > myapp.width or ball.x < 0:
            ball.x -= ball.direction
            reverse(ball)

# Handle the space key
def spaceKey(event):
    ball.go = not ball.go

# Handle the "reverse" key
def reverseKey(event):
    reverse(ball)

# Handle the mouse click
def mouseClick(event):
    pew1.play()
    ball.x = event.x
    ball.y = event.y
    
# Set up event handlers for the app
myapp.listenKeyEvent('keydown', 'space', spaceKey)
myapp.listenKeyEvent('keydown', 'r', reverseKey)
myapp.listenMouseEvent('click', mouseClick)
    
myapp.run(step)

Play With It

Feel free to play with the app and substitute other assets. Enjoy!