Project: Creating a Game Using Python & Pygame

A while ago I thought it would be a good idea to learn the basics of Python. I’ve seen it pop up multiple times and people seemed thrilled about it.

And since I learn the most by doing projects, I decided to start a new one.

The Goals of This Project

The goals of this project were simple:

  • Learn the basics of python and object-oriented programming
  • Create something cool with it
  • Have fun

Simple, however, doesn’t mean easy. Finding a project that combined all 3 things turned out to be more difficult than I thought.

At first, I came up with a bunch of ideas, but none of them were usable. Some projects were cool but too big for a starter project. Others were small enough to start, but not exiting at all. After a couple of weeks of frustration, I came across an old dusty box at my parent’s house.

When I opened it up I noticed it contained all of my old NES games. After reminiscing about the good old days where videogames weren’t riddled with microtransaction and other hot garbage, I noticed one game I had forgotten a long time ago: Kirby’s Adventure.

I remembered it having a minigame where you duel different characters and had to draw your weapon before time ran out.

Kirby quickdraw minigame

I always thought this was a fun minigame, so I decided I was going to create my own version of it.

How It Was Made

Creating the game consisted of 4 parts:

  1. Writing the code
  2. Creating the graphics
  3. Creating the animations
  4. Adding the sound

Since the goal was to learn the basics of python, I started out by writing – or attempting to write – a basic version of the game.

Writing the code

At the start of the project, I had no clue what I was doing. I didn’t know how to create a new window, how to detect user input, or how game loops work.

I started googling around and found some tutorials on the basics of creating games with python. All of them used a python module called PyGame, so I thought I’d use it as well.

After figuring out the basics of pygame, I created a version of the game that worked in the console. If I could get it to work in the console, I thought, I could create the whole thing. Having a working prototype also made it much more fun to work on.

The main logic behind the game is simple:

A timer starts and after a certain period of time, the duel gets triggered. If you don’t react fast enough, you lose.

This meant I needed 3 main variables:

  1. The current time
  2. The time until the duel (current time + a random integer)
  3. Time until death (time until duel + the bandit’s reaction time )

Using these 3 variables, I was able to create a very basic duel. It was impossible to win since it wasn’t registering user input yet.

After I got the console version working, I started adding stuff to the game. I added a background and some static characters. I added text so I could see what it looked like in game. I added user input,… After a good deal of time, the game started taking shape.

There were definitely some hiccups along the way, though. Figuring out how to implement animations took longer than expected and I ran into quite a few bugs. In the end, however, I managed to create a solid first project. Check it out:

After the project was done and everything was coded. I noticed some things could be done more efficiently.

At first, I had a separate class for the player and the bandit, but I noticed they were so similar I might as well create one character class. This cut the amount of code needed for the characters in half.

Creating the graphics

Because the goal was to learn python, I wanted to keep the graphics as simple as possible. I thought to myself: “Maybe I’ll create some stick figures, a simple backdrop, maybe a cactus or two to set the scene, nothing special.”

But then scope creep kicked in and simplicity went straight out the window.

“What if I add a saloon in the background? What if I make it look like they’re in monument valley? Oh and what if I give them hats? Fuck yeah they definitely needs hats!”

Before long, I was creating a full scene and I was spending more time on the graphics than on the entire game.

Project test style
I was planning to create an entire town

A few days in I realized this way was going to take waaay to long. So I looked for other ways to make it look cool, while still keeping it (somewhat) easy to draw. That’s when I came across a poster from Red Dead Redemption 2:

I loved the aesthetic of this poster and thought I could do something similar for my game. So I gave it a shot.

Project test style 2

This looked way cooler than what I originally created, but it had so little detail it made animating a problem. Once one arm moves in front of the body, you have to guess what is going on.

I ended up creating something in the middle. Something that had enough detail in it so you could see what was going on, but still simple enough so it didn’t suck up all of my time.

Project final style

Creating the animations

Animating was the part I was worried about the most. It can be tedious when you’re a pro and a nightmare if you’re doing it once in a while.

Luckily, people who are way smarter than me have created tools to make it less of a drag.

The DUIK plugin for After Effects, for example, helps you rig characters and applies inverse kinematics so you don’t have to animate every single element.

This allows you to put controllers on the arms and legs (the weird shapes on in the gif below), and animate those. DUIK will take care of the constraints and joints.

DUIK Controller example

Thanks, smart people.

For this game, I created 2 animations:

  1. Shoot
  2. Getting shot

It took a while to get right, but in the end, I was pleased with how the animations turned out. Once I created the animations for one character, I switched out the head sprites and mirrored the entire composition.

Adding the sound

I didn’t spend too much time on sounds. I just downloaded some audio packs for Counterstrike, picked a sound that might fit the scene, and implemented it.

For the title screen, I took a piece of music from a guitar tutorial on youtube and used it as title music.

As for the background noise, I found a free clip of wind blowing and just put it in. It won’t win an academy award, but it’s good enough for this project.

Now let’s go over some of the things that went well, and some of the things that could have been better.

The Good…

It works!

It satisfying to see something you’ve created come to life. This project was a challenge, but I learned a lot working on it. It helped me get a basic understanding of how object-oriented programming works. Being able to reuse classes is neat.

This project also taught me that I don’t need to know everything in order to start creating. In the beginning, I didn’t even know how to create a new window in pygame but I’ve still managed to create a good looking game. Pretty proud of that.

The visuals and animations came out better than I expected.

…The Bad…

Since this is the first thing I made in Python, there are some things that could have gone better.

The first is scope creep. Halfway through the project it became so big it started to frustrate me. I wanted different kinds of bandits, a menu where you could choose your difficulty, a gunshot sound that has the sweet ricochet sound it it like in westerns. The list just kept getting longer and longer.

All these things required more research and more trial and error. Even though those ideas are cool, I thought it’d be better if I made it smaller and actually finished the damn thing.

Code wise, there are certain bugs that I managed to fix, but I still don’t fully understand what’s causing the error.

Take this piece of code for example:

def events(self):
		self.allow_key_press = True
		for event in pg.event.get():
			if event.type == pg.QUIT:
				self.playing = False
				self.running = False

			if event.type == pg.KEYDOWN and event.key == pg.K_SPACE:
				if self.gamestate == 'Startscreen':
					self.gamestate = 'Waiting for duel'
					self.startduel(BANDIT_DIFFICULTY - self.game_level * DIFFUCULTY_MODIFIER)

				if self.gamestate == 'Duelling':
					self.game_level += 1
					self.gamestate = 'Player won'
					self.allow_key_press = False

				if self.gamestate == 'Player won' and self.allow_key_press == True:
					self.gamestate = 'Waiting for duel'
					self.startduel(BANDIT_DIFFICULTY - self.game_level * DIFFUCULTY_MODIFIER)

				if self.gamestate == 'Game over':
					self.game_level = 0
					self.startduel(BANDIT_DIFFICULTY - self.game_level * DIFFUCULTY_MODIFIER)
					self.gamestate = 'Waiting for duel'

			if event.type == pg.KEYUP:
				self.allow_key_press = True

This handles all the user inputs in the game. If the spacebar is down, it checks what game state the game is in. Depending on the state, different things happen.

The problem starts when the player wins a duel. In an ideal world, you’d win and you’d have to press the spacebar again to move on to the next stage. In reality, winning a duel triggered the next one right away. This didn’t happen when you lost or in any other game state. I figured this could have two causes:

  1. Something else in the code was triggering a new duel
  2. Holding the spacebar down for longer than 1 frame/tick after winning triggered the next event.

After pulling my hair out checking the code for the millionth time, I concluded it was most likely the second reason.

To fix this issue, I added a variable that controls when a key can be pressed. If you’re in a duel and you win, the game requires you to release the spacebar before you can press it again.

This solves the problem, but I’m not 100% confident that was the main cause of the issue.

…And The Ugly

For a game this small, it takes a long time to load (6 – 8 seconds).

The reason why is because I didn’t use sprite sheets for the characters. All of the character frames are huge (1024 x 900px). Putting them in one giant sprite sheet just wasn’t feasible (I tried and it ended up being multiple 8000+ by 8000+ px sprite sheets).

Instead, it loads all the images when initializing the characters and puts them in an array. It works but the loop takes up time. So yeah… not a very efficient use of images.

Other than that, I’m happy how this game turned out. I learned a ton creating this and I’d love to create more projects like it.