Sunday, September 26, 2010

$quickly create ubuntu-pygame targets


I hit a kind of hard part in photobomb, basically, creating copies of objects and applying clippings turns out to be a bit challenging. Sometimes when I'm feeling stymied, I switch to something new for a while to keep the programming fun. Of course, this results in having a few programs, like Photobomb, that are in a perpetual state of being incomplete. However, since I write FOSS software for the fun of it, I figure ... meh ... might well keep it fun.

Anyway, I recalled a really simple and fun game we used to play in college. I decided to build some similar game play, using the Quickly PyGame template. I created this template for Lucid, but it didn't really get any attention from me since then.

Here's some game play video, I mercifully left out the sounds:



The goal is to get points by touching the target, which moves after it is touched. The player controls the green smiley faced guy. By default, the PyGame template assumes that the player will control "the guy" using the keyboard. So, I had to change this by mapping the guy's location to the mouse. The place to update the guy's location is in the main files "controller_tick" function. This function fires each clock tick, and you use it to update the models of all the sprites and such. Checking out the PyGame docs, this turned out to be a simple one-liner:
    g.x, g.y = pygame.mouse.get_pos()

g is the variable name for the instance of the guy, and it's easy to set with the tuple returned from mouse position function. So that was all it took to make the guy follow the mouse (and deleted the code that used the keyboard of course). But then, I wanted to enemies to bounce around and be a bit hard to avoid, by by default, enemies wrap around the screen. Since I wanted all the sprites in the game to bounce off of wall, I edited the base_sprite.py file to change the wrapping to bouncing:
        #make the sprite bounce off of walls
if self.x + self.rect.width > sw:
self.x = sw - self.rect.width
self.velocity_x = -self.velocity_x
if self.y + self.rect.height > sh:
self.y = sh - self.rect.height
self.velocity_y = -self.velocity_y

if self.x <= 0: self.x = 0 self.velocity_x = -self.velocity_x if self.y <= 0: self.y = 0 self.velocity_y = -self.velocity_y
This code simply changes the direction that the sprite is traveling so it is reflected at the opposite angle that it hit. There's probably a more elegant way of doing it, but this turned out to be readable and reliable for me once I wrote it.

A bit harder was making enemies bounce off of each other. In previous games I wrote, sprites either did not interact, or killed each other. So this part took me quite a few iterations. I also saw that there were quite a few options for how to program the physics. I chose to make items essentially exchange their velocities, but I added just a touch of randomness. The randomness is designed to keep enemies from getting locked into boring patterns, like moving back and forth in a straight line. Here's the code I eventually ended up with to handle guys bouncing off of each other.
    for enemy in enemies:
e = pygame.sprite.spritecollideany(enemy, enemies)
if enemy is not e:
if enemy.velocity_x * e.velocity_x >= 0:
if enemy.velocity_x >= e.velocity_x:
faster_x = enemy
slower_x = e
else:
faster_x = e
slower_x = enemy

stash = faster_x.velocity_x
faster_x.velocity_x = slower_x.velocity_x
slower_x.velocity_x = stash
else:
stash = e.velocity_x
e.velocity_x = enemy.velocity_x
enemy.velocity_x = stash

if enemy.velocity_y * e.velocity_y >= 0:
if enemy.velocity_y >= e.velocity_y:
faster_y = enemy
slower_y = e
else:
faster_y = e
slower_y = enemy

stash = faster_y.velocity_y
faster_y.velocity_y = slower_y.velocity_y
slower_y.velocity_y = stash
else:
stash = e.velocity_y
e.velocity_y = enemy.velocity_y
enemy.velocity_y = stash

e.velocity_x += random.randint(-5,5)
e.velocity_y += random.randint(-5,5)

enemy.velocity_x += random.randint(-5,5)
enemy.velocity_y += random.randint(-5,5)

Due to horrible variable naming, this code is too hard to read. I should really fix that up. In any case, it is work mostly reliably. The only time it doesn't work is that sprites can get overlapped too much, and they don't travel far enough way to not collide in the next tick, and they look a bit odd weirdly bouncing off of each other.

I added a few game play features, like making the guy a ghost after he gets killed, to give the player a chance to get away and not killed over and over again if the guy is caught in a touch place. I also programmed for the background to update every 20 points.

I used the beloved InkScape to create some new sprites. Of course, I'm a pretty bad programmer, but I'm a worst artist! Any help with graphics would be most welcome :)

I still have some stuff left to do before I consider the game done enough to put in my PPA:
  1. Make and associated some nice sounds.
  2. Make the game increase in difficulty as levels go on. I'll probably increase the number of enemies, make them go faster, and also make the homing missles smarter, laster longer, and maybe faster.
  3. Format the score and levels so they are readable.
  4. Add power ups. I'm thinking ones that make the enemies freeze for a bit, maybe slow them down too. Free lives, and point multipliers of course. I was also thinking that sometimes the power ups could do bad things, like speeding up the enemies and/or changing their velocities unpredictably.
  5. Find a theme and a reasonable name.
If you want to check it out, branch, collaborate, etc... feel free to get the code from launchpad.

3 comments:

  1. I'm working on a pyglet engine ( https://edge.launchpad.net/wengine ), and would like to eventually have it set up as a quickly template. Where should I look for docuemntation on how to do all that?

    ReplyDelete