Posts Categorized: Tutorials

Under the Hood: Building a Build Pipeline

We’ve been slowly working towards a build pipeline. The code below expects that you have your project organized in a specific way.

With this build pipeline setup, you’ll get:

  • Per-store build options, with automatic preprocessor defines based on the store names.
  • Build versioning, with that version included in the project resources to use at runtime.
  • Post-build file copying, at a per-store, per-platform level.
  • Zipping of projects to names based on their version for easy distribution.
  • The ability to copy and archive your builds in Dropbox for distribution. (Old builds are automatically moved to an “Archive” folder when a new version for that platform is copied in.)

Note: Someone pointed out on Reddit that this is Pro-only. (I’ve always been a Pro-user, so I wasn’t keen to look out for those things. I’ll try to do that more in the future.)

Setting up the Pipeline

Import ZapdotBuildTools.unitypackage to get started.

For most of this to work, my scripts expect the folder hierarchy to mimic what is described here. (Mainly that BuildTools and Unity exist in the same folder.)

  • If you’re running in Windows, you need to unzip the command line version of 7zip so that it exists in PROJECTFOLDER/BuildTools/7zip/7za.exe.
  • In AppInfo.cs, change companyName, longName, shortName, appleAppId, appleBundleId, and steamAppId to match your information. shortName is used for bundle identifiers and filenames, so keep it alphanumeric, with no spaces.
  • In Build.cs, change scenes to match the scenes in your game. The first scene in the array is the one that Unity loads first, and these paths are relative to your Unity project directory.
  • If you want any post-build file copying to be done, simply create a BuildIncludes folder at your main project directory (Same folder depth as BuildTools) and mimic the Store/Platform folder hierarchy to whatever depth you want. The file copy operation will overwrite the destination with whatever you place in the BuildIncludes folder.
  • If you want to use the Push to Dropbox feature, it should automatically work on Windows, but for Mac users, you need to edit line 203 on FileTools.cs to include your Dropbox path. This assumes, of course, that you’ve installed and linked Dropbox to your account.
    • Tip: Grab the share link to the top-level folder for game builds in Dropbox, and create a shortlink for it, so you can then simply share something like bit.ly/MyGameBuilds.

See it in action!

Under the Hood: Versioning your Builds

If you’re going to be putting builds of a game in development into the hands of testers frequently, keeping track of what build they’re playing with can be crucial to tracking down bugs. For the most part, you might be able to tell a build by a major new feature or other visual change. As those builds become more frequent though, especially between strictly bugfix builds,  you’re going to have a much harder time determining what build the user has without explicit definition somewhere.

What you could do is simply throw a static string in some class, and update it every time you kick a new build out. You could also stick your hand on a hot oven. Neither of those options sound very fun to me, though.

Overview

I create a static class that I call AppInfo. In this class I have a bunch of strings and ints that don’t change very often, if ever:

  • Company Name Zapdot
  • Long Name Battles of Ug’Matumtum: Acid Reflux
  • Short Name, without punctuation ugmatumtum
    • This might be used for a filename or path.
  • Major version 0
  • Minor version 1
  • Apple App ID 0123456789
  • Apple Bundle ID com.zapdot.ugmatumtum
  • Steam App ID 9876543210

With this, I can easily create build scripts or UIs that use this information. The most important string that I’ve got in this class is AppInfo.fullVersion, which includes the major version, minor version, build date, and build count.

The build date is formatted YYMMDD (for sorting purposes) and the build count is simply an incremented integer to differentiate multiple builds on the same date.

Storing the Data

I want the data to be easily available to both the editor and the built project. I’d also prefer that the user cannot easily access the data of this file to edit it and potentially introduce confusion with bug submission. To accomplish this, I store the data in a comma-separated format within the Resources directory.

The Goods!

Throw AppInfo.cs in Scripts/ProjectSpecific/AppInfo.cs

Whenever you need to increment the stored build number, call the following function (editor only):

AppInfo.IncrementBuildNumber();

Whenever you want to print or display the full version, simply print or display this string:

AppInfo.fullVersion

Under the Hood: Project Organization

I spent a few hours this weekend fixing up a build script to drop into some prototypes that I’m working on. Much of the work originates from the lessons I’ve learned shipping multiple Unity titles to various storefronts. I started writing a post to describe it all, but it had so many various dependencies that it made a bit more sense to break it up and go a bit more in-depth when needed.

Over the next few weeks, we’ll look at how I’ve crafted this build script. If it goes well, I may continue the series with more “Best Practices” that I’ve picked up over the past few years.

Project Organization

Cooking is a pretty big hobby of mine. In the same ways that drive my infatuation with games — I’ve had some incredible experiences with some incredible meals, and I couldn’t simply leave them to hand-wavy magic. There was talent and knowledge behind the preparation of the food I was eating, so I set out to learn what it took to make a good meal or three.

One of the first cooking lessons that you should learn is a simple concept called “mise en place”. It’s French for “put in place”, and it refers to the constant organization of your kitchen. Time is incredibly valuable, especially in a professional kitchen, so efficiency must be at it’s highest point wherever possible. Keep your knives, measuring utensils, spices, and cookware in the exact same location, and preparation becomes quicker, allowing the chef to put their time and effort into the creation of their dish, not the assembly.

This applies to game development too. Spend the majority of your time on your craft. Don’t waste your time looking for the damned cinnamon.

Here’s how I currently set up my projects. Certain things get moved around and changed as I find slightly better ways to consider the organization, but after a few projects, I’ve arrived at this.

  • ProjectName
    • Builds
      • Steam
        • Linux
        • Mac
        • Windows
    • BuildIncludes
      • Steam
        • Linux
        • Mac
        • Windows
    • BuildTools
      • 7zip
    • Unity
      • Assets
        • Art
        • Audio
          • Music
          • SFX
        • Editor
        • External
        • Fonts
        • Plugins
        • Resources
        • Scenes
          • _Test
        • Scripts
          • ProjectSpecific
          • SceneGame
        • Shaders
        • StreamingAssets
      • ProjectSettings

From here, I add everything except the “Builds” folder to version control, and I’m on my way. Since the BuildTools will be included, and I can find all the included tools via relative paths, *any* computer that syncs to version control will be able to run the build on their machine.

My “Scripts” folder gets broken up a bit more, but that’s a bit dependent on the project . For instance, with Ugly Baby, level generation and PCG were very important, so there was a “Level” folder with a dozen or so scripts related to that.

The “External” folder is generally where I put all the plugins and assets I grab from the Asset Store, GitHub, and the ilk. If there is some unmanaged code that I’m compiling on my own to include as a plugin, I’ll put the source in a folder of its own at the same depth of the Unity and Build_____ folders.

“Builds” and “BuildIncludes” follow the exact same folder structure. (StoreName/BuildTarget) This is important, but I’ll get to it in a later post.

I still haven’t found a perfect solution for my “Art” folder. I’ve previously separated things into Models, Prefabs, and Textures, but that doesn’t work well when you have many assets for a 2D game. With the new 2D system from Unity in beta right now, I imagine that might change how I deal with sprites going forward.

Redmine (with Subversion) on Dreamhost

I absolutely love Redmine. I’m not sure if it’s a textbook case of Stockholm syndrome, since it took it’s fair share of cursing and bellyaching before I got it running for a company I’m currently contracting for, but since I’ve been using it, it’s got every feature that I’ve needed in small projects to keep myself organized, and my team connected.

It’s got a Wiki, Task Management (to track both Bugs and Tasks), Time Management, Repository Integration, and strong Access Control, all of which are compartmentalized by Project (of which you can have an infinite amount). That’s just scratching the surface of the features, there’s so much more available if the team were to grow larger.

Dreamhost is a pretty good host too, for many reasons, (price, featureset. The speed hit from page loads is a bit frustrating at times, but I’ve yet to find someone on the east coast that can beat them out, feature for feature, so I continue to stay a customer of theirs. (Something like 6 years and counting…)

Now, it’s really best if you start this setup with a brand new account (with SSH enabled), or you’re prepared to move a bunch of files.