Blog

You are filtering on tag 'postmortem'. Remove Filters
RSS

Periodic Deliveries Postmortem - Time-Saving Tricks

December 9th, 2019 (edited November 3rd, 2022)

I'm releasing a Unity 2018 game called Periodic Deliveries! Development of any game is fraught with little speedbump tasks that slowly soak up dev time and make it a little less fun, too. Here are a few tricks I did to knock out some of those bumps in my process that you can easily implement in your Unity projects, too.

Use the MenuItem attribute to create developer cheats

In a larger game with a larger team, you might implement a developer console you can activate with a key press and type commands into. That will scale well if you have a lot of commands, and it will work in standalone builds. But if your game is small, this is a quick and effective approach. Quick implementation is important because it will encourage you to add cheats more often, and those will save you time as development goes on.

Screenshot of a dropdown menu containing cheat commands

I put them all in a static class called CheatCommands. Don't forget to add a validation function, which will disable the option when it doesn't make sense and improve your own experience.

public static class CheatCommands
{
	[MenuItem("Cheats/Infinity Money", true)]
	public static bool ValidateInfinityMoney()
	{
		// Disables the item in Edit Mode and in the Main Menu
		return InGameManager.Instance.PlayerCompany != null;
	}

	[MenuItem("Cheats/Infinity Money")]
	public static void InfinityMoney()
	{
		InGameManager.Instance.PlayerCompany.AddMoney(PeriodicInt.PositiveInfinity);
	}
	
	...
}

Make custom property drawers for structs that show up in the Inspector

Using Serializable structs for common data patterns can help you avoid writing similar code multiple times. But you'll have to expand a foldout every time you want to edit this data in the Inspector. If you can write a custom property drawer quickly, you can save yourself a lot of clicks over the entire development of your project.

Here is a base class for a common property drawer I used several times. You can inherit from it to create a custom property drawer for any struct that represents a "quantity" of a "thing" (like a stack of items in a loot container, or an ingredient requirement in a recipe).

using UnityEditor;
using UnityEngine;

// Author: Brian MacIntosh (The Periodic Group)
// MIT License

/// <summary>
/// Base class that can be extended to quickly create a property drawer for a structure containing
/// data like "quantity" of "thing".
/// </summary>
public abstract class ItemQuantityPropertyDrawer : PropertyDrawer
{
	/// <summary>
	/// Name of the quantity/stack size property.
	/// </summary>
	public abstract string QuantityPropName { get; }

	/// <summary>
	/// Name of the item/object property.
	/// </summary>
	public abstract string ItemPropName { get; }

	public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
	{
		EditorGUI.BeginProperty(position, label, property);

		position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);

		int indent = EditorGUI.indentLevel;
		EditorGUI.indentLevel = 0;

		Rect quantityPosition = position;
		quantityPosition.width = 40f;
		Rect resourcePosition = position;
		resourcePosition.xMin += quantityPosition.width + 4f;
		EditorGUI.PropertyField(quantityPosition, property.FindPropertyRelative(QuantityPropName), GUIContent.none);
		EditorGUI.PropertyField(resourcePosition, property.FindPropertyRelative(ItemPropName), GUIContent.none);

		EditorGUI.indentLevel = indent;

		EditorGUI.EndProperty();
	}
}

Alternatively, get it from Github.

Here's an example use:

//RecipeComponent.cs

[Serializable]
public class RecipeComponent
{
	[Tooltip("The resource.")]
	public ResourceData Resource;

	[Tooltip("The quantity of the resource.")]
	public int Quantity = 1;

	public RecipeComponent(ResourceData resource, int quantity)
	{
		Resource = resource;
		Quantity = quantity;
	}

	public override string ToString()
	{
		return Quantity.ToString() + " " + Resource.name;
	}
}
//RecipeComponentPropertyDrawer.cs

using UnityEditor;

[CustomPropertyDrawer(typeof(RecipeComponent))]
public class RecipeComponentPropertyDrawer : ItemQuantityPropertyDrawer
{
	public override string ItemPropName
	{
		get { return "Resource"; }
	}

	public override string QuantityPropName
	{
		get { return "Quantity"; }
	}
}

Separate your code files into 'Game' and 'SDK' folders

Put any code files that are specific to this game in the 'Game' folder. Put any code files that might be useful on your future games in the 'SDK' folder. The idea here is to create a folder that you can literally copy and paste into your next project (or you can use a git submodule if you jump back and forth between a lot of projects).

You need to follow one rule to make this work: code in the 'SDK' folder can never reference code in the 'Game' folder. If this happens, either (1) rework your SDK code so the Game code can insert its own behavior - for example, provide an event it can subscribe to, or virtual methods it can override - or (2) put that code in the Game folder.

My SDK folder contains things like:

  • Static classes full of utility functions (MathUtility, ListUtility, etc).
  • The abstract property drawer above (in an Editor subfolder).
  • My generic input context system.
  • Generic helper components, like PositionAtMouse, MirrorColor, GameObjectPool...
  • etc.

If do this, you will write better, reusable code and save yourself time debugging this project and implementing these functions on the next.

Use a GlobalData ScriptableObject

ScriptableObjects are a great tool for organizing data. I like creating a scriptable object for each item, ability, and other such thing in my game to hold all the data associated with it. On Periodic Deliveries, I also created a monolithic 'GlobalData' object. This object holds all those global configuration options that usually end up throughout the project on various manager components and code constants. Putting them in one place meant I never had to spend time tracking down where so particular value was. The scriptable object also diffs much better in source control than a component in a scene, in case you need to look back through the history.

The GlobalData object can be held on a global singleton component somewhere, or maybe loaded with the new Addressable Asset system (I haven't played with it yet).

Screenshot of the Global Data object inspector

I hope some of these tips can make your development a little faster and more fun. If you're into space, simulation, or management games, don't forget to check out Periodic Deliveries on Steam!


Permalink

Ludum Dare #29 Complete

May 11th, 2014 (edited November 3rd, 2022)

A few weeks ago, I participated in the 29th Ludum Dare. It has been a while, but not many posts, since I last participated in the Dare. The theme for this one was "Beneath the Surface".

I worked with Justin Britch on this one. We met just after the theme was announced to brainstorm. I really liked this theme - it evokes mystery and exploration, provides an easy setting (underground or underwater) to start with, and could simultaneously be tied into gameplay elements. While we thought it would have been a lot of fun to make "Ben Eath, the Surf Ace", we ultimately decided that we really wanted to go after the mystery, the thrill of exploration, fear of the unknown, and such themes. We also knew that we wanted to attempt to introduce some sort of narrative into the world.

Thunder Fish game work in progress.
The beginnings of the conversation system.

The design was ambitiously scoped for a jam, and I'm happy I was able to turn out so many features.

Thunder Fish game finished conversation system
More conversation.

The Good: Dedicating time during the development process for polish worked well for the game. When polish gets left as a task for the end of the jam, there's often no time to actually do it. I didn't leave a feature until it was in a state it could stay in.

I also didn't run into too many momentum-killer problems. I've worked on several smaller projects using HTML5 and ThreeJS over the past few months, so I knew some of its quirks and was able to work continuously without getting stuck on strange bugs, even though the codebase for Thunder Fish pushed way past the size of my previous HTML games. Familiarity is key for jams, and it definitely pays off in the ability to continue grinding out features.

Learned: Yet again, I completely failed to allocate time for audio. Fortunately, I was already in the Jam category for this one, so I pulled some free music from NGXmusical in the last hour. Sound effects could have improved the feel even further, though.

You can play The Legend of the Thunder Fish on the web!

Also some of the other Dare games, here: Ludum Dare 29.


Permalink

Ludum Dare #27 Complete

September 2nd, 2013 (edited November 3rd, 2022)

Last weekend, I participated in the 27th Ludum Dare. For anyone who doesn't know, the Ludum Dare is a competition held a few times a year in which participants attempt to make a game in 48 hours. Games made for this Ludum Dare had to follow the theme "10 Seconds".

When I first heard the theme, I was a bit put off. I thought it was too shallow, too easy to just tack onto an arcade clone. Fortunately, many brilliant people proved me wrong. However, I opted to take the theme a little more laterally, interpretting "seconds" as a measure of distance based on global lat/lon coordinates. I thought, "what if there were only 10 square seconds of the earth left?" The earth would just be a really small section of the surface (about 300 ft on each edge) and a huge slice of the core dropping off into nothingness.

That sounded like a great setting for a survival-horror game, something I hadn't tried before. The design I ended up attempting to implement was very ambitious. Overall, I think this was a good plan, even though I certainly didn't get to everything I wanted to. I was considering and developing a lot of extra content for the game, rather than just taking the first few things I thought of and ending design because of scope. Of course, over-scoping is still a terrible thing, but by prioritizing things that needed to get done, I was able to still have a playable game after 48 hours even while not reaching all my goals.

I did neglect the gameplay aspect of the game a bit during this jam. Had I been able to implement some form of combat and developed the building mechanics more, the game would have become much more viable from a gameplay perspective. So perhaps the scope did kill me there. I ended up getting to explore ideas for creating ambiances and environments, though, and I think on that front it went fairly well.

You can play my entry here: The 91st Parallel.

The 91st Parallel Screenshot


Permalink

Super Pirate Box: 48 hours postmortem

January 20th, 2013 (edited November 3rd, 2022)

Last weekend, I participated in a 48-hour game jam with UCI's Video Game Development Club. I had a lot of fun pulling a couple of 14-hour workdays with my fellow game developers. Together, we turned out four games in the weekend, spanning platforms including XNA, Unity, and SDL.

I decided to use my weekend to learn more about using SDL with C++ to develop a 2D game. I spent some time in the preceding weeks writing some libraries for spriting and controls to simplify the process, but of course, there were still a few issues during the weekend. For one thing, I hadn't realized that SDL doesn't load PNG images natively. I was able to implement the SDL_image library, but that set me back a bit.

Anyway, the game itself it an arcade-style top-down game. You play as a pirate who must repel legions of enemy pirates who try to board your ship. However, for some reason neither you nor any of the other pirates have weapons. So what do you do? You attempt to shove them overboard, of course. It's fast-paced action, easy to pick up, but difficult to master - and one slip-up means starting over from zero!

I hope to be able to wrap the game up within a week with some basic menus and online highscores.

Super Pirate Box screenshot

Update: Get the game on the project page.


Permalink

Making Music Games (a programmer's postmortem)

March 20th, 2012

Being a rhythm game, Music Island presented a number of unique and fun challeges from a programming perspective. The great difficulty in programming rhythm-based games is in keeping gameplay synchronized with the game's music. A fully-featured triple-A engine like BASS might be able to analyze the music in real-time and use it to guide gameplay. However, for our one-programmer team, analyzing the stream in real-time was not really an option. We would have to find some way to overlay music onto the game and force the game to keep pace with it.

The concept was fairly simple. We would use MP3 music. With the length of the song, the number of measures, and the time signature, I can calculate the time in milliseconds between each beat - or any subdivision of the beat. When the song is running, the program keeps track of which beat it is on simply by counting up with a Stopwatch. When the time exceeds the amount of time per beat, we go to the next one (carrying over the leftover time).

To have the game actually use this information, I determine what kind of beat the song was currently on (quarters, eighths, thirty-seconds, etc) and the distance in thirty-second notes from the nearest quarter note. If that value is within a threshold, I accept input and generate a spell.

I found that MP3 files were not actually a sufficient solution, for our resources at least (the XNA engine). Despite our best efforts to get accurate timings, I could not make the game stay in sync with the music. Within 5 or 10 seconds, they would be on off-beats, and XNA offered me no way to control how the MP3 was being played to attempt to correct this. I concluded that XNA's MediaPlayer class, which was playing the MP3s, was probably the issue, so our solution was to split the songs up into segments of WAVs. In addition, our songs were designed and composed such that they could be split up into layers by instrument. So we ended up with a bunch of two-measure wave files of single-instrument lines. I wrote an XML format so our composer could stack and sequence these segments however he liked. This helped cut down on the otherwise-huge filesize wav files would have caused by re-using tunes multiple times in a song.

Now with these segmented WAVs, we had far more control over the speed the music was played at, and we were able to synchronize the music periodically. This was ultimately sufficient to keep the game as a whole completely in sync. It was a feature we were still tweaking, though, even after the game's first submission!


Permalink


Previous Page
5 posts — page 1 of 1
Next Page