Why learn to make games?

This year, at PyCon, I'll be joining Richard Jones to teach a tutorial on PyGame. I'm no expert at PyGame (he is), so I'll be covering game design and theory, since it's something I wish people put more thought into, rather than impulsively shoving bits of sucessful franchises into the empty husk of a big title, like Dr. Frankenstein trying to sort out what he's going to do with all these leftovers.

Ahem.

Why would someone want to learn to write a game? We've all heard about the hell that is working in the gaming industry, and I've met very few veterens of Python that have any interest in joining it. We all know that being the next Minecraft is a one-in-a-million shot, and that even Minecraft came to the table with decks stacked (Notch is a pretty smart dude, and had been doing this for a while). If you want a game, why shouldn't you just buy one?

It makes you a better coder

Games are finicky things. Unlike systems where all we're doing is moving around bits of text and the occassional image, games are practically nothing but images. Even the text is treated as an image. You have to be more conservative with your resources, watching how every function affects the load on your system. Sure, that ponderous animation you wrote up works fine when you just have one little dude on the screen, but what about when you have fifty? Or add particle effects, or explosions? You know you want explosions, so maybe you should tidy that up now.

It also forces you to organize earlier. You can't just dive in with a game. You need to have a plan, because at the very least, you need some graphics. If you need graphics, chances are you're going to mock something up, which will at least give you a vision of where you're going. I know many developers who never bother to do a mock-up when doing a web app, figuring they'll work that out 'later.' Writing for a game opens your eyes to how much easier having a vision makes development.

You will finally learn to make graphics

Every developer should know how to make their own graphics. Yes, there are designers in the world, but few of them will willingly give up their Saturday to make you an orc, gratis. Now is the time that you'll learn that making okay game art is not as hard as you've been lead to believe. It just takes time. 

It won't be fabuluous, but there was a time when everyone was taught how to draw. It wasn't seen as teaching art: it was a life skill. Okay, a life skill for the monied, but still. Making graphics should be one of your life skills. If your game does turn into the next Minecraft, you can have a visual artist overhaul the thing for you once your funding rolls in.

It's crazy awesome fun

Oh my goodness. It is such crazy awesome fun.

I didn't think it would be this much fun, honestly. I thought, meh, making websites is easier, I can do most of the prettifying with CSS and webfonts. Games are work.

Games do take a lot of work. I spent a lot more effort getting my little guy to move around than I did creating fake bank for my kids. It was addicting, though. Once I got him to move, well, I had to make some monsters, and move them around, too! And chests, oh, I needed chests! And the chests need things in them, what will I put in them...? It's layers of fun, because everything you make could have something even awesomer inside it!

I could have spent my mornings playing a game that was already done, but I found myself having more fun hacking at one that will never sell a million units, and that,hell, if I get 100 downloads, I'd be dancing. It made my kids laugh when I replaced the walls with silly tornados, or let them make me a new tileset. My daugher now understands that when I change the code, I change how my little man acts, or how many monsters there are, or what is in the chests. She thinks that's awesome.

And you know what? It totally is.

Pygame: Getting started

As I've mentioned before, I'll be helping Richard Jones with a tutorial on PyGame at PyCon this year. When I agreed to help him, I admit, I knew nothing about PyGame. I was coming on as someone who would talk about tropes and designs and pitfalls (and the crusher of a few dreams). I thought it would be wise to pick it up, though, so last month, I started playing around with it.

Getting started

System: Linux Mint 11

Katie, giving her laptop a dark look. Caption: Please make this code sentient, so when I kill it, it will feel pain.

The first thing I tried to do was to get PyGame installed into a virtualenv. This went... poorly. gcc kept throwing the dreaded "Status 1" error that makes me want to put my fist through something. I spent the better part of an hour Googling and trying to tweak the env, until I finally said Screw it, and tried a system-wide install.

Worked first time.

Fine. Be that way. Whatever.

System: Lion

Curious, I tried the virtualenv trick on Lion, and came up with errors there, as well. This time, though, doing a global install from source failed just as badly. I ended up having to use one of their pre-packaged distros. I hate doing that. I always come away feeling dirty.

Round zero - Decisions, decisions

I'm not starting with zero to be cute. I'm starting with zero because there's something very important that needs to be done, once you've established that your machine can, in fact, work with PyGame.

You have to figure out what your first project is.

Now, I have some dream games that I'd love to make. I'd love to make a sim, or a quirky RPG, or a sandbox game, but I don't even know if I'm up for making Pacman at this point.

Rather than spending weeks writing out requirements for a game before I know how to make one, I decided to just copy something that's already been documented to hell and back. I decided to make a roguelike.

A rogue character standing with a large red D behind him.

Roguelikes, one of the worst named genres ever, are nearly as old as the personal computer (if you discount the ones that weren't mass-produced). The basic premis is that dungeons are automatically generated and populated, and you descend levels until you get to some big baddie a the bottom. I wouldn't have to think about requirements when you start coding, as they're already written out for me.

Why not just make my dream game? Because there are subtle issues that crop up, and how you decide to solve these issues can impact design decisions down the road. You're already going to screw things up. Why screw the whole pooch?

Now that I knew what I was doing, I knew I would need some graphics. Sure, I could just make circles and squares and make do, but if I knew I was going to move to a graphic game, why not start out right? After struggling with Gimp, I was able to produce three things I knew I would need: a player, and a board.

I also had a list of things that I knew I wanted working for my first attempt: 

The list looks skimpy, but trust me, while learning you want a short, easy list. Impementation can be more subtle than expected.

Round one - Diving in

And here is where I made my first bad decision: I didn't start out with an object oriented design. My only excuse was that I was excited, and I was just trying to get the screen to pop up and look pretty.

# INTIALISATION
import pygame, math, sys
from pygame.locals import *

screen = pygame.display.set_mode((1024, 768))
player = pygame.image.load('dude.png')
clock = pygame.time.Clock()
k_up = k_down = k_left = k_right = 0
direction = 0
position = (300, 300)
BLACK = (0,0,0)
WHITE = (255, 255, 255)

def main():
	while 1:
		clock.tick(15)
		hor = 0
		vert = 0
		for event in pygame.event.get():
			if not hasattr(event, 'key'): continue
			if event.key == K_ESCAPE: sys.exit(0)
			if event.key == K_LEFT: hor = -25
			if event.key == K_RIGHT: hor = 25
			if event.key == K_UP: vert = -25
			if event.key == K_DOWN: vert = 25
		x, y = position
		x = x + hor
		y = y + vert
		global position
		position = (x, y)
		screen.fill(BLACK)
		screen.blit(player, position)
		pygame.display.flip()



if __name__ == "__main__":
	main()

The above certainly does that, as well as lets my little guy move around. There's a little bug in the above code that followed me around for a while, until I fixed it much, much later. No, I won't be telling you what it is... yet.

Round two - Doing it right

Happily, I remembered how to actually code properly, and tossed the above screwing-around code for some proper classes. Had I been thinking farther ahead I would have split things even more. As I've said before, making games is crazy fun, so it can be easy to get carried away.

# INTIALISATION
import pygame, math, sys
from pygame.locals import *


BLACK = (0,0,0)
WHITE = (255, 255, 255)
TILES_ACROSS = 20 - 1
TILES_DOWN = 15 - 1

class Map(object):
	def __init__(self):
		self.map = []
		for i in range(TILES_ACROSS+1):
			row = []
			for j in range(TILES_DOWN+1):
				row.append(0)
			self.map.append(row)

	def clear_block(self, position):
		x, y = position
		column = x/50
		row = y/50
		print "Column %s, Row %s" % (str(column), str(row))
		self.map[column][row] = 1

	def print_ascii_map(self):
		for row in self.map:
			print row

class Game(object):
	def __init__(self):
		self.screen = pygame.display.set_mode((1024, 768))
		self.player = pygame.image.load('dude.png')
		self.bg = pygame.image.load('rainbowbg.png')
		self.clock = pygame.time.Clock()
		self.direction = 0
		self.position = (300, 300)
		self.map = Map()
		self.map.clear_block(self.position)
		self.run()

	def draw_darkness(self):
		print self.map.map.__len__()
		for row in range(TILES_ACROSS+1):
			for col in range(TILES_DOWN+1):
				if self.map.map[row][col] == 0:
					pygame.draw.rect(self.screen, BLACK, (row*50, col*50, 50, 50)) 	

	def move(self, hor, vert):
		x, y = self.position
		x = x + hor
		y = y + vert
		if x > TILES_ACROSS * 50 or x < 0 or y > TILES_DOWN * 50 or y < 0:
			return
		self.position = (x, y)
		self.map.clear_block(self.position)
		self.screen.blit(self.bg, (0, 0))
		self.draw_darkness()
		self.screen.blit(self.player, self.position)
		pygame.display.flip()

	def run(self):
		while 1:
			self.clock.tick(30)
			hor = 0
			vert = 0
			for event in pygame.event.get():
                        	if not hasattr(event, 'key'): continue
                        	if event.key == K_ESCAPE: sys.exit(0)
                        	if event.key == K_LEFT: hor = -25
                        	if event.key == K_RIGHT: hor = 25
                        	if event.key == K_UP: vert = -25
                        	if event.key == K_DOWN: vert = 25
				self.move(hor, vert)
				self.map.print_ascii_map()

def main():
        while 1:
		game = Game()

if __name__ == "__main__":
        main()

Now, everything is snug in its own class. By accident, I made a good design decision above, making a master class called Game that would keep track of all of the pesky details within the game itself.

Explanations

At this iteration, I had a map, a player, a layer of darkness to obscure where the player hadn't been yet, and absolutely no documentation. Bad Katie. As I said, the fun can get in the way of being a good person, sometimes. Here are some explainations of what I have above:

Map

I needed something that would keep track of what blocks had been cleared. Though there was nothing to hide yet, I knew that I would eventually want to implement fog of war. I also tossed in a sanity checking function that prints out the ascii map on request. It's easy to screw up movement, even when it's just up, down, left, and right.

Game

The game itself keeps track of all the parts of the game with an init that's only going to grow. I also put it in charge of drawing everything, a move that I may regret later. For now, the player is a nebulous quality within the game itself. This is something that would have to change later.

Blits and flips

I admit, these confused me at first. A blit is just an image that is being drawn onto the screen on top of whatever else is already there. So, I draw the floor, then I draw the player. If I reversed this, the player would be under the floor, and the game would be a bit confusing.

Once an image is drawn, however, it isn't shown. PyGame draws everything, then reveals what it's drawn by 'flipping' the screen. It makes the graphics much more smooth, apparently. There's also an option to reveal the changes to just a part of the screen, but I wasn't rad enough at this point to play with that.

Event keys

Once the game is started, it starts listening for events. Any event that happens, such as a mouse click or a key stroke, goes into pygame.event. It's up to us to determine which of these events we want to actually use. I only care about movement and quitting, so I just watch for left, right, up, down, and escape. That's right. No WASD. I had enough to debug at this point. I didn't need to add optional movement choices.

Want to play with what I did?

Here's the commit!

Next

I get serious about splitting things up, and we find Treasure!

PyGame: Treasure!

Want to see where I am now? Here's the code! 

Setting some defaults

Now that I was hip deep in moving my little dude around, I realized that I was going to need some defaults. In games, nothing is a given. You might need to make the board bigger, smaller, change the size of graphics, or even swap out colors.

BLACK = (0,0,0)
WHITE = (255, 255, 255)
COLUMNS = 16
ROWS = 21
TREASURES = 10
TILE_SIZE = 48

Had I been less eager, I would have started off with defaults written out. Seeing as how this was only the second session, however, I think I can be forgiven.

Since a rogue-like is a fairly basic game, I didn't need too many defaults: a few colors, columns and rows, and the size of the tiles for movement.

Colors in PyGame

Colors in PyGame are set through triads of red, blue, and green. Colors are the first thing I recommend you go ahead and add to defaults, as they can be annoying to puzzle out later.

I don't have any specific tool that I use to calculate RGB. I tend to use what's at hand. When I'm using my Mint box, I use Gimp. When I'm on my Mac, I pull up any one of a million, billion websites that do color conversions. What I'm looking for is RGB values:

A screen shot of a Gimp color picker, with the RBG values highlighted

I can take those, and make some default colors that will make reading the rest of my code so much easier.

BLACK = (0, 0, 0)
WHITE = (255, 255, 255)

This way, if I decide later on that I want my whites to be off-white for usability reasons, I just have to change one value.

Why go binary?

I made a major change at this point, and it wasn't adding treasure: I moved to base 2.

Why do this? I'd never really thought about base 2 numbers, aside from the fact that they would crop up in computing. It was just one of those things that you learn to expect. I can't recall ever having it explained to me, and it wasn't quite interesting enough for me to look into.

I stumbled into the reasoning by accident, while reading, of all things, a crazy novel about religious extremists and MMO gold farmers.

Having everything in base two makes everything halve more easily, which is something you do all the time when making a game.

Let's say I have a tile width of 50px (which is what I started with). If I want something to be half on that tile, I can move it 25px. But what if I want it to be on a quarter of that tile? Now we're at 12.5... and there is no such thing as half a pixel.

As soon as I was out of the bath (yes, I read in the bath), I switched my tiles to be 48x48. Now, we were divisible all the way down, something that would become very important later.

Treasure!

Now that I had a little guy moving around a dungeon, I decided he needed something to find in the dungeon. Treasure! Finally, I had my Treasure class do something besides sit there and look sad.

ALL_TREASURES = {
     "hat": "Quite cunning",
     "sqord": "Knock-off sword. Probably from Ikea.",
     "book": "What the hell are you going to do with this?"
   }
LONG_STRING = "X" * 50

class Treasure(object):
	''' Not implemented yet. 
	'''
	def __init__(self):
		k = ALL_TREASURES.keys()
		r = random.randint(0, ALL_TREASURES.keys().__len__()-1)
		self.title = ALL_TREASURES.keys()[r]
		self.description = ALL_TREASURES[self.title]
		

The Treasure class, in its pupa stage, only has two things: a title, and a description. Why not go hog wild? Why not give it attributes and effects and all the awesomesauce I can?

Rogue discovering a chest. This delights him. Caption: Finally! Purpose!

Why? Because there's more things to consider before I go crazy. Being a little bit clever with the titles and descriptions was really all the complications I want at the moment. After all I have to get them on the board.

I decided to go with adding another layer to the Map. It worked for making the darkness, so it should work for treasures. Adding random treasures turned out to be quite easy, as was clearning them. Huzzah! This is going to be super easy to write!

Viewing area and fog

I realized, after placing treasures, that my rogue needed to be able to see a bit better. I paused on the treasure feature to give my guy a field of view.

I modified my move function to clear the blocks around the rogue, but only slightly. I still wanted some shadows, so my cleared layer got some subtle tweaks. Now, if the rogue hadn't been in a block, or by it twice, that block would be shadowy.

Screenshot, showing rogue's path through the darkness. There are shadows where he can see, but it's still kind of dark. Caption, with arrow pointing to shadows: Sort of dark here!

The 'walked by it twice' bit was an accident, but one that I ended up keeping. I imagined my little guy squinting into the darkness, looking really, really hard for treasures, worrying about the day I decide to add monsters.

One thing that bit me here: if I moved my rogue to the bottom of the screen, the top would light up. I was completely confused until I read up on some subtlties with python's arrays. They accept negative numbers as the index.

>>> a = [1,2,3,4,5]
>>> a[-1] = 0
>>> a
[1, 2, 3, 4, 0]
Lesson learned: test for your bounds.

Back to treasures!

Now that my guy could actually see treasures, I could get back to making them awesome! Placing and picking up treasure was a cinch, but I was pretty sure that the player would want to know what they had picked up. Time to add alerts and an inventory.

I already had some dead area, due to the size I'd made my tiles and board. Whenever my guy ran across a treasure, it would be added to an inventory, and its title and description would be displayed in an alert area at the bottom of the screen.

I discovered very quickly that text likes to persist in PyGame. My hacky solution was to write over the existing text with a bunch of dark letters before writing something new.

# In the Game class...
def draw_alert(self, alert, color=WHITE):
		''' Draws the alert box at the bottom 
		'''
		self.alert = self.font.render(LONG_STRING, True, BLACK, BLACK)
		self.screen.blit(self.alert, (0, 790))
		try:
			pygame.display.flip()
		except:
			pass
		self.alert = self.font.render(alert, True, color, BLACK)

This is a horrible, horrible solution, and it fills me with shame. Works, though.

Is that bug still there?

Oh yeah. Found it yet?

Next time

We add monsters! 

Pygame: Monsters and Walls!

Where am I in my code base? Here!

Walls

One feature I know I'm going to need in the future is rooms. A dungeon isn't very interesting if it's just one big featureless room. No, no, you need a lot of little featureless rooms to make your game sing!

I considered briefly coding for rooms, but decided to start with something even more granular: a wall. It ended up being the right decision. Several design decisions popped up almost immediately.

That last one seems like a no brainer, right? Of course it shouldn't! You'd be surprised how many modern games allow for the user to sense what's beyond a wall. Next time your game has a minimap, check out what happens when you belly up to a wall. Many will show a ghostly outline of what's just beyond your field of view.

A Diablo 2 screenshot of the mini map. The user has never been to a certain area, but they can see the outline of the room. Caption: How do I know about this?

Either all of these developers are lazy, or there's some advantage to making players mildly psychic.

I decided I would solve this in the time honored way that many developers have solved such conundrums before me: ignore it for now. Chances are, if I code the solution for this now, I'll just have to redo it when I add in rooms. Might as well save the effort.

As for the other issues, I decided that running into walls is a perfectly valid behavior for people and monsters to have. I do it all the time. Why should they be any different? When I make smarter monsters, I may have them see walls and not run into them. Maybe.

I'm not sure I want monsters that are more perceptive than me.

Monsters!

The rogue has encountered an ooze, and is terrified.

Now that we have something for my little guy to pick up, we now need something to smack him back down.

My first monster isn't really all that terrifying. He's not that bright. He can't do any damage. He can't chase after the hero. He's really quite dull.

Dull is good, though. Dull is a good place to start coding. I decided the only thing I wanted my monster to do was move. That should be easy enough, right? I made another layer in my Map class, and expended its init for adding monsters to random tiles.

Once they were technically on the board, all I had to do was render them. This, I added to the Game class, since that was doing all the heavy work of rendering. For once, things went as I wanted. The monsters were added, each its own distinct object, and if I took darkness away, I could see that they populated the full bounds of the board. Huzzah!

Then I decided to move them.

I had the monsters move every time the hero moved in a random direction. I'd already learned about testing the boundaries of the board, so they didn't try to move outside of the board. If they tried to move onto the same tile, however... they overwrote each other. Only one monster would emerge from the tile, the other doomed to the garbage collector. They also had the annoying habit of jumping over my walls, which is not something derpy monsters should be doing.

Dammit.

Checking tiles

One incredibly important function I hadn't realized I needed yet was something that would check to see if a tile was open. That got written in a hurry and added to the Map class.

def is_block_empty(self, row, col):
    if not self.treasure[row][col] \
    and not self.monsters[row][col] \
    and not self.walls[row][col]:
        return True
    else:
        return False        

It's not the most elegant bit of code, and it's highlighting where my method of 'just add another layer' may have issues down the road. For now, though, it works. I'm now starting to think about version 2.0, and how I might better deal with layers.

Why not just stop and fix it now? Because I'm not sure I know how I want to fix it. That's the beauty of prototypes. At the end of the day, you trash the project. The product of the prototype isn't something you use: it's all the lessons you learn.

Inventory

My guy has treasure, so now we need a way for the player to see what they've collected. After all, monsters imply fighting at some point, so the sqord he's been toting around will be for more than show.

Three kinds of inventories: slot based, volume based, and weight based

Rather than just have a list of items stored somewhere, I decide to make the Inventory a class of its own. Sure, it's just a list of things right now, but inventory management is one of those things that can get stunningly complex. Do we have a weight limit to the bag? A volume limit? Do items take up varying amounts of volume, like in Diablo, or is it a thing-per-slot system, like Torchlight? Are we going to completely screw with the player by making it a weight AND volume system? Or is this bag simply a Bag of Holding, that can hold an infinity of objects?

With all these unanswered design decisions (I don't know how much I dislike the player, yet), it's better just to create a simple class that I can build off of, right now.

class Inventory(object):
    ''' The inventory for the player.
    '''

    def __init__(self):
        ''' Sets up the initial blank inventory.
        '''
        self.inventory = {}

    def get_items(self):
        return self.inventory.keys()

    def add_to_inventory(self, item):
        ''' Adds an item to the inventory
        '''
        try:
            self.inventory[item] += 1
        except:
            self.inventory[item] = 1

Rogue guy, with a cunning hat, a rainbow, a sword, and a book. He is proud of his epic loots.

This set-up may not be perfect, but it will allow my little guy to carry more that one type of an item. I know I'll have to print out my inventory, so I might as well have a function that will return all the unique items in the bag.

Now that I have this nifty function for giving me all the items in the inventory, I suppose I should display them, huh? Displaying the inventory can be done in any number of ways: key stroke, icon, constant HUD, text listing, etc. I would like to say that I thought long and hard about how I would display my inventory, but that would be a dirty, filthy lie.

I had a big black space to the right side of my screen, and I decided to stick it there.

To the Game class, I added the following function:

def draw_inventory(self):
    self.screen.blit(self.inventory_screen, (1008, 0))
    items = self.inventory.get_items()
    for i in range(items.__len__()):
        line = self.small_font.render(LONG_STRING, \
            True, BLACK, BLACK)
        self.screen.blit(line, (1008, ((i+1)*15)))
        pygame.display.flip()
        for item in items:
            line = self.small_font.render(item, \
                True, WHITE, BLACK)
            self.screen.blit(line, (1008, \
                (items.index(item)+1)*15))
        pygame.display.flip()

It's simple, and likely going to change as my UI gets more complex. For now, though, having it on the side means I don't have to hit any keys or icons while I'm trying to debug treasure functionality.

The bug!

By this commit, I had found the movement bug! Basically, every keystroke is, in fact, two events: KEY_DOWN and KEY_UP. Adding a check for KEY_DOWN made it so that I wasn't moving twice for every keystroke:

for event in pygame.event.get():
    if not hasattr(event, 'key'): continue
        if event.type == KEYDOWN:
            if event.key == K_ESCAPE: sys.exit(0)
            if event.key == K_LEFT: hor = -TILE_SIZE
            if event.key == K_RIGHT: hor = TILE_SIZE
            if event.key == K_UP: vert = -TILE_SIZE
            if event.key == K_DOWN: vert = TILE_SIZE
            self.map.move_monsters()
        self.move(hor, vert)

Feedback!

I've been getting super feedback in posts about making the game work better, or about some bugs that people are spotting. Awesome!

Since I code a bunch, then write the blog posts, some of these fixes aren't going to show up for a while. I'm not ignoring you! For instance, Joshua found the another issue I had been having with movement. Marius contributed a better way to add choices to the game.

I was also alerted to an upcoming seven day roguelike challenge by purplearcanist. Though I don't have time to get involved, contests like this can be fun to jump into, or at least watch.

Getting kind of long, isn't it?

Next time, we start actually organizing the code and media, so it's easier to manage and grow.

PyGame: Fog of War and Organization

Where am I? Here!

Organization

Up to this point, I had been diving into the code, head-first, without any consideration as to organizing it. Bad Katie. I took some time to break out my code into more manageable chunks, and finally put all of my images in their own directories. My file structure ended up looking like this:

- roguey
    __init__.py
    - classes
        constants.py
        game.py
        gamemap.py
        items.py
        monsters.py
        player.py
    + images
+ comps
main.py
README.txt

Looking into the contents of the files, it's not quite the Java-esque style of one class per file, but it's close. While I abhor Java, and only tolerate it until I can use Python and C to make Android applications, this is one pattern that can be okay to copy. I don't plan on keeping it to one class per file, though. The player is one thing that I expect to grow to include all sorts of classes, from Inventory to SpellList to CharacterStats.

Paths

One thing tripped me up briefly, so I'll go ahead and lay out it out here, even though it fills me with shame.

I have an image directory. I'd like to point to it. In fact, I have to point to it throughout the game, since I'm not doing an ASCII version of Rogue.

Python's os library has a great function that returns the current working directory. At first, I thought that it would return the current working directory of the file. It doesn't, because that would be stupid. It returns the cwd of the user. So, in order to make the full path of my images a constant, I had to add this code to constants.py:

import os
...
IMG_DIR = os.getcwd() + "/roguey/images/"

Since the game has to be run from the the same directory as main.py, this works. For now.

Also, now that I had all my class files in another directory, it became important to import them where needed. In the end, my imports ended up looking a bit like this:

A diagram showing imports in PyGame.

Darn you, Python (Maps)

If you plan on having a map class in your game, I highly recommend training yourself from the start to call it anything but 'Map'. Initially, I called my map 'Map', and this caused all sorts of strangeness when I separated things out. Python already has a Map class, and was a bit uppity that I had my own.

I renamed the file to 'gamemap.py', so Python and I are friends again.

Fog of War

Rogue guy, looking at a dying torch, surrounded by darkness. He looks very worried.

Fog of war is a nice way of making the player have memory, but be a bit less psychic. Essentially, the player can remember where they've been, but that doesn't give them the magical ability to know what's going on in those places. The simplest version of this is to not show monsters until they're in the player's field of vision.

There are some cool variations of this that I may play with in the future. For example, a listen check might reveal that a certain type of monster is in the next room. A spell might reveal creatures that are far away. Or, if I want to be really dastardly, the type of monster might not be revealed until they're in direct combat. Perhaps torches in the distance might show the player what's in its immediate radius, with blobby outlines further out.

Fog of war examples: Hard shadows, field of war with memory, and field of war with light sources.

It's a cool mechanic to play with.

For now, I'm keeping it simple. I added another layer, this time containing the view radius of the player. If a mob isn't in that radius, it isn't rendered. Everywhere else he's been on the board is now covered in a grey overlay.

# In gamemap.Map

def set_current_position(self, position):
    row = row/TILE_SIZE
    col = col/TILE_SIZE
    self.current[row][col] = 1
    for i in range(RADIUS):
        if row-i > 0:
            self.current[row-i-1][col] = 1
        if row+i < ROWS-1:
            self.current[row+i+1][col] = 1
        if col-i > 0:
            self.current[row][col-i-1] = 1
        if col+i < COLUMNS-1:
            self.current[row][col+i+1] = 1
    for i in range(RADIUS-1):
        if row-i > 0 and col-i > 0: self.current[row-i-1][col-i-1] = 1
        if row-i > 0 and col-i < COLUMNS-1: self.current[row-i-1][col+i+1] = 1
        if row+1 < ROWS-1 and col-i > 0: self.current[row+i+1][col-i-1] = 1
        if row+1 < ROWS-1 and col+i < COLUMNS-1: self.current[row+i+1][col+i+1] = 1

# In game.Game

def draw_darkness(self):
    ''' Draws the darkness and shadows on the board. 0 is dark, 1 is in shadows,
    '''
    for row in range(ROWS):
        for col in range(COLUMNS):
            if self.map.cleared[row][col] == 0:
                if not self.map.current[row][col]:
                    pygame.draw.rect(self.screen, BLACK, (row*TILE_SIZE, col*TILE_SIZE, TILE_SIZE, TILE_SIZE))  
            if self.map.cleared[row][col] == 1:
                if not self.map.current[row][col]:
                    shadow = pygame.Surface((TILE_SIZE, TILE_SIZE))
                    shadow.set_alpha(200)
                    shadow.fill(BLACK)
                    self.screen.blit(shadow, (row*TILE_SIZE, col*TILE_SIZE))

Next!

I explore "Active Gaming," and we talk animation!

PyGame: Animation and Active Gaming

Where am I? Here!

Animation

Now that I had my movement working correctly, I had another issue. The bug had the 'feature' of giving the rogue the illusion of movement through a little pause halfway through the movement. Now that the movement was gone, I found myself annoyed by his jumping from square to square.

Rogue dude sliding across the floor.

The first demo I ever used for PyGame involved animating a car and driving it around a box. Animation should be child's play, right? I decided to take my rogue, and have him move smoothly from square to square.

To move a something in PyGame, you have to step the blit through being drawn, flipping the screen, moving the bit a tiny bit, flipping again, and doing that until you get to the end point, if there even is an end point. If you've decided your blit is living in a frictionless world, then it could, technically, keep going forever.

I just want my guy to move to the next block, so I added this bit of code to game.py:

# In Game:
def animate_move(self, hor, vert, blit):
    if vert:
        if vert > 0:
            for i in range(TILE_SIZE/MOVEMENT_SIZE):
                self.draw_screen_layers()
                self.screen.blit(self.__getattribute__(blit),\
                    [self.old_row, self.old_col+i*MOVEMENT_SIZE])
                pygame.display.update()
        else:
            for i in range(TILE_SIZE/MOVEMENT_SIZE):
                    self.draw_screen_layers()
                    self.screen.blit(self.__getattribute__(blit),\
                        [self.old_row, self.old_col-i*MOVEMENT_SIZE])
                    pygame.display.update()
        if hor:
            if hor > 0:
                for i in range(TILE_SIZE/MOVEMENT_SIZE):
                    self.draw_screen_layers()
                    self.screen.blit(self.__getattribute__(blit),\
                        [self.old_row+i*MOVEMENT_SIZE, self.old_col])
                    pygame.display.update()
            else:
                for i in range(TILE_SIZE/MOVEMENT_SIZE):
                    self.draw_screen_layers()
                    self.screen.blit(self.__getattribute__(blit),\
                        [self.old_row-i*MOVEMENT_SIZE, self.old_col])
                    pygame.display.update()

What is MOVEMENT_SIZE? It's the number of pixels I want my rogue to move for every frame. I found out quickly that moving him just one or two pixels caused the animation to be extremely slow, but not any more smooth. I ended up with a movement size of sixteen (set in constants.py) before I was happy.

Well, kind of happy.

Complications from Animation

While I was delighted that my guy was animated now, I noticed that the interface now seemed... off. My monsters were still leaping to and fro, so that was jarring. The shadows were leaping, too, with only the area where the rogue was going to be getting highlighted. Movement still seemed very slow, even with reducing it to only a few frames. One small decision now made my interface feel very, very strange.

Katie looks disheartened. Caption: Well... hell.

I thought about making the monsters animated, but realized that still left the shadows leaping about. I could rewrite the shadow logic, but I knew that I wanted to rewrite it when I introduced rooms. Did I want to put up with jarring animations that slowed down my testing?

I decided that animation had become a time sink at this point. Combat still didn't exist, my player was nothing more than a bag of rainbows and sqords, I had no rooms, and I hadn't even gotten to adding multiple levels to my dungeon. I didn't even have a McGuffin yet.

I took it out, but left the functionality in there for another day.

Active Gaming

What the heck is active gaming? Am I making a Kinect version of my roguelike? Will this be on the Wii? Will the player have to sweat?

No. Active gaming allows a developer to think more deeply about gaming mechanics.

In high school, most people are taught 'active reading.' The name might differ across systems, but it comes down thinking about what you're reading while you're still reading it. You might underline text, or take notes, or reread every section before moving on. Whatever trick is used, it's meant to force the reader to engage with the material, rather than sitting back and letting it wash over him/her.

Active gaming is similar. Rather than just playing mindlessly, you take note of mechanics or plot devices or timing. You force yourself to become aware of how the game is manipulating you into playing the game a certain way. How are you brought to the next plot point? Did the developer motivate you with quest objectives, or did a big monster chase you there? How does combat work? If it's turn based, what starts a combat round? How does combat carry from round to round? Do the creatures stick it out the bitter end, or do they flee?

Let's Plays

One of the cool things about active gaming when compared to active reading is that there's more than one way to get input. This is great news for people like me who have hand issues, and can't do long gaming sessions. This is also huge for people who want to look at a game that's long out of print, or on a system they don't own.

One of my favorite ways to do an active gaming session is to wander over to YouTube and look for a 'Let's Play' for that game. Oh, you think there won't be one? You think your game is too rare for there to possibly be an LP? I found an LP of some Simpson's Driving game I'd never heard of. There are LP's of Flash games. You can find LP's for mods that add My Little Ponies to Skyrim, or for specific maps in Minecraft. People who make LP's are crazily dedicated.

Watching a Let's Play allows you to sit back and just take notes without getting obsessed about learning the keyboard commands, or what the left shoulder button does. You can pause and go back when you want to note how a certain mechanic works.

These people are also usually pretty good gamers, meaning they're not going to spend an hour locked in the bathroom because the texture of the handle is the same as the door and you just didn't notice it. Yes, I have done that. I'm not proud. Even if they're not great gamers, their death only sets you back seconds, since they usually cut out geting back to where they were before.

So, how do you do active gaming?

First, decide what you're going to be investigating. I like to go after game elements one at a time, so I can really focus on just those things. You might just focus on how movement is handled, or combat. You might spend a session tracing quests, and how you're being lead from objective to objective. I recently started working on combat in my roguelike, so I did a session just focusing on how you kill things in classic Rogue.

Note taking can be oenerous when you're trying to game, so I opt for video and audio capture. FRAPS does a great job at this, but Jing is quite good as well. I still haven't fully investigated any *nix options (sorry!). 

The idea is to talk while playing, so you can later review your findings with pen in hand. I loaded up a Flash roguelike (don't judge), started Jing, and started playing.

After I was done, I had a rogue outline for what combat was like for a basic roguelike:

Just those simple things made me sit back and look at how I was doing rounds of combat, and ended up working as a list of starter requirements for the next round of coding.

Next week!

We get down to brass tacks and start fighting!

PyGame: FIGHT FIGHT FIGHT

Stats

Now that I had decided to add combat to the game, I realized I had to give the player and the monsters something to lose. Namely, hit points. Deciding how hard they would hit would be nice, too.

Now that the player was more than a bag of stuff, he needed his own class filled out a bit more:

class Player(object):
    """The player class. Contains level, HP, stats, and deals with combat."""
    def __init__(self):
        self.level = 1
        self.attack = 5
        self.defense = 5
        self.current_hp = 10
        self.strength = 1
        self.name = "Dudeguy McAwesomesauce"

    @property
    def max_hp(self):
        return 10 + (self.level-1)*5

    @property
    def get_defense(self):
        return 1

    def receive_damage(self, damage):
        self.current_hp -= damage

    def attempt_block(self, attack):
        pass

    def get_attack(self):
        return self.strength

The imporant bits here for combat came from my old stand-by, D&D 3.5. There might be simpler systems, but this one covers pretty much anything I might want my guy to do eventually. At the moment, I'm worrying only about good old hand to hand combat, so I have an attack value (affects likeliness of landing a hit), strength (how hard a landed hit is), and defense (likeliness of absorbing a hit).

The monster got a bit of a boost as well:

class Monster(object):
    def __init__(self):
        pass

    def get_attack(self):
        return self.strength

    def receive_damage(self, damage):
        self.current_hp -= damage


class Derpy(Monster):
    def __init__(self):
        self.title = "Derpy Slime"
        self.level = 1
        self.attack = 10
        self.defense = 1
        self.current_hp = 5
        self.max_hp = 5
        self.strength = 1

The monster is a better at hitting, but has a lower defense, and fewer hitpoints. Have to keep things interesting, after all!

Combat!

A flowchart outlining a possible combat scenario.

Oh, man. Combat.

If anything in your game has you breaking down things into the most basic of flow charts, it's combat.

At first, I tried to avoid dragging out the old flowchart. I'm not in CS 100 anymore! I can totally do this in my head! Besides, I've played lots of games. I know how combat works.

Oh, the hubris of the pre-alpha developer.

A few false starts later, it occurred to me that I was going to have to start writing some stuff down. My first go looked like this:

Player moves into monster square → Player hits monster → Monster hits back → Life goes on

Simple. I decided that, even if it looked simple, I would have a module to do the heavy lifting of combat.

from player import Player
from monsters import Derpy

from random import randint

class Combat(object):

    def __init__(self, player, monster):
        self.player = player
        self.monster = monster

    def fight(self):
        '''For now, we'll always start with the player.'''
	# Player, try to hit the monster!
	hit_attempt = randint(0, self.player.attack)
	if hit_attempt == 0:
	    pass
	if hit_attempt > 0 and hit_attempt <= self.monster.defense:
	    pass
	if hit_attempt > self.monster.defense:
	    damage = self.player.get_attack()
	    self.monster.receive_damage(damage)

	# Monster, try to hit back.
	if self.monster.current_hp > 0:
	    hit_attempt = randint(0, self.monster.attack)
	    if hit_attempt == 0:
	        pass
	    if hit_attempt > 0 and hit_attempt <= self.player.defense:
		pass
	    if hit_attempt > self.player.defense:
		damage = self.monster.get_attack()
		self.player.receive_damage(damage)

I decided, since these were stupid monsters, that they wouldn't chase down the Player, nor would they stay locked in combat. They'd get their hit and go on about their business. This will likely change as I make more complex monsters, so this module is going to completely morph over time.

Looking over the code, I noticed that I had started to make myself some setters and getters, but never ended up using them. When I expand combat, this will probably be the first thing I rectify.

Also, checking for empty squares had to get more complicated. Again. UGH.

#In Map:

def is_block_empty(self, row, col):
    if not self.treasure[row][col] and \
      not self.monsters[row][col] and \
      not self.walls[row][col] and \
      not (self.player[0]/TILE_SIZE, self.player[1]/TILE_SIZE) == (row, col):
        return True
    else:
        return False

Death

Rogue and derpy monster are messed up, with weapons sticking out of them everywhere. The rogue is still cheerful, giving the viewer a thumbs up.

One thing I forgot to add at first was death. That's right, I had things running around the board with negative health. D'oh.

One thing about death is that you have to catch it right away. Checking after combat (my first attempt) lead to dead creatures getting one last lick in. Poetic as that was, I decided I wanted them to die right off. So, before trying to hit someone, I had to make sure the creature was still alive. Once we'd returned back to the game, I could deal with all the bodies.

# In Game.move:

if self.has_monster(row, col):
    Combat(self.player_stats, self.map.monsters[row/TILE_SIZE][col/TILE_SIZE]).fight()
    if self.map.monsters[row/TILE_SIZE][col/TILE_SIZE].current_hp <= 0:
        pass #put death throes here
    if self.player_stats.current_hp <= 0:
        self.end_game()
    self.move(0,0)
    return

Now, why am I calling 'move' again, with no actual movement? Because I decided that, while the rogue is fighting something, life should go on. Creatures should keep moving around, possibly complicating his life when they get smart enough to chase after him.

Next time...

We think about seperating our our visual layers.

PyGame: View seperation and throwing away code

Where am I? Here!

View seperation

Katie looks distressed. Caption: How have I not been eaten by wolves?

I did a bad, bad thing.

We're taught very early on about model-view-controller. It's hammered into us constantly. Make your view seperate! It will make your life so much easier!

It's so true, too. MVC (or MVT) is wonderful, and it really does make things so much simpler. It's just easy to deviate from, when you're excited and caught up in making something awesome and new.

Well, no more. All my view stuff is going into it's own class! Game is now dedicated to all my game mechanics, and GameScreen is going to be for all my visual bits.

Splitting Game into two parts was a tiny bit tricky, but nothing that some wine, chocolate, and deep breaths couldn't take care of. Happily, most of my 'draw' functions were already seperate, so it became a game of move a function, update all calls to it, and run the game. If it breaks, fix it. If it doesn't break, move on to the next function.

One thing I had to consider now that everything wasn't in one bucket is what GameScreen had to know. I decided that all I should have to tell it is what I wanted to draw, where. All I'm passing back and forth is a map or some coordinates (where) and sometimes some text (what). This is as it should be.

GameScreen does not know what a monster is. It doesn't know anything about the player, or the inventory, or any other object in the game. Its only concern is what it needs to draw where, and in what order. If you have to import anything besides some constants and PyGame, you're starting to see MVC bleed.

As a counter, Game shouldn't know anything about how these things are being drawn. Maybe it's ASCII, maybe it's 3D. It should only be concerned about the mechanics of the game, like who hits first, and when we get to pick up some awesome stuff. If you see that, you have MVC bleed in that direction as well.

import pygame

from constants import *

class GameScreen(object):
    
    def __init__(self):
        ''' Does the initial drawing of the game screen.
        '''
        self.screen = pygame.display.set_mode((1280, 832))
        self.font = pygame.font.SysFont(None, 48)
        self.small_font = pygame.font.SysFont(None, 20)
        self.bg = pygame.image.load(IMG_DIR + 'rainbowbg.png')
        self.player_blit = pygame.image.load(IMG_DIR + 'dude.png')
        self.screen.blit(self.bg, (0,0))
        self.inventory_screen = self.small_font.render("Inventory", True, WHITE, BLACK)
        self.draw_alert("Welcome to Katie's Roguelike!")
        self.stats_screen = self.small_font.render("ARGH", True, WHITE, BLACK)
        pygame.display.flip()

    def draw_player(self, coord):
        ''' Draws the player at a specific coordinate
        '''
        self.screen.blit(self.player_blit, coord)

    def draw_stats(self, player_stats, color=WHITE):
        ''' Renders the stats for the player
        '''
        self.screen.blit(self.stats_screen, (1008, 0))
        self.stats_screen = self.small_font.render(player_stats.name, True, color, BLACK)
        self.screen.blit(self.stats_screen, (1008, 0))
        self.stats_screen = self.small_font.render("Level: " + str(player_stats.level), True, color, BLACK)
        self.screen.blit(self.stats_screen, (1008, 15))
        self.stats_screen = self.small_font.render("HP: %s/%s" % (str(player_stats.current_hp), str(player_stats.max_hp)), True, color, BLACK)
        self.screen.blit(self.stats_screen, (1008, 30)) 

    def draw_alert(self, alert, color=WHITE):
        ''' Draws the alert box at the bottom 
        '''
        self.alert = self.font.render(LONG_STRING, True, BLACK, BLACK)
        self.screen.blit(self.alert, (0, 790))
        try:
            pygame.display.flip()
        except:
            pass
        self.alert = self.font.render(alert, True, color, BLACK)
        self.screen.blit(self.alert, (0, 790))
        pygame.display.flip()

    def draw_inventory(self, inventory):
        ''' Renders the inventory for the user
        '''
        self.screen.blit(self.inventory_screen, (1008, 100))
        items = inventory.get_items()
        for i in range(items.__len__()):
            line = self.small_font.render(LONG_STRING, True, BLACK, BLACK)
            self.screen.blit(line, (1008, ((i+1)*15)+100))
        pygame.display.flip()
        for item in items:
            line = self.small_font.render(item, True, WHITE, BLACK)
            self.screen.blit(line, (1008, (items.index(item)+1)*15+100))

    def draw_treasure(self, treasure_map):
        ''' Draws the treasure chests yet to be opened.
        '''
        for row in range(ROWS):
            for col in range(COLUMNS):
                if treasure_map[row][col] != 0:
                    treasure = pygame.image.load(IMG_DIR + 'chest.png')
                    self.screen.blit(treasure, (row*TILE_SIZE, col*TILE_SIZE))
    
    def draw_monsters(self, map):
        ''' Draws monsters that appear in the area that the rogue can see
        '''
        for row in range(ROWS):
            for col in range(COLUMNS):
                if map.monsters[row][col] != 0 and map.current[row][col] != 0:
                    monster = pygame.image.load(IMG_DIR + 'dumb_monster.png')
                    self.screen.blit(monster, (row*TILE_SIZE, col*TILE_SIZE))
    
    def draw_walls(self, walls):
        ''' Draws walls on the game map
        '''
        for row in range(ROWS):
            for col in range(COLUMNS):
                if walls[row][col] != 0:
                    wall = pygame.image.load(IMG_DIR + 'wall.png')
                    self.screen.blit(wall, (row*TILE_SIZE, col*TILE_SIZE))

    def draw_darkness(self, map):
        ''' Draws the darkness and shadows on the board. 0 is dark, 1 is in shadows,
        '''
        for row in range(ROWS):
            for col in range(COLUMNS):
                if map.cleared[row][col] == 0:
                    if not map.current[row][col]:
                        pygame.draw.rect(self.screen, BLACK, (row*TILE_SIZE, col*TILE_SIZE, TILE_SIZE, TILE_SIZE))  
                if map.cleared[row][col] == 1:
                    if not map.current[row][col]:
                        shadow = pygame.Surface((TILE_SIZE, TILE_SIZE))
                        shadow.set_alpha(200)
                        shadow.fill(BLACK)
                        self.screen.blit(shadow, (row*TILE_SIZE, col*TILE_SIZE))

    def draw_background(self):
        ''' Draws my glorious background.
        '''
        self.screen.blit(self.bg, (0,0))

    def draw_screen_layers(self, map, player_stats):
        ''' Draws the layers of the game screen
        '''
        self.draw_background()
        self.draw_treasure(map.treasure)
        self.draw_walls(map.walls)
        self.draw_monsters(map)
        self.draw_darkness(map)
        self.draw_stats(player_stats=player_stats)
        self.draw_player(coord=map.player)
        pygame.display.flip()

Throwing away code

Sometimes, I look at my code, and think, what horrible thing have I wrought? I know others do, too. It's tempting, to go in and try to get everything working perfectly. Sure, the combat works, but it could be more efficient! It could be more elegant! I could rid myself of the dread that Guido is going to jump out of a closet and ax-murder me!

The rogue and the derpy monster have been put out with the trash. The rogue doesn't look happy.

It's tempting, but I have to remind myself of something: I'm going to throw all this code away.

Keeping bad code is a habit that I've seen even the best developer succumb to. I spent forty hours on this! If I throw it away, I'll be throwing away all the time I spent on it! Look, just a few more hours, and it'll be better, okay? Maybe you just don't understand its innate genius.

No. It is bad code. Throw it away.

I've seen people treat code as if it's their child. Having two children, I say you should relish the things in your life that you can screw up and toss out without worry.

When I have a working roguelike, the most important thing I'll have won't be a game: it'll be the lessons I learned. At that point, I can spend a day looking over the monster I've brought into this world, and figure out where I could possibly be more efficient. Where am I duplicating effort? What did I never use? Where did I commit my cardinal sins because I was coding after my second glass of wine (again)?

Once I've bled the code dry of all its usefulness, into the wastebin it goes. With what I've learned under my belt, I can recreate the game, but this time without the stumbling about and 'Oh, Hell's of before. It won't take me nearly as long, and I can cut out the ugly bits from the start.

The only reason I decided to pull apart the screen code was because it was starting to make it harder to code, and I really wanted to talk about MVC. The time it took me to do that, however, was time I didn't spend making combat cooler, or adding more treasure, or adding rooms. In this case, I decided it was worth it, but for most things, I just choose to forge ahead.

Next time!

We're back to adding cool features! I think we need some rooms!

PyGame: Rooms!

Where am I? Here!

Algoritms

Before we dive in, I should point out that this world we're in is not short on room building algorithms. Google it, and you'll be awash in them. There's ones that do the basic rooms + hallways setup. Others generate walls in such a way that rooms naturally occur. Some add different kinds of rooms, like meeting halls and living quarters, and do it in a semi-reasonable fashion. Why don't I use one of those?

The rogue and the slime monster are in a very badly constructed room. The rogue looks worried. The monster is wearing a hard hat.

I might, eventually, use one of those (the one on Roguebasin looks especially promising). But before I do, I really want to understand the parts of the algorithm. I want to try making my own rooms and hallways before I let someone else do it for me. Will mine be awesome? Of course not! It'll be crap! It will have the minimum number of features I would want, and I bet its error checking won't be anywhere near as nice as one that's been tweaked for years, by dozens of hands, and tested by hundreds of users.

I believe in trying to do things on your own, at least once, though. I once built the world's most crap CMS ever from scratch. It not only gave me a deep appreciation for other frameworks, but made it easier for me to understand why they might have built theirs the way they did.

Making a room

I decided to start simple. All I wanted, for my first round, was to make a darn room. This is made much harder if you can't actually see the whole map, so I decided to turn off everything but the floor and the walls.

def draw_screen_layers(self, map, player_stats):
        ''' Draws the layers of the game screen
        '''
        self.draw_background()
        #self.draw_treasure(map.treasure)
        self.draw_walls(map.walls)
        #self.draw_monsters(map)
        #self.draw_darkness(map)
        #self.draw_stats(player_stats=player_stats)
        #self.draw_player(coord=map.player)
        pygame.display.flip()

Then, I created a class for my room:

class Room(object):

    def __init__(self, height=5, width=5, start=(0,0)):
        self.title = "Generic room"
        self.start = start
        self.width = width
        self.height = height
        self.end = (self.start[0]+self.width, self.start[1]+self.width)

Why? Why not just generate room like structures with walls? Because I know me. I'm not going to be able to resist tossing some flavor text in there. It also seems really useful to have a list of rooms. Maybe, at some point, I want to make sure I don't have too many monsters being generated in one room, or that no one room has all the treasure. Or, maybe I want to make a room that has ALL the treasure. We'll see.

As for adding the room, Gamemap does that, since a room is really just a bunch of walls:

def create_room(self, room):
        # make top and bottom walls
        for i in range(0, room.width):
            self.walls[room.start[0]+i][room.start[1]] = 1
            self.walls[room.start[0]+i][room.start[1]+room.height-1] = 1
        # make side walls
        for i in range(0, room.height):
            self.walls[room.start[0]][room.start[1]+i] = 1
            self.walls[room.start[0]+room.width-1][room.start[1]+i] = 1
        # fill in the floor
        for x in range (1, room.width-1):
            for y in range (1, room.height-1):
                self.rooms[room.start[0]+x][room.start[1]+y] = 1

I will not lie. Getting the walls to draw correctly took me an embarrassingly long time. I kept forgetting to subtract one, or I'd add my incremental variable to the wrong index.

At this point, though, I can at least draw a room. But I don't want just one room, do I? I want a number of random rooms! I don't want them overlapping each other, though, so I need to check all the squares that a room might contain first. Thus, check_room is born.

def check_room(self, coord, height, length):
        ''' Are all the spaces in a room free?
        '''
        for i in range(0, height):
            for j in range(0, length):
                if coord[1] + i > COLUMNS-1:
                    return False
                if coord[0] + j > ROWS-1:
                    return False
                if self.rooms[coord[0]+j][coord[1]+i]:
                    return False
        room = Room(start=coord, height=height, width=length)
        return room

I don't care if the rooms share walls, as that's often how rooms work. I don't want them to share floorspace. You might have noticed, I've added a new layer to my map for rooms. This is JUST for the open floor in a room, and is filled in when the room is created.

Now, I can get a bunch of random rooms (always generating one at 0,0, because I am boring).

def get_rooms(self):
        # Set initial room
        room = self.check_room(coord=(0,0), height=5, length=5)
        self.roomlist.append(room)
        rooms = 1
        keep_going = 50
        while rooms <= MAX_ROOMS and keep_going:
            height = randint(4,7)
            length = randint(4,7)
            x = randint(0, COLUMNS-1)
            y = randint(0, ROWS)
            room = self.check_room(coord=(x,y), height=height, length=length)
            if room:
                rooms += 1
                self.roomlist.append(room)
            else:
                keep_going -=1
        

What the heck is keep_going? It's possible for the board to be filled with rooms in such a way that no more rooms can be placed. Infinite loops are bad and embarrassing, so at some point, we just tell the game to give up and continue with loading the level.

Adding stuff back

A small room is almost completely filled by slimes and chests.

Another way having floors is useful: it makes it easier to tell where I can place something. I don't want my creatures outside of the room. That's just wasted XP, and is going to taunt the player. Happily, making things appear on floors is really easy. Just check to make sure your random square is actually a floor.

# in __init__()

for i in range(TREASURES):
    while 1:
        col = random.randint(0, COLUMNS-1)
        row = random.randint(0, ROWS-1)
        if not self.treasure[row][col] and self.rooms[row][col]:
            self.treasure[row][col] = Treasure()
            break

for i in range(MONSTERS):
    while 1:
        col = random.randint(0, COLUMNS-1)
        row = random.randint(0, COLUMNS-1)
        if not self.treasure[row][col] and self.rooms[row][col]:
            self.monsters[row][col] = Derpy()
            break

I could do something fancy, like get all the squares that are floors and pick from them, but doing it this way isn't eating up significant processing power. Now that they're back, we can start drawing them again, as well. The only thing I'm holding off on is the darkness, since it's very likely I'll be reimplementing that.

Next time!

Doors and hallways!

Pygame: Hallways

Where am I? Here!

Hallways!

In the last post, I made a reasonable number of rooms that would not overlap with each other. Rooms are nice and all, but what we really want are ways to get from room to room.

The plan

I ran through a ton of graph paper while brainstorming on how I wanted to do this. I didn't want an orphaned areas. I wanted some bends in the hallways and some reasonable variation. I wanted this plan to scale to larger spaces. I also wanted the simplest possible solution.

I finally settled on the simplest pattern I could find: give each room a door. Connect the doors, starting at the first generated room, and ending with the last room connecting to the first, using just one bend. Knock down all walls on the way. Simple, right? Super simple! I should barely be cursing at all!

Doors

Rogue guy is wielding a sledgehammer, looking determined.

The first thing I would need to make is doors. Right off the bat, I saw that this wasn't going to be as easy as knocking out a random wall. What use is a door that's in a corner, or at the edge of the map? What if the door is right next to another wall? Should I just smash the wall and connect the two rooms?

After lots and lots of sketching, I came up with the following rules:

A sketch showing where doors would be okay, and where they would not be possible.

The above lead to the following code:

def check_door(self, coord, check, next):
    ''' Can the current block be turned into a door?
    '''
    # Is it at the bounds?
    if check[0] < 0 or check[1] < 0:
        return False
    # Is it next to a wall?
    try:
        if self.walls[check[0]][check[1]]:
            # Is that wall next to another wall?
            if self.walls[next[0]][next[1]]:
                return False
            else:
                try:
                    self.walls[check[0]][check[1]] = 0
                except:
                    pass # Sometimes, we're one away from the border. That's okay.
    except:
        return False
    return True

With this, if the door is okay, I return True, with any extra blocks that needed destroying taken care of. If there are any issues, I return False, which will force the function that's trying to make the door to try again with a new random square.

def make_random_door(self, room):
    while True:
        wall = choice(DIRECTIONS)
        if wall in ['north', 'south']:
            block = randint(1, room.width-2)
        else:
            block = randint(1, room.height-2)
        if wall == 'north':
            coord = (room.start[0]+block,room.start[1])
            check = (coord[0], coord[1]-1)
            next = (coord[0], coord[1]-2)
        if wall == 'south':
            coord = (room.start[0]+block, room.start[1]+room.height-1)
            check = (coord[0], coord[1]+1)
            next = (coord[0], coord[1]+1)
        if wall == 'east':
            coord = (room.start[0],room.start[1]+block)
            check = (coord[0]-1, coord[1])
            next = (coord[0]-2, coord[1])
        if wall == 'west':
            coord = (room.start[0]+room.width-1, room.start[1]+block)
            check = (coord[0]+1, coord[1])
            next = (coord[0]+2, coord[1])
        door = self.check_door(coord, check, next)
        if door:
            self.walls[coord[0]][coord[1]] = 0
            self.floor[coord[0]][coord[1]] = 2
            room.door = (coord[0],coord[1])
            return

This way, every room has a proper door that we can use to make a hallway.

Hallways

I considered quite a few options when I thought of hallways. I played with the idea of having them bend a random number of times, or have random dead ends. I even considered making the passages twist and turn, in a more natural way. In the end, though, I went with what was simplest: start at a door, tunnel over until you're at the next door's column, tunnel up or down.

At this point, I had added a list of rooms to my map, and added the door coordinates to the rooms, so I was able to go go down the list, connecting one to the next as I went. I could have tried tunneling to the closest one, but I found I liked the branching more, connecting random rooms to each other.

def connect_rooms(self):
    for room in self.roomlist:
        i = self.roomlist.index(room)
        # If we're at the last room, connect the last to the first.
        try:
            next = self.roomlist[i+1]
        except:
            next = self.roomlist[0]

        #Determine where we're starting, and where we're ending.
        if room.door[0] < next.door[0]:
            start = room.door[0]
            end = next.door[0]
        else:
            start = next.door[0]
            end = room.door[0]

        # Tunnel over
        for x in range(start, end):
            self.walls[x][room.door[1]] = 0
            self.floor[x][room.door[1]] = 1

        # Determine where we're starting, and where we're ending, again
        if room.door[1] < next.door[1]:
            start = room.door[1]
            end = next.door[1]
        else:
            start = next.door[1]
            end = room.door[1]

        # Tunnel up/down
        for y in range(start, end):
            self.walls[next.door[0]][y] = 0
            self.floor[next.door[0]][y] = 1

Filling in

I had considered building walls around my hallways, but in a moment of fatigue, filled in the rest of the blank spaces on the board, and decided that I liked that better. Goodbye, forever, glorious rainbow background!

def fill_map(self):
    for i in range(ROWS):
        for j in range(COLUMNS):
                if not self.floor[i][j]:
                    self.walls[i][j] = 1

The Final Result

A screenshot of the Roguelike, with rooms and hallways implemented

Next

We make our items worth getting!

PyGame: Returning to Items

Where am I? Here!

Buffing up items

At the moment, Items are rather bland: they're just a title and a description. We want them to actually mean something, though, so it's time to give them a bit more shape.

class Treasure(object):
  ''' Things that can be picked up. 
  '''
  def __init__(self, title="Nada", description="", type="trash", armor=0, buff=0, attack=0):
    self.title = title
    self.description = description
    self.type = type
    self.armor = armor
    self.buff = buff
    self.attack = attack

Okay, not the most exciting upgrade ever, since we're only making armor and swords, but we're going with baby steps here. Though I've added three attributes, we'll only really be using two: armor and attack. Now that the item can have a bit more oomph, let's look at the Player again.

Getting Player ready

Right now, Dudeguy is pretty basic. I need to get him ready to deal with not only carrying things, but having those things matter.

class Player(object):
  """The player class. Contains level, HP, stats, and deals with combat."""
  def __init__(self):
    self.level = 1
    self.stats = {
      'strength': 1,
      'attack': 5,
      'defense': 5
    }
    self.current_hp = 10
    self.name = "Dudeguy McAwesomesauce"
    self.equipped = {}

    for treasure in EQUIPMENT_TYPES:
      self.equipped[treasure] = None

I decided to make stats a dictionary, since I might want to give some classes a stat that others didn't, and this seems like the easier way of doing this. Also, it's easier to iterate for my interface, once I get to that.

I've also added a dictionary for equipped items. He starts off with the standard equipment slots:

EQUIPMENT_TYPES = ('hat', 'shirt', 'pants', 'shoes', 'back', 'neck', 'hands', 'weapon')

but no equipment, of course. Naked adventuring, FTW!

Managing items

Now that I was buffing up items, I needed a better way to manage them. Looks like my game will need an admin tool. Enter admin.py:

import sys, pickle
import sys, pickle

sys.path.append("roguey/classes")

from items import Treasure
from constants import *

class Admin(object):
  def __init__(self):
    f = open("roguey/resources/items.pk")
    try:
      self.treasures = pickle.load(f)
    except:
      print "No treasures!"
      self.treasures = []
    f.close()
    self.main()

  def new_treasure(self):
    for treasure in TREASURE_TYPES:
      print "%s. %s" % \
              (TREASURE_TYPES.index(treasure)+1, treasure)
    choice = raw_input("Pick a type [1-9]: ")
    type = TREASURE_TYPES[int(choice)-1]
    title = raw_input("Give it a title: ")
    desc = raw_input("Give it a description: ")
    attack = 0
    armor = 0
    if type == 'weapon':
      attack = raw_input("How much damage will it add? [1-999]: ")
      attack = int(attack)
    else:
      armor = raw_input("How much armor will it add? [1-999]: ")
      armor = int(armor)
    tr = Treasure(title=title, \
               description=desc, \
               type=type, \
               armor=armor, \
               attack=attack)
    self.treasures.append(tr)

  def list_treasures(self):
    for treasure in self.treasures:
      print treasure.title

  def save(self):
    f = open("roguey/resources/items.pk", "w")
    pickle.dump(self.treasures, f)
    f.close()

  def main(self):
    while 1:
      print "1. Make a new treasure"
      print "2. List current treasures"
      print "0. Quit"
      c = raw_input("Make a choice [1-2, 0]: ")
      if c[0] == "1": self.new_treasure()
      if c[0] == "2": self.list_treasures()
      if c[0] == "0": 
        self.save()
        return

if __name__ == "__main__":
  a = Admin()

It's simple, but it made managing my items so much easier. Now, rather than maintaining a text file (or *gasp* a spreadsheet) of items, I could deal with them through this script. My items could be saved as a Pickle and checked into the repo.

Wait, a Pickle?

Pickles!

Rogue guy has found some pickles in a treasure chest. He doesn't look happy.

Now that we have all this data, we need to save it somehow. In this instance, I've chosen to use Pickles.

Pickles get a bad rap, mostly due to the fact that they're not very secure. It's almost a knee-jerk reaction at this point. Go ahead, test it: walk into any Python discussion, and suggest that they use Pickles. Watch them foam at the mouth.

Pickles are inherantly insecure. You should never accept a Pickle from a source you can't trust (this is still true if talking about lower-case pickles). In this case, we know who made the Pickle, so we're good. If you decide to fork my code and turn my roguelike into something where people would be swapping these piles of items... well, you'll have a bit more work cut out for you.

Giving the player items

Now that the items are worth having, and we have a selection of them, let's have the player pick them up.

For now, I only have one of each type of item, so when the player comes to an item and doesn't have anything in that slot, it's automatically equipped. If something is already there, into the backpack it goes, because as we know, all players are cleptomaniacs.

def add_to_inventory(self, item, player):
  ''' Adds an item to the inventory
  '''
  if item.type == "trash":
    return
  if player.equipped[item.type]:
    try:
      self.inventory[item] += 1
    except:
      self.inventory[item] = 1
  else:
    player.equip_item(item)

Now that the player can equip items, we need to update our status screen area to reflect that:

def draw_equipment(self, equipment=START_EQUIPMENT):
  ''' Renders the equipment. Expect it to be exchanged for something
    awesomer
  ''' 
  self.screen.blit(self.equipment_screen, (1008, 200))
  for i in range(equipment.keys().__len__()):
    line = self.small_font.render(LONG_STRING, True, BLACK, BLACK)
    self.screen.blit(line, (1008, ((i+1)*15)+200))
  pygame.display.flip()
  i = 1
  for slot in EQUIPMENT_TYPES:
    try:
      line_text = slot + ":  " + equipment[slot].title
    except:
      line_text = slot + ":  "
    line = self.small_font.render(line_text, True, WHITE, BLACK)
    self.screen.blit(line, (1008, i*15+200))
    i += 1
  pygame.display.flip()

Side note: I got a ten-minute lecture from my son about how 'awesomer' isn't a word. What are they teaching kids these days?!

Next time!

Combat needs to be updated, now that we have all these great items!

PyGame: Returning to Combat

Where am I? Here!

Revisiting admin.py

So, remember how I joked that Pythonistas froth at the mouth over Pickles? Yeah, my admin module (which was just a bit of trash code to be used while making the game) was easily the most contraversial bit of code I churned out. This is why working on open source is a good thing, though! It inspired someone to completely rewrite it! Thanks, Ian!

The new and improved admin.py:

import sys
import xml.etree.ElementTree as etree
from xml.dom.minidom import parseString

sys.path.append("roguey/classes")

from items import Treasure
from constants import *

def prettify(element):
  # Helper function to make XML look more prettier
    txt = etree.tostring(element)
    return parseString(txt).toprettyxml()

class Admin(object):
  def __init__(self):
    # Load the existing treasures
    f = open("roguey/resources/items.xml")
    self.treasures = etree.fromstring(f.read())
    f.close()
    # trim the annoying whitespace...
    self.treasures.text = ""
    for element in self.treasures.iter():
      element.text = element.text.strip()
      element.tail = ""
    # Load the list of treasure type templates
    f = open("roguey/resources/item_templates.xml")
    self.treasure_templates = etree.fromstring(f.read())
    f.close()
    # Enter main loop
    self.running = True
    self.main()   

  def new_treasure(self):
    item_attributes = {}  # This will hold optional stats only.

    template_options = [
      template.find("item_type").text for template in self.treasure_templates
    ]

    # Gather the mandatory attributes
    selection = self.prompt_for_selection(
      prompt="Choose an item type",
      options=template_options
    )
    item_type = template_options[selection]
    template = self.treasure_templates[selection]

    title = raw_input("Give it a title: ")
    description = raw_input("Give it a description: ")

    # Check if this template requires any additional attributes
    for attr in template:
      if attr.tag == "item_type":
        continue
      prompt = attr.attrib["prompt"]
      value_type = attr.attrib.get("type", "string")  # type defaults to "string" if not specified
      item_attributes[attr.tag] = (raw_input("%s (%s): " % (prompt, value_type)), value_type)

    # finally we can add this new item to the list
    new_item = etree.SubElement(self.treasures, "item")
    etree.SubElement(new_item, "item_type").text = item_type
    etree.SubElement(new_item, "title").text = title
    etree.SubElement(new_item, "description").text = description
    for attrib, value in item_attributes.iteritems():
      optional_stat = etree.SubElement(new_item, attrib)
      optional_stat.text, optional_stat.attrib["type"] = value

  def list_treasures(self):
    for treasure in self.treasures:
      print treasure.find('title').text.strip()

  def save_and_quit(self):
    f = open("roguey/resources/items.xml", "w")
    f.write(prettify(self.treasures))
    f.close()
    self.running = False

  def delete_treasure(self):
    pass

  def main(self):
    menu_options_with_actions = [
      ("Make a new treasure", self.new_treasure),
      ("List current treasures", self.list_treasures),
      ("Delete a treasure", self.delete_treasure),
      ("Quit", self.save_and_quit),
    ]
    menu_options = [x[0] for x in menu_options_with_actions]
    menu_prompt = "Make a choice"

    while self.running:
      selection = self.prompt_for_selection(menu_prompt, menu_options)
      # Call the appropriate action based on the user selection
      menu_options_with_actions[selection][1]()

  def prompt_for_selection(self, prompt, options):
    """Given a list of options and a prompt,
    get the users selection and return the index of the selected option
    """
    # Print out the numbered options
    for i, option in enumerate(options):
      print "%3s. %s" % (i+1, option)
    # Get the users selection
    selection = raw_input("%s: " % prompt)
    return int(selection)-1

if __name__ == "__main__":
  a = Admin()

It's been updated to use an XML file, which is semi-human-readable, and the code for generating the menu has been made cleaner. Woot!

Planning ahead

Katie sits before a desk with crumpled paper on it. She holds up another piece of paper, a joyous look on her face. Caption: A plan! Now, surely things won't go to shit!

At one point, I had this lovely roadmap of what I was going to do, when, for my roguelike. That list is about as relevant today as a midieval monk's advice on what smartphone I should get. I quickly found that a round-robin approach was working much better for me. I was able to crank out enough for a blog post each week, and I wasn't getting caught up on smaller details that relied on other modules being done.

A list isn't a bad thing to have, though. You just have to respect that the universe hates a tidy plan.

As the game has grown more complex, however, I've found I've had to stop and think about what bit I need to develop first more often. Modules are starting to affect each other more, and the changes I'm making are more subtle. For example, when do I want to add animation back in? After I've added all my monsters? Before I put darkness back? Hm, when do I want to add darkness back? How about levels?

It's tempting fate, but I put together a rough roadmap for the rest of game:

You may now take bets on how well I'll adhere to this.

Combat

Combat wasn't broken in the sense that it didn't work: it was broken in the sense that it worked in spite of the way it was written. In order to get it ready for new monsters, classes, stats, and levelling up, it needed some serious work. I needed to use proper settings and getters, because now my items had meaning. First, I had to update Player.

First, I decided that having stats as their own property was getting an little unweildy. It was much better to put them into a dictionary, for later spitting out onto the screen.

def __init__(self):
  self.level = 1
  self.stats = {
    'strength': 1,
    'attack': 1,
    'defense': 5
  }
  self.current_hp = 10
  self.name = "Dudeguy McAwesomesauce"
  self.equipped = {}

  for treasure in EQUIPMENT_TYPES:
    self.equipped[treasure] = None

Now, throughout my code, I'd referred to the 'strength' and 'defense' attributes. Rather than hunt them all down, I decided to name my new properties the same thing.

@property
  def defense(self):
      return self.stats['defense'] + self.get_armor()

  @property
  def strength(self):
      return self.stats['strength']

  def get_armor(self):
      armor = 0
      for slot in self.equipped.keys():
        if self.equipped[slot]:
          try:
            armor += self.equipped[slot].armor
          except AttributeError:
            # The Treasure in this slot doesn't have an 'armor' attribute
            pass
          except TypeError:
            # The Treasure in this slot has an armor stat, however it
            # appears to be a non numeric type. Make sure this Treasures armor stat
            # has the attribute type="int" in its XML definition.
            # This sort of error should probably get properly logged somewhere
            print (
              'Please make sure the armor stat for the "%s" weapon '
              'has the type="int" attribute in its XML definition' % self.equipped[slot].title
            )
      return armor

The properties can now be called like any other attribute:

>>> from player import Player
>>> p = Player()
>>> p.strength
1
>>> p.defense
5

Now that the Player is working well, I need to update my Monster class to work the same:

class Monster(object):
  def __init__(self):
    self.level = 0
    self.stats = {
      'attack': 0,
      'defense': 0,
      'strength': 0,
    }
    self.max_hp = 1
    self.current_hq = self.max_hp

  @property
  def attack(self):
    return self.stats['attack']

  def receive_damage(self, damage):
    self.current_hp -= damage

  @property
  def defense(self):
    return self.stats['defense']

  @property
  def strength(self):
    return self.stats['strength']

class Derpy(Monster):
  def __init__(self):
    self.title = "Derpy Slime"
    self.level = 1
    self.stats ={
      'attack': 5,
      'defense': 1,
      'strength': 1,
    }
    self.current_hp = 3
    self.max_hp = 3

Hm. I'm starting to see overlap here (well, to be honest, I saw overlap a while back). I'll make a note to keep an eye on how alike these two things stay, and if it would be worth it to make a new base class when the time comes to refactor.

Now, let's update combat!

*looks at combat module*

And nothing there needs to be changed.

class Combat(object):

  def __init__(self, player, monster):
    self.player = player
    self.monster = monster
    self.fight()

  def fight(self):
    '''For now, we always start with the player.
    '''
    # Player, try to hit the monster!
    hit_attempt = randint(0, self.player.attack)
    if hit_attempt > self.monster.defense:
      damage = self.player.strength
      self.monster.receive_damage(damage)
    else:
      pass

    # Monster, try to hit back.
    if self.monster.current_hp > 0:
      hit_attempt = randint(0, self.monster.attack)
      if hit_attempt > self.player.defense:
        damage = self.monster.strength
        self.player.receive_damage(damage)
      else:
        pass

The only thing I re-jiggered was having combat call fight itself, since there's really no other reason for me to call Combat, and removed some unnecessary checks. Everything is set up, now, for our next addition...

Next time!

We add smart mobs!

Accessibility: Video Games

Anyone who follows the rest of my passions knows that I spend quite a bit of time advocating for accessibility on websites. I've given talks, done posters, and I'm even working on a book about it for O'Reilly. Recently, however, I've been thinking more and more about accessibility in video games.

Within the past year, two games made a special effort make their games more accessible. Dungeons of Dredmor added a mode especially for colorblind people. Legend of Grimrock added an on-screen movement pad for a disabled gamer who used a mouth stick to use his computer. I admit it: I still get a bit choked up when I read those stories. I just wish there were more of them.

Without a doubt, video games have made some strides in accessibility in the past few years. Audio is now almost always captioned, and inversely, dialog is almost nearly spoken. Controls are customizable. Controllers specifically for the disabled are being marketed more. But as long as we're making games, it's something that developers and designers will have to keep thinking about. It's not a problem that can be 'solved'. That would be like saying we solved the 'usability' problem, or the 'fun' problem. It's something that's going to keep growing and changing as technology and the way we interact with it keeps changing.

Why bother?

This is the first question I get from people. Why add to your development time and overhead, when you could be putting that money towards another level, or slightly better art? Many people see the time and effort put towards accessibility as wasted.

First, the number of people that need accessibility is not as small as most people think. Seven percent of all males are colorblind, to some extent. Twenty-one million people in the US suffer from arthritic issues that can affect how well they can hold a controller, or how quickly they can use a keyboard. This year, a million people will break their arm in the US, while 300,000 more will be treated for repetitive stress injuries. Approximately 10% of the world's population is left-handed.

Second, a side benefit of designing for accessibility is that the usability for everyone often goes up. The things that made a game impossible for one person are often still annoying for someone with no issues. No one likes a bad font choice, or all caps, and most people want a choice when it comes to subtitles, or listening to the voice acting.

Guiding principle

There's one rule for accessibility:

For every player, all the data, and all the functionality

It's up to you how many groups you want to cover, but for everyone you've decided to include, they should be able to not only do everything that everyone else can do in your game, but they should be able to access all the information you're exposing. If a player has to mouse over something figure out what its stats are, but they can't use their mouse, your game isn't really accessible. If you force users to use their arrow keys to move, but a player needs to use WASD, once again, your game isn't accessible. All the stuffs for everyone.

It's not just blind people

I've already alluded to this, but accessibility isn't just about blind people. There's generally four groups:

Unlike a website, you aren't going to be able to make your game accessible for all the people in all of those groups. By knowing what annoys them, however, you can start considering what you can add to your game to make it more accessible.

Visually disabled

Being visually disabled is more than being blind. If someone is color blind, or needs glasses, they fit into this category as well. Yes, most games cannot be tweaked for someone who's completely blind (though they may surprise you), but that's no reason not to consider the other sub-categories.

Color-blindness

This is the group that should never be ignored. One, it's a huge group, no matter what your age range (7% of males). Two, it's one of the easiest groups to test and make adjustments for.

Your goal, when adjusting for color blindness, should be making sure that your interface doesn't get muddled or ambiguous if someone can't differentiate certain colors. If you're developing on a Mac, Color Oracle makes this dead simple. Start up Color Oracle, start up your game, then select the type of color blindness you want to emulate.

Things you should look for:

If you find you have an issue, you don't need to choose a drastically different color: usually, a minor tweak is enough. Normally, I nudge the colors around the color wheel until I find two that seem different. Adjusting the darkness or lightness of the colors usually has the most impact, and has the least impact on the feel of the game.

And if it's really impossible? Steal Dredmor's solution, and have a theme just for the color blind.

Corrected vision

As someone with a rather strong astigmatism can tell you, suffering through bad interface choices can be painful. Sure, I can see pretty well (my vision is close to perfect, otherwise), but after a while, I'll start to get headaches and teary eyes, even with my glasses on.

This happened to me a few years back with a game called Eternal Sonata. I downloaded the demo... and discovered that the font / color / text size combo made it impossible to play. Sure, there was voice acting for most of the dialog... but not for the menus! The crazy fancy font plus the brilliant blue background and teeny size were just too much. I didn't buy the game.

Unlike the color-blind group, this group doesn't have any nifty tools to use for testing. Instead, it's better to follow some guidelines:

And there's more!

In the next article, I'll discuss the hearing disabled, which ranges from the completely deaf to those who have to wear hearing aids, and people who forgot their headphones.

Accessibility in Video Games - Hearing

This group is more than just the Deaf. It includes people who use hearing aids as well, since most hearing aids do not work well with headphones and commercial electronics. Sometimes, there's feedback. Sometimes, they pick up too much. Some people that wear hearing aids are still deaf to certain pitches, no matter how much they crank them up.

As a side benefit, it includes anyone who forgets their headphones at home at least once a week, like yours truly.

Dialog

Your cut scenes and dialog should always, always have subtitles. Many developers like to simply put the text at the bottom of the screen, with no background. This can be an issue if your text is light, but you have a scene where it happens to be snowing. You can either test every single scene to make sure your contrast remains high, or you can use banding.

Screenshot of a snowy scene. The white text at the bottom is almost impossible to read.

If you use banding, you put a band of color at the bottom of the screen that's used exclusively for captioning. Ideally, it would be placed below your screen, not covering part of the screen, so that the user isn't missing out on any of the action.

A snowy scene, with the captions in a box beneath the game. The text reads: See? This is better. Everyone can read this!

A few notes about styling: If you can, let people customize this. Me, I'm a white-background-dark-text person, but I know others who hate that combination. Also, ALL CAPS ARE BAD. DO NOT USE THEM.

Another side-benefit of using subtitles: If your voice acting is cringe-worthy, I can mute it. I've had to do this with so many triple-A titles, it's not funny.

Good captions

Good captioning is not transcribing. Usually, in games, since there's a script, this isn't an issue. When people speak off the cuff, they pepper their language with mis-fires, um's, er's, and restarts. An actor probably isn't going to do this unless someone's trying to be super edgy.

However, there is one place where I've seen captioning go off the rails: wordiness. People can only read so fast, so try to keep the words on screen down around ten to twelve. After all, they need time to read and watch the action. This might mean cutting words out of a particularly verbose scene.

Also, make it clear who's speaking. Adding their name to the text can get a bit bulky, so a common solution is to use a slightly different font color for the different speakers. Just remember to keep an eye on color contrast. Does that dark green look different enough from that dark red? Just because someone has issues with hearing doesn't mean that can't be color blind, too!

Cues

Also, dialog isn't the only important thing going on in your game. Off-screen cues are incredibly important. True story: the first time I played Plants vs. Zombies, I played it on my Touch with the sound off. I thought it was a pretty easy game until the stage where they introduced balloon zombies. I felt like it jumped several levels in difficulty. What the hell?! It wasn't until I put my headphones on that I realized how important the off-screen sounds were. Hearing the balloon fill up with air was a cue for me to start laying down cacti. Without it, I was suddenly stunned when I was being bombed with zombies behind my lines of defense.

If you have an off-screen cue, make sure you caption it, or at least add a visual bell that can be turned off and on. With PvZ, I would have added an icon that would flash with the a zombie icon whenever a zombie made a noise off-screen. If the balloon icon flashed, then I would know to start adjusting my strategy.

Screenshot of Plants vs Zombies, with where I would put a sound icon circled.

Voice chat

Live transcription is a long way off, so if your game features voice chat, a deaf user is probably going to miss out on that. We can live with that for now (though do keep an eye on that technology).

If you offer voice chat, though, make sure to offer text chat as well. Yes, they'll still be missing out on some of the game content, but at least there'll be a way for them to talk to others, and have them talk to others.

Options

Remember how I said that hearing aids pick up everything? That's one of the biggest complaints I've heard from those that depend on them, and it's one of the main reason why they get turned off. My grandmother only wore hers if she absolutely had to, and my father-in-law switches his off whenever he's out of the house. All sounds are blasted at equal volume, whether it's the person sitting across from you, or someone dropping a glass from the other side of the restaurant.

Someone wearing a hearing aid may want to turn off your ambient sounds. Most games include a slider for sound effects, but that includes important sounds, like the sound of a creeper walking up behind you. Consider adding a slider for environmental sounds, like water, wind, or merchants chattering, and then another slider for important ambient clues, like monster growls, clues for quests, or signs that you're running out of life.

Next Time

The physically disabled!

Accessibility in Video Games - Physical

There's nothing like losing use of your dominant hand for a year to give you empathy for the physically disabled.

The Iguana Saga

It was my sophomore year in college, and I was bitten by my five-and-a-half foot iguana. Four tendons were completely severed. Twelve hours in the ER, three in the OR, and a year of physical therapy later, I had most of my use back, though I would never regain full feeling, and my right hand tires quickly.

This was a few months after I got my PlayStation. AUGH. TIMING.

Once I was off the meds that made me think that British people were speaking Spanish, I broke out my PlayStation again. I couldn't go to school since I couldn't take notes, and the meds I was on tired me out if I did anything more strenuous than go out to eat. Might as well game.

I discovered that there were some games that were impossible to play, but some were doable. I beat Final Fantasy 8 thanks to being able access all the functionality of the game with one hand. I could also set the fight system to one that waited for me to enter what I wanted to do rather than beat on me constantly.

The people at my work did some research, and one of them found a one-handed keyboard for me to use. Another installed Dragoon Natural Speaking. I played around with both, and ended up with a grab-bag of successes and failures. I could poke around on my computer, but anything more complex than surfing was painful. Games? Hah. No. I couldn't hit the keys fast enough, even with my nifty one-handed keyboard. Mousing with my off-hand was shaky, and it didn't get much steadier as time went on.

I have all of my functionality back, but I still have time limits when it comes to how long I can do things on the computer or on a console. If I game more than an hour or two at a time, I pay for it the next day. My hand wears out quickly, so if I have to hold down a button for a long time, I'm going to eventually get pains shooting up my arm.

So what?

Not every physically disabled gamer is in a wheel chair (though some are). We may not even appear to have a disability. Most people don't know about my hand until I stick it in their face and start rattling off my story ("I know what my hand looks like without any skin on it!"). It may be incredibly subtle, and it may take time to kick in.

Who does it include:

The tools

The physically disabled have a huge variety of tools, some of them made especially for gaming.

They also might use a standard keyboard and mouse, albeit slower than normal, or less steadily.

What can a game developer do?

Since games are so heavily reliant on input (and often, quick input), this can feel like an impossible task. It isn't, though! Ask yourself a few questions...

Does input need to be realtime?

With Final Fantasy 7 and 8, I was able to configure battles so that the game waited for my input before continuing. Without this, the game would have been impossible for me to play. Did it take longer? Oh, sure. A replay a few years later proved I could beat the game pretty quickly if I could use both hands. I was still able to beat it, however. Tetris wouldn't work if it wasn't real time, but many RPGs have room to add this in. Even Fallout 3 had this, in a way, with their TADS system. Hit a key, and you can choose where you want to plug enemies.

Can you have on-screen controls?

Take a look at your game screen. Could you add an optional pad for movement or input onto the screen? Legend of Grimrock has one of these, added back in at the request of a disabled developer who uses a touch-screen to play games. If you do decide to add this, just remember to add all functionality a user would need: Pausing, movement, spell casting, and combat should all be represented.

If this seems impossible to add, go look at a few RPGs on Android or iOS. If they're an import from another system, they will almost always have a set of translucent controls.

A shot of Minecraft on Android, with the movement controls shown.

One advantage to designing some on-screen controls: You're already primed for deploying to a tablet market.

Can you lose the mouse? Or the keyboard?

Many PC games are a hybrid: they insist on using both the mouse and keyboard. For some games, this would probably be impossible to work around. I can't image playing a FPS without the ability to look around with my mouse. For others, though, there's a bit more leeway. I have a feeling that Blackwell Legacy could have worked with just the keyboard

Can the user customize controls?

Many specialized controllers depend on the ability for the user to customize their controls. Maybe they want to remap everything to work on their mouse. Maybe they're left handed and really don't want to use the arrow keys.

What do StickyKeys do?

If you've ever worked with Windows, I'll bet you know about StickyKeys. In many versions, if you hold down the shift key too long, you'll turn them on. They'll then proceed to drive you up the damn wall until you get them turned off.

With StickyKeys enabled, if you hold a key for a bit, you can release it, but Windows will pretend that you're still holding it down until you hit another key. If you're someone with joint issues, this can be a god-send. Holding down one key, whether on the keyboard or on the mouse, can cause quite a bit of pain if you have any hand issues.

I discovered the glory of StickyKeys and games when I played The Witcher. In The Witcher, you generally walk by left-clicking and holding somewhere away from your character. You walk all over the damn place in that game, so I was getting horrible pains up my arm.

I turned on StickyKeys so that I'd only have to hold down the mouse button for a few seconds. After that, I guided my character by moving my mouse around. He followed my cursor until I clicked again, at which point he stopped.

Most games work with StickyKeys without any interference, but it's worth revving up your game and seeing how the two interact.

Next up...

The cognitively disabled!

Accessibility in Video Games - Cognitive

When people talk about accessibility, they often forget the cognitively disabled. This is a group that's become increasingly vocal in the web accessibility arena, especially as the Internet has become a fancier, busier place.

Most people think of the cognitively disabled as the ones that are labeled 'Severe and Profound': Someone with no (or little) language, with the mind of a toddler.

The truth is, you probably know someone who's cognitively disabled. Having a cognitive impairment isn't the same as not being smart. It means that the act of taking in information or actively thinking is somehow different for an individual. Some take in too much information, and need either training or medication to help them filter the world. Others might have issues taking in information visually, so either have to take their time or

Most of the people who are issues with cognition really have issues with information processing. They might need more time with information, or they might have issues parsing a specific kind of information. They may also get information overload quickly.

So, what can you do, as a game developer?

Subtitles and voice-over

It's hard to tell what someone with a cognitive disability will find easier: Text they can read through, or subtitles they can listen to. My uncle, who's dyslexic, had to have his school books read to him. My son, who has Aspergers, has issues following verbal instructions, but does extremely well with written ones.

Since you don't know, if you have dialog, try to offer both a captions and voice. This is a nice benefit for people who can't stand voice acting, or are in an environment where they can't have their sound on.

Fonts

A diagram showing the difference between serif and sans-serif, with the serfis circled.

In general, sans-serif fonts are easier for someone with dyslexia to read. Why? Because many people with dyslexia recognize word shapes. Serifs mess with word shapes, making them more complicated to decipher.

You know what else is hard to decipher? All caps. Don't use them.

If your designer is absolutely married to some fancy font, at least add an option for the player to switch to another font.

Contrast

Most people assume (wrongly) that pure white against pure black is the most accessible contrast. It's actually one of the worst. People with dyslexia report preferring colors that are closer together in hue and value (that's color and lightness/darkness for you non-art people).

Picking the colors can be tricky. I usually go with a very dark grey and a slightly off white. This may not work for every dyslexic gamer, though. I knew one who could only read when the text was a medium blue on a deep blue background.

For these users, allowing them to set the background and foreground for their text areas is a huge help. It also helps users who prefer a lighter background, or users who prefer light text on a dark background.

Busy busy busy

In theory, everything is better with particle effects. They add interest, they make your game seem more real, they give a polished glean to your interface. Particle effects are great for most of the population.

They're not so great for the people who have issues with taking in too much information, and not being able to filter it. If you can, add an option to kill particle effects. The people with crap CPUs will thank you as well.

Quest logs and maps

There was a time, back in the glory days of the CRPG, when you had to write down everything worth remembering. Get a quest? Better get out your pencil and paper. Have a dungeon? Graph paper time!

While there is a certain charm to this, if you're someone who has trouble processing information the first go around, it can be problematic. Important details might be left behind as notes are made. And if you have trouble with spacial reasoning, making a map is no trivial task.

So, include a map and a quest log. Hell, give someone the ability to search through the quest log (I haven't seen that, yet), so they can quickly find out what the heck they were supposed to be doing with these raptor heads.

Next time!

Some final thoughts and some resources.