Friday, January 4, 2008

Some pygame examples for audiogame programming

While there are tutorials for pygame, they often contain graphic examples. So I have started to write some very simple examples focused on audiogames programming.

To use this tutorial, you should already know Python. The Python tutorial is a good start; there is Dive into Python too. To run the examples, you must have Python and pygame installed.

You can download all the examples (with the sounds) here.

Example 1: play a sound and change its stereo position


The example 1 shows how to play a sound and pan it. To go further:
Edit: warning! nowadays, it might be necessary to initialize the display or no sound will be heard! for example: pygame.display.set_mode((200,100))

# example1.py
# play a sound to the left, to the right and to the center

# import the time standard module
import time

# import the pygame module
import pygame


# start pygame
pygame.init()

# load a sound file into memory
sound = pygame.mixer.Sound("bird.ogg")

# start playing the sound
# and remember on which channel it is being played
channel = sound.play()
# set the volume of the channel
# so the sound is only heard to the left
channel.set_volume(1, 0)
# wait for 1 second
time.sleep(1)

# do the same to the right
channel = sound.play()
channel.set_volume(0, 1)
time.sleep(1)

# do the same to the center
channel = sound.play()
channel.set_volume(1, 1)
time.sleep(1)



Example 2: read keyboard events


The example 2 shows how to read events from the keyboard. To go further:

# example2.py
# start playing a sound when a key is pressed
# exit if the letter "q" or the "escape" key is pressed

# import the pygame module
import pygame


# start pygame
pygame.init()

# load a sound file into memory
sound = pygame.mixer.Sound("bird.ogg")

# start the display (required by the event loop)
pygame.display.set_mode((320, 200))

# loop forever (until a break occurs)
while True:
  # wait for an event
  event = pygame.event.wait()
  # if the event is about a keyboard button that have been pressed...
  if event.type == pygame.KEYDOWN:
      # ... then start playing the sound
      sound.play()
      # and if the button is the "q" letter or the "escape" key...
      if event.unicode == "q" or event.key == pygame.K_ESCAPE:
          # ... then exit from the while loop
          break

That's all for the moment!

12 comments:

DefaultGen said...

Thank you so much! These 2 little tutorials were very helpful in getting me started with pygame sound! I'm making a game that relies on sound to position the player. It's a sort of musical maze for a small class project, nothing as big as SoundRTS! Today is my first day programming with Python and it's so very rare to see such concise segments of code with exactly what I need (Playing audio, getting input from the keyboard/mouse, and adjusting the left/right channels).

I have yet to play SoundRTS but I'll check it out when I can take a break from coding. Thanks again for this post!

Storm Dragon said...

Hi,
Thanks for the tutorial. Will there be more any time soon? Like, in SoundRTS, some areas have the sound of a stream, how do you make that continueous water sound?

SoundMUD said...

@Storm Dragon:

To make the sound loop, add -1 to the arguments when calling the play() method:
channel = sound.play(-1)

More info at:
http://www.pygame.org/docs/ref/mixer.html#Sound.play

Anonymous said...

Do you know how to send a key while pygame is running?
I have tried:

pygame.event.post(Event(pygame.KEYUP, "Z", mod=None))

SoundMUD said...

@Anonymous:

I have never placed a keyboard event in the queue yet, but here are some hints. Instead of Event(...), use:

Event(pygame.KEYUP, key=pygame.K_z, mod=None)

or

Event(pygame.KEYDOWN, unicode=u"Z", mod=None)

Not sure for mod=None. If this doesn't work, try to check the attributes of a real keyboard event when you get one with poll() or wait().

Storm Dragon said...

I have a couple more questions. First, I found some stuff about sprite colision detection, but in a strictly audio game, there aren't any sprites, but the same kind of detection is needed. How is this handled? Second, if a exact series of keys need to be pressed with in a short time period, how would that be handled? For example, if a game move were accomplished with down arrow, right arrow, and finishes with the down arrow and the left shift key pressed together. It would need to forget that it is in a sequence if the key presses were too far appart, like maybe more than 0.25 seconds

SoundMUD said...

@Storm Dragon:

About the collision detection, I don't use pygame, even if it might be interesting for speed. It depends on the game: is it 1D? 2D? (like SoundRTS or SoundMUD) 3D? Are there many objects to check? How precisely will the collision be checked?

About the series of keys, use at least 2 variables: state and previous_key_time. Here is a first draft of a solution:

Note: I have replaced spaces with dots for the indentation.

# do this when an event is a keypress
if time.time() > previous_key_time + .25:
..state = None
if state is None:
..if event.key == pygame.K_DOWN:
....state = "1a"
elif state == "1a":
..if event.key == pygame.K_RIGHT:
....state = "1b"
..else:
....state = None
elif state == "1b":
..if event.key == pygame.K_DOWN:
....do_combo_1()
....state = None
previous_key_time = time.time()

That's all. I am not sure about the simultaneous DOWN and LEFT keys. It's another problem. Use special keys (mod keys: control, alt, shift) or use another variable to say that the time between DOWN and LEFT (or LEFT and DOWN) must be less than .1, for example.

Storm Dragon said...

The game I have in mind is loosely based on Mortal Kombat with some hints of Soul Blade. There aren't, as far as I know, many fighting games like that in Linux, and none that I know of for the blind. There won't be many obsticals, unless I get pretty detailed with the arena info. I am thinking of using d (move back), e (move forward), s (move left), and f (move right) and having the arrow keys do different punch, kick, and/or weapon attacks. I am not sure if it would be considered 2 or 3D though. If there were grapgics, it would be as though you are looking through the eyes of your character, seeing your opponent coming straight for you I guess you would be able to see your own hands and weapon too, but not your entire character's body. The detections would have to be able to tell if an attack connected with the opponent, or was parried/blocked, or just plain missed. There are several ways of blocking or evading attacks, pressing the same key that the opponent presses will parry, moving left will dodge an attack from the right, or a quick double press of d will cause you to jump back, and hopefully out of range. This may be a bit much for my first attempt at an audio game, but there's no better way to learn than to do, so. I plan on making the code open source, so maybe some others will jump on board and help with it. Of course, at the moment, there's not enough code to bother posting but soon there should be.

Storm Dragon said...

I have decided to tackle something I thought would be a little simpler before trying the fighting game. I have some work done on a space-invaders style game. I have ran in to a small problem though. How do you put sound files in a queue so that the current sound finishes playing before the next one begins?

SoundMUD said...

The music channel does this:
http://www.pygame.org/docs/ref/music.html#pygame.mixer.music.queue

Or remember in which channel the sound is played and check the channel with the get_busy method:
http://www.pygame.org/docs/ref/mixer.html#Channel.get_busy

Storm Dragon said...

I have hit a couple more snags in the space-invaders game. I would like to have constant movement while holding down either the left or right arrow. As it is now, I have to continuously press the arrows to move. Also, the alien ships only move and/or shoot when I do an action. So, how do I get them to act on their own even if I am not doing anything? Also, how to get constant movement while holding down an arrow key? I would like to also use auto firing when holding the space bar down but I figure the same solution for constant movement will work for the automatic firing too.
Thanks for the help.

SoundMUD said...

For every key that can be held down, you can keep a variable called for example space_is_down, etc. It would be updated every time a KEY_UP or a KEY_DOWN event is found for the space key.

About the alien ships, they can be updated at the end of the main loop.