A Game Programmer's Tale
Google+TwitterFacebook

RPI Game Jam 10/27/12 Postmortem

MineEscape is our entry for the RPI game dev club’s game jam on 10/27/2012.

Source code: https://github.com/Robmaister/RPI-Game-Jam-10-27-12
Binary release: https://github.com/Robmaister/RPI-Game-Jam-10-27-12/downloads

Requires OpenAL. If the game crashes at the very beginning, this is probably the issue. Go download it here: http://connect.creativelabs.com/openal/Downloads/oalinst.zip

So now that I’ve got two of these RPI game dev club game jams under my belt, it’s time to get that second postmortem done. The theme was Surprise and Suspense. I’m just going to dive right in…

What Happened?

Like the previous game jam, we spent about an hour coming up with an idea. This time I decided to use C# and OpenTK, a combination I’m much more familiar with than Python and Pygame. I borrowed a teeny tiny amount of code from a previous game jam (open sourced) to avoid having to spend time on boilerplate code that most engines already have. Pretty much just the Texture class, the AudioBuffer class, and the Resources class, the AABB class, and the PhysicsManager class, along with a few method definitions for the IState interface. In total it was about ~650 lines of borrowed code, including blank lines and those bulky C# doc comments. The only functionality I borrowed was basic resource management, light wrapping around some OpenGL/OpenAL functionality, and AABB collision detection.

The first few hours were spent setting things up – getting the camera and player classes to move, defining the proper coordinate spaces, etc. Following that, one of my teammates finishes a rough outline of the first level which I use to set up the map collision code. (The entire map is converted to a bool[][] based on whether or not the alpha value of that pixel is below a certain threshold. Each entity’s axis-aligned bounding box is checked against the map for any pixels that are considered solid.)

Following that, I start work on the flashlight/shadowing. The way it works is that I draw the area of the flashlight to the stencil buffer (along with an alpha-tested version of the player) and then draw a fullscreen quad with a slightly transparent black texture everywhere except for the parts of the stencil map I wrote to. The results were alright, there was no penumbra (the edges of the flashlight were very harshly aliased) and no falloff towards the end of the light.

At the same time my teammates were working on detailing the level more and making sprites. I got a bit of work done in the ways of text rendering and menus, then grabbed the new version of the map and the sprites and incorporated them in. There were some minor issues with the level collision map, due to the layering effect/transparency that took a little bit of time to find a hack around. Towards the end of the night, we got a second level done and I took that in and set up some code to progress through levels. This also involved setting up entity interactivity (some hacks to detect collision between the player and the entities in the world) and implement the core mechanic – turn on the generator to turn on the lights in the level and make all the enemies disappear, then use the newly-activated lift to get up closer to the surface.

Before I went to bed that night I also added all of the menus and polished the transitions with some nice fading effects.

A scaled-down version of the first level.

The next morning I woke up at about 9:15am and walked over to Sage 2510 (where the game jam was taking place) and continued working on the menus and polishing the game a bit until my teammates arrived. When that happened I imported the third completed level that he had finished later on the previous night and put together AI for goblins and possessed pickaxes. AI was very simply a radius check and some movement code, and it worked well enough. Following that, I spent a little while trying to get OpenAL’s positional audio to work, but I couldn’t manage it. I’m still not entirely sure why it wasn’t working, but thinking about it now the scale was probably too large for the units OpenAL assumed, but I made all the sources relative to the listener and it got the job done.

With only a few hours left, we started work on the 4th and final level and the “end” level in which you simply walk towards a bright exit out of the cave. We finished that and added a bit of polish in the last remaining hour we had. The final game still had a handful of item placement bugs and dying would send you back to the first level instead of just the last level you were on, which we found out while presenting the game…

What Went Right?

  • Execution. We thought of the idea and got something done within the 24 hours with enough time to eat/sleep. We were not rushing any features in the last hour, unlike the previous game jam and we had time to do a little bit of polish (not as much as I had hoped for.
  • Communication. Again, comparing this to the previous jam, my team communicated much more effectively and we were much more efficient in this way. There were some minor communication issues with the first level’s collision map (and spawn point info), but after that it was all smooth sailing.
  • I used a language/libraries I was already familiar with but still learned something new (stencil buffer flashlight). I didn’t have to look up any language features like the previous game jam. The code was cleaner and more organized because of this.
  • I let the artists do the art (mostly). There were a few things that didn’t look too great as they were getting imported, but since I was getting psd’s, it was very easy to go in and adjust the map quickly. The most time I spent on art was cleaning up the player sprite and making it look like his headlamp was emitting the light.

What Went Wrong?

  • Like all game jam entries, there were features that we didn’t have time to bring into the game. What went wrong here was that the main balancing mechanic was not implemented. We originally wanted enemies to back off when the light was shined towards them, but never got a chance to implement it. This should have been done very early on but never happened.
  • The game was too simple, again. The feedback we got from the judges was very useful and highlighted that the game was too easy once the generator was on. Some additional puzzle elements would have made the game a lot better.

How Will I Fix My Mistakes?

  • Implement core mechanics earlier, then work on the polishing and other effects. That way we won’t run into this issue of completely forgetting a mechanic that would have added a bit of balance to the game.
  • Aim higher and focus the programming early on. A simple inventory and locked door/key puzzle system would have taken the game a lot further. We might not have had as much polish (or a 4th level), but the game would have been a lot more interesting with this.

Worst Hack?

Continuing with the organization of the previous postmortem, I’ll post the worst hack in the game’s source code. A lot of the hacking in this game was just making variables public to save some time in writing wrapper properties, and there weren’t really too many terrible hacks since I’m more familiar with C# and have a better idea of how to structure game code in it. The biggest hack I had to make, though, was one dealing with the flow of game states. I wrote a basic state manager really early on that kept track of a state stack (very similar to the article I wrote earlier on the subject), but the way I got the game to restart after winning was by spawning a main menu state under the next menu after the main menu was popped off the stack:

startMenu.OnPopped +=
	(s, e) =>
	{
		startMenu.Reset();
		StateManager.PushState(startMenu); //reset and loop
		StateManager.PushState(objectiveMenu);
	};

This meant that for a while the game could not be exited with Esc, since that would just pop states off the stack, so the fix was to write a method that would pop all the states off without giving it a chance to spawn the temporary new start menu. So I added a bool to StateManager’s StateAction class for clearing the entire stack knowing that the method immediately after update would check for an empty stack and exit the game, giving it no time to actually push the new stack on.

else if (action.clearing)
{
	while (stack.Count > 0)
	{
		stack.Pop().OnUnload(new EventArgs());
	}
}

This was also what caused the death-returning-to-main-menu bug. We had simply forgotten to set up the lose menu state to load a new GameState with the same level, and the game quitting to desktop would have been a clearer indication of this forgetfulness than returning to the main menu, which we simply overlook since most of the testing was done on the first level.

Fork me on GitHub

RPI Game Jam 9/23/12 Postmortem

This is the technical postmortem for the game I made earlier this weekend, “60 Sekonds to Save Boxopolis”. The game was made as an entry for a 24-hour game jam hosted by RPI’s game development club.

Title screen

The competition’s theme was 60 seconds to save the world. I was working in a team with 2 other people, one of whom did most of the art and the other who helped a bit with the code, but mostly learned about it from me. The idea of the game is that a 6 year old is playing in his backyard and coming up with scenarios in which the world is ending, and that he has 60 seconds to retrieve the toys he needs to save the world. Before I start the actual postmortem, I’d like to mention that the entire source code (plus history) is available on GitHub - https://github.com/Robmaister/RPI-Game-Jam-9-22-12

What Happened?

We only took a half hour or so to come up with the basic idea and started dividing up work. Within an hour we had a list of art assets we needed to make and a list of things we had to start programming. I wanted to make a game with pygame (despite my lack of experience with it) and I had everything I needed to start working with it installed and ready from the previous night, but I still spend a good half hour helping the other programmer set up eclipse, git, python, and pygame on his laptop. Once that was set up, we got coding. By 6pm I had worked my way around pygame enough to set up a window that rendered a map in .tmx format with some text overlayed. There was also a solid red square that was our player, and he would collide with certain tiles in the world. The other programmer had set up (with some assistance from me) a method to render text on top of the game, to display the 60 second countdown.

Pygame Logo

From 6pm to about 8pm we went to downtown Troy to grab some dinner at the Brown Bag, and it was raining when we had to walk all the way back up to campus :( . After that we spent a bit of time implementing multiline text to list all of the goal objects you need to pick up in a level, and also wrote some code to have the player collide with obstacles and goals, and to be able to pick up the goal objects. By that point it was 10pm and my teammates decided to go work in their own rooms and go to bed after that, so I took the rest of the night off (but went to bed much later than 10pm).

The next morning, we wake up and meet at 7:30 to (supposedly) have breakfast. Except everywhere is closed at 7:30am on a Sunday. Which is understandable, since we only walked past 2 people in the 10 minute walk back to Sage 2510 (the location of the jam). We spent the rest of the day getting the game done. I spent way too much time importing the art (downscaling it and touching things up, I’ll talk more about this later), but after that I implemented levels, redid the background tileset, set up sound, and set my teammate up with the level editor. I spent some time copying the description text and making sure it was properly aligned and not going past the edge of the screen. When the levels were done, I copied them into the game and they all worked with only a few issues I could tweak by hand with Notepad++. We had the other teammate playtest the levels, there were a handful of bugs that I fixed. By that time it was 20 minutes to the deadline, and we realized that I had never put in the animated player sprite and that we almost finished our game with a red square for a player. I finished implementing sprite animation with about a minute to spare, which I spent changing the window title.

After that, we presented our game and got some very valuable feedback from the judges.

What Went Right?

  • For starters, we finished the game. We didn’t win any awards, but with only 24 hours, making a finished game is a reward in itself.
  • I learned about pygame. I went into this game jam wanting to know about python game development a little bit more, and now I do. Although after using OpenGL for so long, surface blitting seems very restricting. If I work in Python for the next jam, I’m going to get more comfortable with PyOpenGL and use that.

What Went Wrong?

  • We ran out of time. What we had done by the end of the jam was what we should have had done by the morning. Ideally, the entire morning should have been spent playtesting and polishing. Getting to polishing around 10am to 11am is a good goal to have, realistically.
  • The levels had no progression. The 10th level was just as easy as the first. The feedback we got from the judges about this was very well articulated. Some sort of moving obstacle or a maze setup would have made the game a lot more difficult and pushed it just that much further
  • We should have been communicating more as a team. All the art was done in 64×64 and I ended up having to convert everything down to 32×32. Better communication would have helped a lot there.
  • I didn’t know pygame before this game jam. Yes, it’s good that I learned about it during the game jam, but having spent all that time learning hurt our final product immensely. If I had made the game in C# using OpenGL, we would have had a better product done earlier.
  • I tend to be a perfectionist. I took some liberties with the art I was being given as I scaled them down. There were some issues with downsampling algorithm that left some artifacts, so I had to outline everything and clean up the edges. I ended up redoing some of the sprites completely, and that took up 1.5-2hrs that could have been better spent.

How Will I Fix My Mistakes?

  • Push for an earlier first iteration of the game. Don’t worry about doing things the right way, just make sure they’re done. This is much easier to do if you know the libraries you’re using really well.
  • We could have added a progression in difficulty if we had time for it. By getting an earlier first iteration, we can identify issues like this much earlier and have more time to try and fix it.
  • Be more specific about the requirements of the art, have better tools in place to let the artist to more of the importing process.
  • Next time I won’t learn a brand new library during a game jam, I’ll use something I’m familiar with and work more on the actual game.
  • I’ll let go of making the art perfect and focus more on getting a better game done. The focus should be on perfecting the mechanics and game itself, not the art. In a way, game jams are there to teach us that we can’t be perfectionists if we want to meet deadlines, so I should also make a conscious effort to try and let go of the finer details.

Worst Hack?

Game jams happen so quickly that almost every entry has some level of hackery in the code. Anything from a harmless, bug-fixing magic number to lengthy, messy hacks that could make grown men cry. I figured with each postmortem I write, I could include the worst hack I had to write to get the game working, so here goes:

I did collision response in the player class, because it seemed to make sense in the beginning, but as we kept building up the game I realized I had to modify the global state of the game when you collide with a goal object. So I wrote a method in main.py and imported it in the player class and called the method. Because I imported main, all the game initialization code was run again and the game was reset to the title menu. The solution? Pass a lambda function to player, player stores it and invokes it whenever it collides with an obstacle or goal object.

def collide_obstacle(obstacle):
    global levelTime, goal_obstacles, pickup_sound, hurt_sound
    key = pygame.key.get_pressed()
    if key[pygame.K_e]:
        if obstacle.get_obs_type() == "Goal":
            goal_obstacles.remove(obstacle)
            allsprites.remove(obstacle)
            pickup_sound.play()
            return obstacle
    elif obstacle.get_obs_type() == "Obstacle":
        levelTime -= 0.5
        hurt_sound.play()

 

player = Player((32, 32), 1, lambda o: collide_obstacle(o))

 

class Player(pygame.sprite.Sprite):
    def __init__(self, (x, y), speed, collide_lambda, fps=10):
        self.collide_obstacle = collide_lambda
        #...

    def move(self, x, y):
        #...
        obstacle_to_remove = None
        for o in self.obstacles:
            if self.rect.colliderect(o.get_rect()):
                if x > 0: # Moving right; Hit the left side of the wall
                    self.rect.right = o.get_rect().left
                if x < 0: # Moving left; Hit the right side of the wall
                    self.rect.left = o.get_rect().right
                if y > 0: # Moving down; Hit the top side of the wall
                    self.rect.bottom = o.get_rect().top
                if y < 0: # Moving up; Hit the bottom side of the wall
                    self.rect.top = o.get_rect().bottom
                obstacle_to_remove = self.collide_obstacle(o)
        if obstacle_to_remove is not None:
            self.obstacles.remove(obstacle_to_remove)

Advanced State Management in Games

In this blog post I’m going to describe the state management system I’ve built for my most recent game, the reasoning behind my design choices, and most importantly the steps I took to build that system and the issues that pushed me to build a bigger system. I’ll start writing posts like this more often if I get a lot of positive feedback. Writing these posts is a win for everyone, since you guys get to skip the trial-and-error that I had to go through and I’ll get to look at my code in closer detail and try to improve it. Anyways, on to the post itself…


If you’ve ever programmed a game, you’ve dealt with the issue of getting a single main loop to act in completely different ways based on what’s happening. When the game first starts up you want to present a main menu to the user. When you’re loading all the game’s assets you want to have a loading screen to show progress. When the user pauses the game, you want to show a paused menu that gives the user the option to resume the game or to exit.

Depending on what kind of game you’re making, that could be the most you need – a state enum and 4 or 5 states just to manage the menus around the game. You can throw that in a switch statement and be done with it. It works great, incurs no overhead, and is very readable. I did this a while back for an escape-the-room game. Each clickable area was a state and I hardcoded all the states together and it worked pretty well for a small game. The full source code is available on my GitHub account.

enum GameStates { 
	mainMenu, 
	room1Side1, 
	room1Side2, 
	room1Side3, 
	room1Side4, 
	room1Up, 
	room1UnderBed, 
	room1OutsideSafe, 
	room1UncoveredSafe, 
	room1InsideSafe, 
	room1InsideVent, 
	room1Dresser, 
	room1ToolBox,
	room1BehindCurtains,
	//room2Side1, 
	//room2Cabinet, 
	//room2InsideCabinet, 
	//room1DoorClose, 
	winMenu
};

GameStates gs = mainMenu;

But what happens when you’re making a game that requires more complex states? What if you need more functionality out the state manager?

Let’s say you’re making an RPG or Roguelike with an inventory system. You have an “inventory” state so that you can look at all your items (a few can be hotkeyed and are visible from the main game state) and you have a “description” state that provides a long textual description of the item along with all it’s stats and a picture. You want the description state to pop up when you select an item, either from the hotkeyed section or from the inventory state. At first you might think “No problem, you just hook both of those to change the state to the description state,” but you’ll run into an issue when you try to reverse that action. When you exit the description state, what state do you change to?

An immediate solution to that problem would be to store not only the current state, but the last state as well. When you want to return to the game or inventory, you’ll just change the state to the previous one. Great! Now what happens when a player pauses the game in the description state? The paused state will overwrite the previous state with the description state. When you try to get out of the description state, the puased screen state will load instead.

The solution is a stack of states. You use Peek() to get the current state, and all state changing can be represented as a series of Push() and Pop() calls. This provides a very elegant solution to the problems I described above:

  1. Both the hotkeys and inventory state can change to a description state.
    Solution: Push a description state to the stack.
  2. The description state must be able to return to the state it was called from.
    Solution: Pop the description state off the stack.
  3. The game can be paused at any point without any side effects to game state.
    Solution: Push a pause state and pop it off afterwards.

Now that we’ve determined the best way to organize states, exactly what functionality does a state contain and how do we store it? Depending on the game you’re making and the libraries you’re using, this will vary. In my case, I’m using OpenTK, which provides a GameWindow class. The GameWindow class handles the main loop, the message pump, OpenGL context creation, and user input. I’m also making a first person shooter, which means a layer must be able to hide the cursor, as well as choose the type of mouse input they want (unbound and unaccelerated for the game, window bound and OS-accelerated for the UI). So it would make sense for me to take most, if not all, of the virtual GameWindow methods and route them to the current state.

As you can see in the source for my Ludum Dare 22 entry, Each state is a class that implements the IState interface, which I defined as the following:

public interface IState
{
	void OnLoad(EventArgs e);
	void OnUpdateFrame(FrameEventArgs e, KeyboardDevice Keyboard, MouseDevice Mouse);
	void OnRenderFrame(FrameEventArgs e);
	void OnResize(EventArgs e, Size ClientSize);
	void OnKeyDown(object sender, KeyboardKeyEventArgs e, KeyboardDevice Keyboard, MouseDevice Mouse);
	void OnKeyUp(object sender, KeyboardKeyEventArgs e, KeyboardDevice Keyboard, MouseDevice Mouse);
	void OnMouseDown(object sender, MouseEventArgs e, KeyboardDevice Keyboard, MouseDevice Mouse);
	void OnMouseUp(object sender, MouseEventArgs e, KeyboardDevice Keyboard, MouseDevice Mouse);
	void OnUnload(EventArgs e);
}

Again, this is simpler than the current system I have for my FPS, especially since I didn’t bother to write an actual manager with a stack (For those who don’t know, Ludum Dare is a competition that gives you 48 hours to develop a full game, so you tend to skip writing any code that isn’t 100% necessary), but it still shows what I consider a state and how it’s stored.

The system I’ve built for my FPS includes a few more methods, simplifies the parameters, and adds functionality that I’ve found to be very useful, like the ability to partially update states below the current state in the stack (rendering, logic updating, mouse/keyboard input). This gives you the ability to do some really fancy things like transparent pause menus that don’t stop the game in multiplayer to defining the HUD as a “state” to split that rendering code off from the rest of the game, since it’s a much different process than rendering a 3d scene. With that new functionality, I found that the word “state” doesn’t really represent what these objects can do, so I call them layers instead.

Control over which actions are passed through to the next layer are defined as 3 separate enums (comments stripped out for readability on a blog):

[Flags]
public enum LayerActionStates
{
	None = 0x0,
	Render = 0x1,
	Update = 0x2,
	All = Render | Update
}

[Flags]
public enum LayerKeyboardStates
{
	None = 0x0,
	ContinuousInput = 0x1,
	KeyEvents = 0x2,
	All = ContinuousInput | KeyEvents
}

[Flags]
public enum LayerMouseStates
{
	None = 0x0,
	ContinuousInput = 0x1,
	MouseEvents = 0x2,
	All = ContinuousInput | MouseEvents
}

And in the ILayer interface, I define the following properties:

public interface ILayer
{
	//...

	LayerActionStates ActionState { get; }
	LayerKeyboardStates KeyboardState { get; }
	LayerMouseStates MouseState { get; }

	//...
}

Looking back, these 3 enums can easily be consolidated into a single enum, which takes advantage of the fact that they’re bitfields (currently I’m only using the first 2 bits for 3 separate enums). Either way, here’s how I use it:

bool continueMouse = true, continueKeyboard = true, continueUpdate = true;
foreach (ILayer layer in layerStack)
{
	//run the current layer's keyboard update, unless the above layer blocks it
	if (Focused && continueKeyboard)
		layer.KeyboardUpdate(frameTime, OpenTK.Input.Keyboard.GetState());

	//run the current layer's mouse update, unless the above layer blocks it
	if (Focused && continueMouse)
		layer.MouseUpdate(frameTime, OpenTK.Input.Mouse.GetState());

	//run the current layer's general update
	if (continueUpdate)
		layer.LogicUpdate(frameTime);

	//if a layer requests that we don't send keyboard/mouse input down, set the proper bool to false.
	if ((layer.KeyboardState & LayerKeyboardStates.ContinuousInput) != LayerKeyboardStates.ContinuousInput || !Focused)
		continueKeyboard = false;
	if ((layer.MouseState & LayerMouseStates.ContinuousInput) != LayerMouseStates.ContinuousInput || !Focused)
		continueMouse = false;
	if ((layer.ActionState & LayerActionStates.Update) != LayerActionStates.Update)
		continueUpdate = false;

	if (!continueKeyboard && !continueMouse && !continueUpdate)
		break;
}

To get a layering effect for transparent menus, I render the layers backwards. Yes, it’s possible to render forwards and get that same layering effect with some fancy depth peeling code, but this way is a lot simpler and only has a small amount of overhead:

//flip the stack to render (rendering needs to happen bottom up, otherwise bottom layer occludes everything above it, etc.)
renderStack.Clear();
foreach (ILayer layer in layerStack)
{
	renderStack.Push(layer);

	if ((layer.ActionState & LayerActionStates.Render) != LayerActionStates.Render)
		break;
}

//Render all the layers in reverse order
foreach (ILayer layer in renderStack)
{
	layer.OnDraw(frameTime);
}

And here’s how I manage the layer stack from anywhere in the code (not thread-safe, but it’s only ever accessed from one thread):

//Originally from http://blog.robmaister.com/advanced-state-management-in-games/
public static class LayerManager
{
	private static Queue<LayerStackAction> stackActions = new Queue<LayerStackAction>();

	public static void PushLayer(ILayer layer)
	{
		stackActions.Enqueue(LayerStackAction.CreatePushAction(layer));
	}

	public static void PopLayer()
	{
		stackActions.Enqueue(LayerStackAction.CreatePopAction());
	}

	public static void PopLayer(int count)
	{
		for (; count > 0; count--)
			PopLayer();
	}

	public static void UpdateLayerStack(Stack<ILayer> layerStack, Size window)
	{
		while (stackActions.Count > 0)
		{
			stackActions.Dequeue().ApplyAction(layerStack, window);
		}
	}

	private sealed class LayerStackAction
	{
		private readonly Action action;
		private readonly ILayer layer;

		private LayerStackAction(Action a, ILayer layer)
		{
			action = a;
			this.layer = layer;
		}

		private enum Action
		{
			Push,
			Pop
		}

		public static LayerStackAction CreatePopAction()
		{
			return new LayerStackAction(Action.Pop, null);
		}

		public static LayerStackAction CreatePushAction(ILayer layer)
		{
			return new LayerStackAction(Action.Push, layer);
		}

		public void ApplyAction(Stack<ILayer> layerStack, Size window)
		{
			switch (action)
			{
				case Action.Pop:
					//pop and unload the layer
					layerStack.Pop().OnUnload();
					break;
				case Action.Push:
					//load and push the layer
					layer.OnLoad(window.Width, window.Height);
					layerStack.Push(layer);
					break;
			}
		}
	}
}

While it may look overengineered for something that’s just supposed to push and pop a stack, a lot of it is necessary to ensure proper functionality. For example, before I had the nested LayerStackAction class, there was a Queue for pushed layers and an int that kept count of how many layers to pop. When it was time to update, it would pop all the counted layers then push all the layers in the queue. The would cause issues when a piece of code calls Pop and Push and expects the actions to execute in the order they were given. For example, calling Push() then Pop() wouldn’t remove the newly pushed layer, but rather pop the existing layer and push the new layer on top of that.

With my recent work on UI, I’ve had to revisit this state management code to add functionality I need in 2D, like control over cursor hiding. While working on that I realized just how much work I’d put into state management and how robust it is, I felt like it wouldn’t be right to keep it hidden.

As for using this source code, feel free to use it anywhere. If you’re redistributing in source format, I would appreciate it if you kept the link to this post with the source code.

I’ve moved!

Now that I just moved to a new domain, I think it’s an excellent time to write a large post about everything I’ve been doing over the last year or so, and start writing regularly on this blog.

First off, I competed in Ludum Dare 22 this last December. I never got around to writing a full post-mortem, and by now all the finer details of my experience have slipped away, so there won’t really ever be a post-mortem for that game. Still, writing a full game from scratch in under 48 hours is quite the experience. It really pushes you to get all the low-level technical code done as quickly as possible so that you have time to actually design the game. The theme for Ludum Dare 22 was Alone so with a little inspiration from this xkcd comic, I made Rover’s Spirit, a small top-down “puzzle” game with only one real puzzle. The full submission can be viewed here: http://www.ludumdare.com/compo/ludum-dare-22/?action=preview&uid=7668

Looking back at it, I’m actually a bit surprised that I got the ratings that I did. While the environment was constructed pretty well, the game was very lacking in content. And from the comments it’s clear that I had a lot of game-crashing bugs and was frantically looking for solutions. The long-term lessons I’ve taken away from my participation in Ludum Dare are the following:

  • Before competing in a game jam, make sure you’re familiar with your tools and know how to handle all aspects of a game (audio was the big issue for Rover’s Spirit)
  • When making a game of any size, get the technical programming done as quickly as possible and get on with the actual design of the game. Only go back and add on to the technical stuff when you really need to. Good art assets can cover up bad code most of the time.
  • If possible, use third party libraries and game engines. In a competition like Ludum Dare, I find it fun (and challenging) to write the game from scratch and I won’t sink too much time into it since it’s only 48 hours, but if you scale the time I spent on “engine” code during LD22, using a ready-made engine can cut your development time significantly. If your game can’t be made on any existing game engines, at least use libraries to handle physics and anything else that would be standard in your game.
  • Make the asset/level pipeline as nice as possible. In the context of Ludum Dare, time is limited so you can’t get too fancy with it, but at the same time you want to avoid huge time sinks like how I create levels in Rover’s Spirit. Seriously, don’t ever spend that much time manually calculating offsets for every entity…
  • Failure is just as good as success, as long as you’re trying something new and putting a lot of effort into it. We learn more from our failures than we do from our successes. Even if you don’t win a game jam, you still learn quite a bit and become a better game developer for it. This short list of lessons is proof of that.

I wasn’t able to participate in Ludum Dare 23 due to the timing of the competition, and Ludum Dare 24 will also happen during a very hectic week for me, but I’ll try to enter something anyways.


I’d also like to mention that since last September, I’ve made a github account and have released a lot of my smaller projects under the permissive MIT license. On there is a project called SharpFont, which are FreeType2 bindings I’ve written in C# for any .NET language. It’s not quite complete yet (I need to move some methods around and get Windows 64-bit working), but it does provide a much nicer interface than the old Tao bindings. I’ll post more on that soon. Also included on my github account is the source for Rover’s Spirit:

https://github.com/Robmaister


The last thing I wanted to mention here is the game I’ve been working on with my friends for the past few months. Over the past year we’ve been hopping from one idea to another but we’ve finally decided to settle on a single idea and push forward with it. The screenshots I put up in the previous post are from that game. Some of the details of the game are still up in the air, so I won’t post much about it until we get close to an alpha build. I’ll still post the occasional screenshot and video as we’re developing. In fact, here’s the most recent video of the game showing off recently added Bézier curves:

 

It’s been a while, have some screenshots!

I’m working on a new project with a whole bunch of friends and I just tried implementing multitexturing for the terrain. When I first tested it, I had the texture weight values incorrectly calculated so that only the bottom third had positive values and it looked pretty cool so I snapped some screenshots of it. I’m not sure why everything above the textured areas are blue, but I’m guessing it has to do with transparency and the background color.




And finally, here’s the working multitexturing: