Gist: Stop Playing on Recompile

Full credit goes to Takuan Daikon (of DFGUI), but we happen to share a mailing list, and this is worth spreading.

Throw this script in an Editor folder in your project, and anytime you save and switch back to Unity, it’ll automatically stop playing.

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

Jam: Xeno Overpass

What do you get, when you do a game jam, in one* hour, with thirteen people?

An hell of a time, for one.

We spent a Frunch at the Collective pulling together a game. Everyone had something to do — art, audio, PR, programming, design… and we not only ended up with a game for you to enjoy, we ended up with a documentary too.

Give it a shot with a friend, or read the press kit.

* I was the only one in Unity, so I actually spent about 2.5-3 hours on the game, integrating the assets, and getting gameplay in. Next time we need a tech artist. :)

Jam: Molydeu…l

This past July, I was one of the organizers for the Boston-area MolyJam. This year, the prompt was from a selection of quotes from Peter Molyneux himself.

I was worried I might be pulled off due to duties that organizing the event might entail, so I decided to whip something up on my own.

PROMPT

I’m not punishing you for button mashing; I’m rewarding you for not button mashing, and that is a really big distinction.

 

I took some heavy inspiration from the humor that QWOP evokes from making movement difficult. Multiplayer games like WrestleJump are very easy to pick up, and a blast to play. What if there was a Western-style duel game, where you had really floppy arms?

After a short amount of time, I had some joint constraints working:

Thanks to the magic of the Unity Asset Store, I quickly whipped together a scene:
SceneAfter a little bit of work on the joints, it was simply too easy to get the player to manipulate the shoulder/elbow joints. I decided to switch gameplay to be a bit more based on timing.

After a weekend of work, I ended up with a finished game. I don’t think I’ll pursue the idea much further — through this little experiment it became pretty obvious that much of the challenge and enjoyment of QWOP and Wrestle Jump come from balancing against gravity.

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.

Perforce in the Cloud, Free (for a year)!

As Dave and I begin working on Ugly Baby with Dejobaan, we had the opportunity to throw the Unity project in our version control system of choice. Coming off the heels of a couple projects versioned with Unity’s Asset Server, I was definitely looking forward to working with something slightly more robust.

Perforce, in a previous life, was very expensive to use ($700+/user). Their free licensing restrictions used to only allow a handful of users/workspaces, which wouldn’t be enough for a small team with a few dev computers. That being said, they have recently changed their terms to allow for up to 20 users/workspaces and unlimited files, or unlimited users, and a limit of 1000 files. Jumping into a game that’s closing in on it’s third or fourth year of iterative development, I decided the former would be the best option for us.

On top of that, it turns out to be very easy to get everything setup for yourself at absolutely no cost.

Setting up the Server

Normally, you’d have to worry about getting a server hosted somewhere, worry about the bandwidth, the disk space, the uptime. Turns out Amazon allows for small servers hosted on their huge EC2 backbone at absolutely no cost.

You have two options here, setup an instance on your own, or use BitNami to do the management of the Amazon EC2 server instance. I have played with both, and ended up sticking with BitNami because I had it setup a server with a base installation of some task management software I wanted to try. BitNami also sends you estimates of what your monthly payment will be with regards to your past week’s activity. It’s a good way to get a heads up if you’re pushing your instance a bit too hard.

This section of the setup will be a little bit sparse, as it’s been a few weeks since I setup my own server, and I did not take notes at the time. If I run through it with a friend, I’ll come back and update the post.

What’s important though, is you setup a server with either Debian or Ubuntu. Having the apt-get package manager makes our lives a bit easier. Realize that if you screw up along the way, and want to start over, simply terminate and delete your server instance off of Amazon’s backend, and start over.

Installing Perforce

  1. Download the `daemon` utility. This utility allows `p4d` to be run by the `perforce` user instead of `root`.
      sudo apt-get install daemon
  2. Connect to the directory where the `p4` and `p4d` files where downloaded (probably the home directory).
      cd ~
  3. Make the `p4` and `p4d` files executable.
      chmod +x p4 p4d
  4. Copy the Perforce executables to `/usr/local/bin` which is already in the system PATH environment variable.
      sudo mv p4 /usr/local/bin
      sudo mv p4d /usr/local/bin
  5. Create a group for Perforce files.
      sudo addgroup p4admin
  6. Create a user for Perforce administrative work.
      sudo adduser perforce
  7. Use `visudo` to give the perforce user account the ability to use `sudo`. Add the following line at the end of the file.
      perforce ALL = ALL
  8. Log off your default user account.
  9. Log in using the `perforce` account.
  10. Create a directory to hold the repository.
      sudo mkdir /perforce_depot
      sudo chown perforce:p4admin /perforce_depot
  11. Create a directory to hold Perforce log files.
      sudo mkdir /var/log/perforce
      sudo chown perforce:p4admin /var/log/perforce
  12. Add the following lines to the end of /etc/profile. These settings will be used by client programs run on the Linux server – not by the Perforce server.
      # Perforce Settings
      export P4JOURNAL=/var/log/perforce/journal
      export P4LOG=/var/log/perforce/p4err
      export P4PORT=localhost:1666
      export P4ROOT=/perforce_depot
      export P4USER=perforce
  13. Load the Perforce settings.
      source /etc/profile
  14. Use `visudo` to remove the perforce user the ability to use sudo. (added in by me, that’s a security risk to leave in otherwise!)

Run Perforce on Boot

  1. Connect to the initialization control directory.
      cd /etc/init.d
  2. Create the Perforce control script using `sudo vi perforce`.
      #!/bin/sh -e
    
      export P4JOURNAL=/var/log/perforce/journal
      export P4LOG=/var/log/perforce/p4err
      export P4ROOT=/perforce_depot
      export P4PORT=1666
    
      PATH="/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin"
      . /lib/lsb/init-functions
    
      p4start="p4d -d"
      p4stop="p4 admin stop"
      p4user=perforce
    
      case "$1" in
      start)
        log_action_begin_msg "Starting Perforce Server"
        daemon -u $p4user $p4start;
        ;;
    
      stop)
        log_action_begin_msg "Stopping Perforce Server"
        daemon -u $p4user $p4stop;
        ;;
    
      restart)
        /etc/init.d/perforce stop
        /etc/init.d/perforce start
        start
        ;;
    
      *)
        echo "Usage: /etc/init.d/perforce (start|stop|restart)"
        exit 1
        ;;
    
    esac
    
    exit 0
  3. Integrate the Perforce control script into the boot process.
      sudo update-rc.d perforce defaults
  4. Goto your home directory then use the control script to start the Perforce server.
      cd ~
      sudo /etc/init.d/perforce start
  5. Use the control script to restart the Perforce server.
      sudo /etc/init.d/perforce restart

Run Perforce for the first time

Before Perforce can be used by a team there are two housekeeping task that need to be done – creating the journal and closing a security hole.

  1. Set the Perforce environment variables.
      source /etc/profile
  2. Start the perforce server. The command line is short because all of the settings are provided by the variables exported by /etc/profile.
      sudo p4d –d
  3. Create the journal.
      sudo p4d –jc
  4. By default, all users connected to Perforce are administrators. However, that level of security is not acceptable for a normal development environment. Run the `protect` option to switch out of the default security mode. For now, accept the default protections by typing `:wq` and pressing Enter.
      sudo p4 protect
  5. Stop the Perforce server.
      sudo p4 admin stop

Enabling SSL

I would suggest that you protect your Perforce connection by logging into it over SSL, rather than the plaintext connection that acts as a default.

  1. Create the directories for Perforce’s SSL certs and whatnot.
    sudo mkdir /perforce_ssl
    sudo chown perforce:p4admin /perforce_ssl
  2. Edit /etc/profile and /etc/init.d/perforce and add or edit the following lines:
    export P4PORT=ssl:1666
    export P4SSLDIR=/perforce_ssl
  3. Create a file called “config.txt” within the perforce_ssl folder, chown it to perforce:perforce, and fill it with the following contents, changing the defaults where you see fit:
    # C: Country Name - 2 letter code (default: US)
    C =
    # ST: State or Province Name - full name (default: CA)
    ST =
    # L: Locality or City Name (default: Alameda)
    L =
    # O: Organization or Company Name (default: Perforce Autogen Cert)
    O =
    # OU = Organization Unit - division or unit
    OU =
    # CN: Common Name (usually the DNS name of the server)
    # (default: the current server's DNS name)
    CN =
    # EX: number of days from today for certificate expiration 
    # (default: 730, e.g. 2 years)
    EX =
    # UNITS: unit multiplier for expiration (defaults to "days")
    # Valid values: "secs", "mins", "hours"
    UNITS =
  4.  After you’ve saved that file, type the following:
    p4d -Gc
  5. Verify that you have both a certificate.txt and a privatekey.txt in your directory now. If you don’t, check the logs at /var/log/perforce/p4err. Installing this a second time, I discovered that I needed to:
    sudo chmod 700 /perforce_ssl/
  6. Generate a fingerprint for the key and certificate pair. Make a note of this keypair, it can be given to your users to verify the authenticity of your server for secure communications.
    p4d -Gf
  7.  Restart the p4d, and it *should* start up in SSL. Confirm this by connecting via SSL (use ssl:your_ip:your_port) in your p4 client as your host address. (I would also look into setting the security settings high enough to require a password on login.)

Setting up Perforce for Unity

It’s also important to setup the typemap for Perforce, to ensure that the files that are being checked out are properly marked as text, binary, and binary exclusive.

In order to do this, type:

p4 typemap

You’ll want to paste in the following typemap, and be sure to save.

text //....js
text //....cs
text //...shader
text //....meta
text+l //....cm
text+l //....proc
text+l //....md5mesh
text+l //....md5anim
text+l //....ma
binary //....dll
binary //....exe
binary //....response
binary //....lib
binary //....pdb
binary //....u
binary //....ini
binary //....stub
binary //....ip
binary+l //....prefab
binary+l //....mb
binary+l //....mat
binary+l //....psb
binary+l //....mp3
binary+l //....fbx
binary+l //....unity
binary+l //....asset
binary+l //....aas
binary+l //....tga
binary+l //....jpg
binary+l //....lwo
binary+l //....wav
binary+l //....ogg
binary+l //....demo
binary+l //....roq
binary+l //....doc
binary+l //....xls
binary+l //....celtx
binary+l //....pdf
binary+l //....odt
binary+l //....ods
binary+l //....ppt
binary+l //....skp
binary+lS //....dds
binary+lS //....bnk
binary+lS //....light
binary+lS //....shadow
binary+lS //....ibl
binary+lS //....bik
binary+lS //....upk

Closing Thoughts

Until I have to run through this again, this is the approximation of the work that I needed to do to get Perforce up and running on my EC2 instance. It may be missing certain important bits of information, and it may be wrong, as I do remember running into a few “gotcha!”s during installation.If you have anything specific to ask or pass along, simply reach out to me via the contact page, or try Twitter.

Troubleshooting

  1. If you have everything up and running on the server, and you cannot seem to access the server from outside the server, make sure that you did not set the P4PORT to ssl:localhost:1666. This restricts all external connections, even if your firewall has an open port to your server.
Good luck!

Credit!

The majority of the credit for this knowledge is not my own. Thanks to the following sites that contained most of this information:

Mulla Nasreddin

Found a quote today, and tried to track down it’s origins. Rather than the quote, I prefer this small anecdote I found from
The Beggar King and the Secret of Happiness by Joel Ben Izzy.

The Secret of Happiness

Nasrudin is known as much for his wisdom as his foolishness, and many are those who have sought out his teaching.

One devotee tracked him down for many years before finding him in the marketplace sitting atop a pile of banana peels–no one knows why.

“Oh great sage, Nasrudin,” said the eager student. “I must ask you a very important question, the answer to which we all seek: What is the secret to attaining happiness?”

Nasrudin thought for a time, then responded. “The secret of happiness is good judgment.”

“Ah,” said the student. “But how do we attain good judgement?”

“From experience,” answered Nasrudin.

“Yes,” said the student. “But how do we attain experience?”

“Bad judgment.”

Reading about the Mulla was quite humorous as well.

Raising dead pixels on a Canon DSLR

Learned a super awesome trick today for Canon DSLRs that get a hot/dead/stuck pixel on the sensor. It’s not foolproof and might not fix the issue, but it turns out that it has fixed the problem for many people including a friend of mine today, so that’s good enough for me!

  1. Take off your lens, putting the lens cap on to protect it.
  2. Put your body cap on to cover your camera’s sensor.
  3. Turn your camera on, and go to the second “wrench” setting page on the menu.
  4. Scroll down to “Sensor Cleaning”, Select it.
  5. Select “Clean manually”
  6. Click “OK” when it tells you the mirror will open up.
  7. Let the mirror stay open for about 30-60 seconds, and then turn off the camera.
  8. Put the lens back on the camera, turn it back on, and take a few pictures
  9. If you’re lucky, you will have risen your pixel from the dead!

Should we call it an excerpt from the NecronomiCanon? ;)