Search this site
Embedded Files
Python Jedi
  • Chapters
    • Fork/Clone
    • 1 Version Control
    • 2 Python Console
    • 3 Input/Output
    • 4 Conditionals
    • 5 Looping
    • 6 Num. Systems
    • 7 Graphics
    • 8 Lists/Strings
    • 9 Functions
    • Prisoner's Dilemma
    • 10 Classes
    • 11 Animation
    • 12 User Control
    • 13 Libraries/Modules
    • 14 Game Design
      • 14 Sprites
      • 14 Shooting
      • 14 Explosions
      • 14 Levels
      • 14 Aiming Trig
      • 14 Walls
      • 14 View Port
      • 14 Grids
    • 15 Data Analysis
Python Jedi
  • Chapters
    • Fork/Clone
    • 1 Version Control
    • 2 Python Console
    • 3 Input/Output
    • 4 Conditionals
    • 5 Looping
    • 6 Num. Systems
    • 7 Graphics
    • 8 Lists/Strings
    • 9 Functions
    • Prisoner's Dilemma
    • 10 Classes
    • 11 Animation
    • 12 User Control
    • 13 Libraries/Modules
    • 14 Game Design
      • 14 Sprites
      • 14 Shooting
      • 14 Explosions
      • 14 Levels
      • 14 Aiming Trig
      • 14 Walls
      • 14 View Port
      • 14 Grids
    • 15 Data Analysis
  • More
    • Chapters
      • Fork/Clone
      • 1 Version Control
      • 2 Python Console
      • 3 Input/Output
      • 4 Conditionals
      • 5 Looping
      • 6 Num. Systems
      • 7 Graphics
      • 8 Lists/Strings
      • 9 Functions
      • Prisoner's Dilemma
      • 10 Classes
      • 11 Animation
      • 12 User Control
      • 13 Libraries/Modules
      • 14 Game Design
        • 14 Sprites
        • 14 Shooting
        • 14 Explosions
        • 14 Levels
        • 14 Aiming Trig
        • 14 Walls
        • 14 View Port
        • 14 Grids
      • 15 Data Analysis

Fork/Clone Ch.12 User Control Jedi Training

User Control

To make a cool game we need to interact with the user. How do we interact with the user for example letting the user move an object on the screen? We can do this with the mouse, with the keyboard, or with a game controller. This chapter will attempt to teach you how to take control of these things by coding their usage.

Using the Mouse

     The key to managing mouse motion is to override the on_mouse_motion method in the arcade.Window class. That method is called every time the mouse moves. The method definition looks like this:

def on_mouse_motion(self, x, y, dx, dy):

The x and y are the coordinates of the mouse pointer and the dx and dy represent the change in x and y since the last time the method was called.

Often when controlling a graphical item on the screen with the mouse, we do not want to see the mouse pointer. If you don’t want to see the mouse pointer, in the __init__ method, call the following method in the parent class:

self.set_mouse_visible(False)

The example below takes our Ball class we made in the last chapter and moves it around the screen with the mouse.

import arcade


SW = 640

SH = 480


class Ball:

    def __init__(self, pos_x, pos_y, rad, col):

        self.pos_x = pos_x

        self.pos_y = pos_y

        self.rad = rad

        self.col = col


    def draw(self):

        arcade.draw_circle_filled(self.pos_x, self.pos_y, self.rad, self.col)


class MyGame(arcade.Window):


    def __init__(self, width, height, title):

        super().__init__(width, height, title)

        self.set_mouse_visible(False)

        arcade.set_background_color(arcade.color.ASH_GREY)

        self.ball = Ball(50, 50, 15, arcade.color.AUBURN)


    def on_draw(self):

        arcade.start_render()

        self.ball.draw()


    def on_mouse_motion(self, x, y, dx, dy):

        self.ball.pos_x = x

        self.ball.pos_y = y


def main():

    window = MyGame(SW, SH, "Mouse Example")

    arcade.run()


if __name__=="__main__":

    main()

Mouse Clicks

You can also process mouse clicks by defining an on_mouse_press method which is called when a user presses a mouse button. This code can come in handy when making graphics on a screen and you quickly want to find the coordinates of where you want to draw something. Try changing the set_mouse_visible to True and deleting the code where the ball follows the mouse. Now you can click in the screen and see the coordinates printed in the python Run window.

def on_mouse_press(self, x, y, button, modifiers):

    if button == arcade.MOUSE_BUTTON_LEFT:

        print("Left mouse button pressed at", x, y)

    elif button == arcade.MOUSE_BUTTON_RIGHT:

        print("Right mouse button pressed at", x, y)

Move with the Keyboard

Moving with the keyboard is similar to our bouncing ball example. There are just two differences:

  • We control the change_x and change_y with the keyboard

  • When we hit the edge of the screen we stop, rather than bounce.

To detect when a key is hit, we override the on_key_press method. We might think of hitting a key as one event. But it is actually two. When the key is pressed, we start moving. When the key is released we stop moving. That makes for two events. Releasing a key is controlled by on_key_release.

These methods have a key variable as a parameter that can be compared with an if statement to the values in the:

 arcade.key library

def on_key_press(self, key, modifiers):

    if key == arcade.key.LEFT:

        print("Left key hit")

    elif key == arcade.key.A:

        print("The 'a' key was hit")

We can use the program below to move a ball around the screen. Notice we made a speed variable at the top of the program. If a key is pressed (on_key_press) we set the appropriate dx or dy to either a positive SPEED or a negative SPEED. If a key is released (on_key_release) we set the appropriate dx and dy to 0 so it will stop moving. 

If you try this program you will see that you can go right off the window. This could be a problem. Can you think of a way to code this program so for example if you continue hitting the Right Arrow key that the ball won't go beyond the right side of the window?

import arcade


SW = 640

SH = 480

SPEED = 3    # 3 x 60 = 180 pixels/second


class Ball:

    def __init__(self, pos_x, pos_y, dx, dy, rad, col):

        self.pos_x = pos_x

        self.pos_y = pos_y

        self.dx = dx

        self.dy = dy

        self.rad = rad

        self.col = col


    def draw(self):

        arcade.draw_circle_filled(self.pos_x, self.pos_y, self.rad, self.col)


    def update(self):

        self.pos_y += self.dy

        self.pos_x += self.dx


class MyGame(arcade.Window):


    def __init__(self, width, height, title):

        super().__init__(width, height, title)

        self.set_mouse_visible(False)

        arcade.set_background_color(arcade.color.ASH_GREY)

        self.ball = Ball(50, 50, 0, 0, 15, arcade.color.AUBURN)


    def on_draw(self):

        arcade.start_render()

        self.ball.draw()


    def on_update(self, dt):

        self.ball.update()


    def on_key_press(self, key, modifiers):

        if key == arcade.key.LEFT:

            self.ball.dx = -SPEED

        elif key == arcade.key.RIGHT:

            self.ball.dx = SPEED

        elif key == arcade.key.UP:

            self.ball.dy = SPEED

        elif key == arcade.key.DOWN:

            self.ball.dy = -SPEED


    def on_key_release(self, key, modifiers):

        if key == arcade.key.LEFT or key == arcade.key.RIGHT:

            self.ball.dx = 0

        elif key == arcade.key.UP or key == arcade.key.DOWN:

            self.ball.dy = 0


def main():

    window = MyGame(SW, SH, "Key Press Example")

    arcade.run()


if __name__=="__main__":

    main()

Move with a Game Controller

Working with game controllers is a bit more complex. We have several 8BitDo Pro2 Game Controllers which will work with our Macs either wired or through bluetooth.

Bluetooth Connection:

  1. Turn the mode switch to X

  2. Press start to turn on the controller, the 1st LED starts to blink

  3. Hold the pair button (small round button on back) for 3 seconds to enter its pairing mode. LED starts to rotate from left to right. (this is required for the very first time only)

  4. Go to your Apple device's Bluetooth setting and turn it on, pair with [8BitDo Pro 2]. LED becomes solid when connection is successful

Wired Connection:

  1. Turn the mode switch to X

  2. Connect the controller to the USB port of your Apple device, wait till the controller is successfully recognized by your Apple device to play

Joystick Code

We can get a list of all game pads that are plugged in with the get_joysticks function. This will either return a list, or it will return nothing at all if there are no game pads.

Below is a block of code that can be put in an __init__ method of your MyGame Class that will create an instance variable to represent a game pad if one exists.

joysticks = arcade.get_joysticks()

if joysticks:

    self.joystick = joysticks[0]

    self.joystick.open()

    self.joystick.push_handlers(self)

else:

    print("There are no joysticks.")

    self.joystick = None

There are actually 3 axes for the left and right joysticks AND triggers. self.joystick.x and self.joystick.y control up and down for the left joystick. self.joystick.z controls the left trigger. For the right joystick and trigger they are self.joystick.rx, self.joystick.ry and self.joystick.rz. You can print the position of the game controller joysticks in the on_update method with the following:

     if self.joystick:

            print(self.joystick.x, self.joystick.y, self.joystick.z)

Joystick Coordinates

Run the program and see the values it prints out for your game controller as you move the joystick on it around.

  • The values will be between -1 and +1, with 0 being a centered joystick.

  • The x-axis numbers will be negative if the stick goes left, positive for right.

  • The y-axis numbers will be opposite of what you might expect. Negative for up, positive for down.

  • The z-axis numbers for the trigger start on -1 when released, 0 is half-way pulled in and +1 is fully pulled in.

We can move the ball by adding the following code to the on_update:

def on_update(self):

        self.ball.dx = self.joystick.x

        self.ball.dy = -self.joystick.y

Notice the - we put in front of setting the y vector. If we don’t do this, the ball will move opposite of what we expect when going up/down. This is because the joystick has y values mapped opposite of how we’d normally expect. 

But with this code our ball moves so slow. How do we speed it up? We can make it run 10 times faster by multiplying by 10 if we want.

def on_update(self):

        self.ball.dx = self.joystick.x * 10

        self.ball.dy = -self.joystick.y * 10

Or better yet, define a constant variable at the top of your program called SPEED = 10 and use that. 

Using Game Controller Buttons:

To can also use the buttons on the Game Controllers. We do this very much like on_key_press. Instead we use on_joybutton_press and on_joybutton_release. Here is code to determine the joybutton numbers. (button_no):

    def on_joybutton_press(self, joystick, button_no):

        print(f"Button {button_no} pressed!")

        if button_no == 14:

            #DO STUFF HERE


    def on_joybutton_release(self, joystick, button_no):

        print(f"Button {button_no} was released!")

Drifting

What if your ball ‘drifts’ when you have the joystick centered? Joysticks are mechanical. A centered joystick might have a value not at 0, but at 0.0001 or some small number. This will make for a small “drift”. I created a variable at the top called DEAD_ZONE = 0.05

We can stop drifting by using this:

self.ball.dx = self.joystick.x * SPEED

if abs(self.ball.dx) < DEAD_ZONE:

     self.ball.dx = 0


self.ball.dy = -self.joystick.y * SPEED

if abs(self.ball.dy) < DEAD_ZONE:

     self.ball.dy = 0

Using Sound effects

Adding sound to your game isn’t too hard. There are two steps:

  1. Load the sound

  2. Play the sound

For these examples, I’m using the sound file called laser.mp3 that you downloaded in this chapter's repository. Make sure you save the file in the same directory as any Python program that tries to use it or in a Sounds folder.

Loading Sounds

The code below creates a new variable called laser_sound. It calls arcades load_sound function. It passes the filename of our sound.

self.laser_sound = arcade.load_sound("laser.mp3")

For this to work, you need to have a sound downloaded and named laser.mp3 in the same directory as your Python file. I suggest making another folder called Sounds that will store all sound effects in a game. If you do this then your code would need to look like this:

self.laser_sound = arcade.load_sound("Sounds/laser.mp3")

Load the sound in an __init__ method of any classes where you are going to play the sound.

Playing Sounds

The code to play sounds is straight forward. Just call the play_sound function, and pass in the variable that we set equal to the sound we loaded:

arcade.play_sound(self.laser_sound)

You should place this play_sound method in some kind of trigger like when the ball hits an edge or when the spacebar is pressed.

Finding Sounds

Great places to find free sounds to use in your program:

  • OpenGameArt.org

  • www.freesound.org is ok, but requires a login, which is a pain.

  • Kenney.nl has a few free sound packs you can download, and several that are cheap.

Sound File Formats

There are several types of sound file formats that you can find sounds in:

  • .wav - This is a raw “wave” format. The sound is not compressed at all. You do not need a special library to decompress the sound, but the sound file itself can be rather large.

  • .mp3 - MPEG Layer III Audio (mp3) is one of the most popular compressed sound file formats. This file format is what enabled on-line music to become popular. However some of the compression algorithms have patents on them, making it not as suitable for free software.

  • .m4a - Apple’s file format for compressed, but unprotected audio.

  • .ogg - A open-source sound file that uses Ogg-Vorbis for compression. A less popular but patent-free method of sound storage.

Arcade should be able to play files in either the mp3 or ogg format. If you have issues getting it to work, try converting the sound to a raw wav format. If you need to convert file formats, or trim a sound file, use the program Audacity.

Quick Lookup:How to download and turn in Jedi Tasks
Syllabus
Marc C. Hermon - Python Jedi InstructorUrbandale High School, Urbandale, Iowahermonm@urbandaleschools.comWebsite: EngineeringU.com
Google Sites
Report abuse
Google Sites
Report abuse