Wednesday, April 7, 2010

How I Learned Python

Being primarily a C++ programmer, I have lived a hard life: All the rules, all the syntax, all the mean spirited compiler errors from the STL...

While I have dabbled in scripting languages in the past, I never really put too much time into learning one. I decided it was time to learn a scripting language in more detail, and I chose Python. The best way to learn a new language is to create a relatively complex program in it, so to accomplish this task, I set out to make a game in no more than 7 days using Python and PyGame (SDL wrapper). Being familiar with using SDL from C++, I felt confident this project would be a success, and it was. 4 days later, I had a new game, "Cloud Cover", and I knew a decent amount of Python. Here's how it happened.

Game Design

I had been toying with an idea for a game that involved the rain theme for quite some time. Originally, I wanted to develop the game for the iPhone and take advantage of the tilt-control. The idea at that point was to tilt the iPhone left and right to tilt platforms in order to fill up buckets with water in order to progress in the level. However, not particularly liking Objective-C or the hoops needed to jump through in order to develop for the iPhone, I nixed that idea.

After hearing the instrumental "2 Die 4" by 'John 5' and then going to sleep, I awoke with the game design in my head. It literally came to me in my sleep. In fact, I even feature the song in my game because it fits so well (and it should since it inspired the game!).

Essentially, the game design is as follows:
  • You play the part of a bowling ball who must fill up beakers with rain drops.
  • On each level, you will have either 1 or 2 beakers to fill, and they may be positioned with at most 1 beaker on each side of the player 
  • The game will only have 2 controls: roll left and roll right. This adds to the challenge of the puzzles because you can only move each beaker in 1 direction.
  • Rain is created by a dynamic rain system from 1 or 2 clouds per-level.
  • The clouds can be configured to behave in many different ways, such as: being stationary, moving left/right (single or multiple passes), being restricted to certain x-value bounds, having endless rain drops, or having a limited supply of rain drops.
  • Tweaking the dynamic rain system along with the placement and movement of the cloud(s) and beaker(s) can be used to create challenging puzzles
Implementation

Dynamic Rain System

Implementing the dynamic rain system was a lot of fun, and a lot easier than you may think. Basically, it boils down to this:
  • Assign each cloud a life span (number of drops it can produce)
  • Assign each cloud an intensity value (number of drops it produces per-frame)
  • Using the dimensions of the cloud, generate a random x,y coordinate at which to generate a new rain drop within the bounds of the cloud.
  • Generate the rain drops in a loop ranging from 1 to the intensity value
  • For each drop created, subtract 1 from the life span of the cloud
  • As long as the life span is > 0 (or the cloud has an infinite life span), continue to generate rain
Pretty simple, eh?

Beakers

There were a few things I had to deal with when implementing the beakers. First, I wanted to have some sort of physics effect so that the character would be slowed down while pushing them. I decided to fake the physics first and see how well it worked before implementing the real deal. I got lucky on my first attempt at this. I gave each beaker a fractional weight value (settled on .5). Then, on collision, I would multiply the player's speed by the weight of the beaker. Since it is a fractional value, it would decrease the player's speed by a factor of the beaker's weight. The result is a very believable friction effect. Sorry, Newton, I don't need you today...

The next issue with the beakers was getting them to "fill up" as they caught rain drops. This one took a little bit more time to nail down, but I'm pretty happy with the solution I came up with. Basically, each beaker keeps track of how many drops it can hold, and how many it's already caught. Using these values, combined with the height and width of the beaker, I do some simple division to create a fill percentage and draw a filled rectangle inside the beaker. This gets updated on every game update, so as you collect more drops, the box grows taller and taller, thus the beaker appears to be filling up. Until the beaker is full, the box is the same color as the rain drops, and once full, it turns red so the player knows to stop trying to fill it. Some normalization had to be done for when the beaker does fill up, as well as to account for leaving some space on the sides, but the code for this is pretty straightforward:



I did have to address one issue with the beakers in that they were detecting "caught" drain drops anytime they collided with a rain drop. Clearly, you could collide with low-falling drops by pushing the beaker into them, but that's hardly catching them, so it had to be fixed.

See this post for details on how I went about that.

Player

The player is a fairly simple piece of code, although there is one neat trick I employed. I was faced with the question of, "How do I make the bowling ball roll around?". Sure, I could do some involved math to do it, but I'm just using a static image for the player character. So, what to do? Rotate the image!



Conclusion 

All in all, the game took around 4 days, but probably only about 15-20 hours of time. It was essentially the first non-trivial program I had ever written in Python, and it turned out really great if I do say so myself.

You can find the game here

                         
 


Just A Drop In The Bucket (or was it?)

I had a known bug in my new game, "Cloud Cover" which involved detecting a "caught" rain drop when the drop actually hit the side of the beaker instead of in the opening. I knew about the issue, but due to the mechanism I was using to detect collisions (a pre-made function from the PyGame library), the solution wasn't immediately apparent. It would have been difficult and clunky to adjust the iteration on each collision detection to then ensure that the drop that had been detected as a collision was in the acceptable range, being y <= the top of the beaker (y increases towards the bottom of the screen).

So, after some thinking, I came up with the following solution: Instead of checking the y-value of the drop once a collision is detected, only check for collisions against rain drops that are within the acceptable range. It works great, and it's pretty slick if I do say so myself.

First, I store the heights of all beakers in the current level. Currently, each level has at least 1 beaker, no more than 2, the beakers exist for the duration of the level, and they are all of the same height. However, in the future this may change, and beakers may even be destroyed as the level plays on, so I had to ensure that I was able to keep track of the beakers that were currently on the screen: having a static list of the beakers that the level started with wouldn't have been sufficient. Then, I sort the list of beaker heights. This ensures the smallest beaker height will be in the front of the list. This is crucial because I need to use the y-value of the *smallest* beaker as my baseline for accepting collisions because even if I have taller beakers, a y-value of <= the smallest one will also suffice for any beakers taller (remember, y gets larger as it approaches the bottom of the screen). Now that I have the y-value of the shortest beaker, I check against this value when a collision is detected. If the rain drop's y-value is <= this number, I know it is within the area of the opening of the beaker, and I can consider that a collision, and thus a "caught" rain drop.

This also has a nice benefit in that it eliminates a number of otherwise useless and costly collision comparisons. Before, every drop on the screen was being checked for a collision. Now, any drop that is below the shortest beaker is ignored entirely. With enough drops on the screen, this can be a big savings in performance.

The code for this is as follows:

Thursday, April 1, 2010

Game In A Day - "Hangman"

The second installment to my Game In A Day series is a project I started many years ago, and have maintained in not-so-consistent fashion: Hangman.

I wrote this about 5-6 years ago in C++. The interface was console-based only and this lead to a lot of added headaches and bugs. I've been meaning to throw a GUI on this project for a while, but just never got around to it... until now!

As part of my Software Engineering capstone, my group and I are making a set of educational games to teach 4th and 5th grade curriculum in Math, English, and Reasoning Skills. The English side of things was lacking a bit, so I decided to create Hangman in the context of my capstone project. Note that at the time, there were only 4 weeks left in the course and we had moved every other project into their final phase. So, if Hangman was to be added, it would definitely have to be developed in less than 1 day!

Interface Design
I came up with the design in a fairly short amount of time while I was sitting in one of my morning classes. Obviously, Hangman is a classic game and interfaces for it can only vary so much, but I did add a few tweaks. First, I designed it so that the vowels and consonants are displayed to the user in horizontal list form. This assists in the curriculum for the target audience as they can learn to identify the different letters as well as learn the words and definitions. Second, guessed letters (both correct and incorrect) get removed from their respective lists. This helps the player keep track of what they've already guessed and what's available to guess. Last, I added a "Solve" button to the interface so that at any time they can choose to solve the puzzle.

Game Design
Much like the interface design, the gameplay for Hangman is relatively set in stone. The flow that I went with is as follows:
  • A definition is displayed to the player
  • Below the definition, a series of _ characters are displayed to the player to represent the number of letters in the answer
  • When the player types a letter, the game checks to see if that letter is in the puzzle, and if so, replaces each corresponding _ with the guessed letter.
  • A wrong guess results in the next Hangman image being displayed to the player on the interface. The images are looked up using the number of questions wrong as the index into the vector of images. In total, there are 9 images, and the game is over when you've missed 9 questions.
  • At any point the player can choose to hit "Solve" and try to solve the puzzle. An incorrect guess results in the player losing.
Pretty simple, eh?

Tools
I decided to use C++ and QT for this project because I'm very familiar with QT and it is an incredibly powerful library.

Implementation
Instead of adding a GUI to the pre-existing code I had, I decided to write the entire game from scratch. Using QT made this quite simple, actually. It allowed me to use the built-in QT types for everything (QString, QChar, QVector, etc...) and keep the code clean and consistent. You would think that writing a game entirely console-based would be easier, but when you have to deal with validating user input, alternating between std::string, char*, and char, things get messy in a hurry. Also, organizing game flow and game state create equally messy situations. A GUI environment like QT eliminates this mess with the use of dialogs and events to properly handle flow and state changes.

All in all, the implementation was fairly straight forward and consisted of the following pieces:
  • Event handler to grab alphabetic input (ignores all other input)
  • Function to check if a letter exists in the answer (and every location in which it does)
  • Function to handle solving the entire puzzle
  • Function to remove the last guessed character from both the vowel and consonant lists.
  • Functions to handle a correct and incorrect guess
  • Function to handle the end of the game
  • Function to handle resetting the game to the initial state
That's it. Total, the project only took around 5 hours from start to finish, so I'm very pleased with that result. The only thing left to do is to plug it in to our QuestionManager to dynamically grab pre-made questions.

Notable Code Snippets

Determining If Input Is An Alphabetic Character



Finding Every Occurrence Of A Letter In A Word

Saturday, March 27, 2010

Game In A Day - "Free Fall"

A theme I've done twice now is "Game In A Day" in which I sit down and create a brand new game from scratch in no more than 24 calendar hours. This post will outline the first such "Game In A Day" experience which took place a little over a month or 2 ago...

I'm huge into music. I play guitar, and I can't accomplish much of anything without my favorite tunes on. As I've mentioned in another post, I've always wanted to create a game based entirely around music. I coupled this desire with my desire to make a brand new game from scratch in less than 1 day. The goal: Create a game that is inspired and based around a single song, and do it all in 1 day.

The Tools
Due to the time constraints and past success with the language/library, I chose to use Python along with the PyGame API for this project. PyGame is really just a front-end for SDL with some added goodies. I use SDL extensively from C++, so PyGame feels very comfortable to me.

The Song
As for the song, that was a harder decision. I loaded up about 5 band's worth of material, turned my monitor off, turned my lights off, and paced around for about an hour trying to find something that screamed, "Game!" to me.

After a while, I decided on Dethklok - Comet Song. It is very fast paced and evokes a sense of urgency and thrill, 2 emotions perfect for a game.

You can check the song out here:
http://www.youtube.com/watch?v=kRQ47oEbA6s

The Game Design
Using the flow of the song as my guide, I started to picture an environment in which I was under constant pressure to evade and/or maneuver. The more I listened to the song, the theme of being in space (as is the theme for the song) started to appeal to me. I decided to design the game around this idea in the following ways:
  • The character will be falling through space
  • There will be objects coming at it with varying rates of speed that it must avoid
  • The character will only have 2 controls: move left, move right
  • The goal will be to stay alive as long as you can, earning points the longer you're alive
Pretty simple, but definitely has the potential for some fun, and it fits the song perfectly.


The Game Implementation

Parallax
Right off the bat I knew I wanted to create some sort of parallax effect. I had never implemented it before, and creating a game in 1 day really isn't that difficult, so I wanted to add some level of challenge to it, as well as gain some experience doing something cool like parallax.

The idea behind parallax is that you create the illusion of movement by layering background images on top of each other, giving each a different speed (and some layers don't move at all) to give the illusion of distance and speed in relation to the character.

I spent probably more time designing and tweaking the parallax than I did on anything else in the project. I wanted to fake the character's fall through space by creating the parallax in such a way that the character would only ever move left or right, but the outer space background(s) would move from the bottom of the screen to the top of the screen, thus creating a falling effect.

My final design was as follows:
  • For each background image traveling at a particular speed, there needs to be another background image traveling at that same speed. The image can be different, but the speed must be the same. The images are the exact size of the screen coordinates.This second image will be offset from the first one by a distance of the height of the image (SCREEN_HEIGHT). The reason for this is so that as both images move towards the top, there will never be a gap in backgrounds before the second half of the pair appears on the screen.
  • Apply 1 or more static background images to create points of reference for the character
  • Have 2 pairs of moving layers: a fast pair and a slow pair. In my implementation, each pair had their own image, but the image was the same within the pair. The images were just basic star maps I made in The Gimp using various colors and sizes of dots placed randomly around the image.
  • Once an image has moved from the bottom of the screen to the top of the screen and then completely off the screen, reset its position to its original starting position (either y = SCREEN_HEIGHT or y = SCREEN_HEIGHT * 2)
That's basically it. It took a lot of tweaking to get it to that level, but in the end it's pretty simple, but does the job nicely for this project. Horizontal parallax is a bit more involved, but not too far of a reach from what I've done here.

Asteroids
Since we're in space, I figured what better obstacle than asteroids and space debris? I browsed Google Images for something I could work with and then doctored it up in The Gimp. I essentially cut my own shape out of an image of a real asteroid and then colorized the image with a nice bright color. I ended up with 4 different colors if I recall correctly and they actually look really nice.

Next, I had to decide where the asteroids would be "attacking" from. I toyed with the idea of having them come from every direction, but felt this would make it too difficult. Then I thought about maybe just up from the bottom and down from the top, but in practice, it took away from the parallax effect and it was no longer believable that the character was falling. I settled on just having the asteroids appear at a random X-value at the bottom of the screen and head upwards toward the player and I'm very happy with the result of this.

I also had to determine how many asteroids I wanted to generate on each update of the game loop. I just went simple and kept track of the number of updates and used the modulus function to only generate an asteroid every 5 updates and it works surprisingly well.

The Character
Next, I needed a fun character to play the main role in the game. I ended up deciding on a cute little leaf. It fit the theme of falling, and I thought there was some neat stuff I could do with a leaf in the game. When I threw the leaf in initially, I knew I had a problem: Only allowing left and right movement wasn't selling the parallax at all.

I essentially had a static image that would just move in a straight line from one side of the screen to the other. So, what could I do? Hey, it's a leaf, how do leaves fall? They sway back and forth, right? So, my leaf should do the same! I used the same modulus trick to tackle this one, and every 5 updates I would rotate the leaf image by 90 degrees. This gave it a nice floating, flipping effect as you see leaves demonstrate when they fall. Once again, modulus saved the day!

Scoring
For scoring, I toyed with the idea of adding objects that you wanted to collide with (power-ups, bonuses, etc...) and the player would get points from those. I tossed this idea because with the random nature of how asteroids were being created, it would have proved difficult for the player to obtain these power-ups. I decided to just award points to the player for staying alive. I got lucky and picked some value multipliers at random and they ended up working out and I stuck with them:
  • The player has 3 lives
  • 1 point is added to the player's score on every game loop update
  • 100 points are deducted for colliding with an asteroid
  • On your 3rd collision, no points are deducted, thus allowing you to finish with the amount of points you've obtained since your last death.
The idea here is to facilitate high scores to keep the player excited and interested in beating their high score. With the scoring and lives, I had to go and implement a simple HUD (heads-up display). It's pretty basic and renders the number of lives (complete with mini images of the leaf) and the current score in the upper-left hand corner of the screen, and the high score in the upper-right hand corner of the screen.

Extras
After some play-throughs, I realized how fun and addicting the game was. It lacked one key thing though: score persistence. I found myself wanting to beat my high score, but only to find that I couldn't keep track of it! I whipped up a save and load mechanism to keep the player's score on disk. I also added the ability to play again once you died instead of having to restart the game.

Conclusion
This project was a total success. I finished the entire thing start to finish in under 10 man hours, and that counts dallying around with images! I'm very pleased with the outcome in terms of the goal I set out to achieve, and honestly didn't expect the game to turn out to be as fun as it is!

Screenshots