Overview
Sometimes, the hardest part of making a game is distributing the game after it’s been made. If your game is designed to be played as a standalone experience, playing a game in-browser can be a large disappointment. This tutorial is designed to show you how to turn your finished game into a standalone, executable file (either a .exe
or .app
, depending on your target operating system) that doesn’t require installing Adobe AIR — just double-click and play.
This guide is written mainly from a Windows perspective, but the adt
tools are the same on OS X.
Ingredients
- A game that’s ready to distribute
- Adobe AIR (anything after AIR 3.0, but I’ll be using AIR 14.0)
- A self-signed certificate (FlashDevelop can generate this for you)
- Optional Some basic knowledge of scripting on your OS
Instructions
Setting Everything Up
Before you start powering out some command-line hacker crap, you’ll need to be sure you have your AIR binaries added to your system’s path.
To make sure the AIR tools are properly added to your path, open a command line window and attempt to call the AIR Developer Tool, adt
. You should get something like this:
$ adt Adobe (R) AIR (R) Developer Tool (ADT) Version 14.0.0.178 Copyright (c) 2008-2014 Adobe Systems Incorporated. All rights reserved. No arguments were found usage: adt -checkstore SIGNING_OPTIONS adt -certificate -cn <name> ( -ou <org-unit> )? ( -o <org-name> )? ( -c <country> )? ( -validityPeriod <years> )? ( 1024-RSA | 2048-RSA ) <pfx-file> <password> adt -help adt -migrate SIGNING_OPTIONS ( <air-file-in> | <airn-file-in> ) <output-file> adt -package SIGNING_OPTIONS ( -target air )? <output-package> ( <app-desc> FILE_OPTIONS | <input-package> ) adt -package SIGNING_OPTIONS -target airn <output-package> ( <app-desc> FILE-AND-PATH-OPTIONS | <input-package> ) adt -package -target ( apk | apk-debug | apk-emulator | apk-captive-runtime ) ( CONNECT_OPTIONS? | LISTEN_OPTIONS? ) ( -airDownloadURL <url> )? ( ARCH_OPTIONS )? SIGNING_OPTIONS <output-package> ( <app-desc> PLATFORM-SDK-OPTION? FILE-AND-PATH-OPTIONS | <input-package> PLATFORM-SDK-OPTION? ) adt -package -target ( ipa-test | ipa-debug | ipa-app-store | ipa-ad-hoc | ipa-test-interpreter | ipa-debug-interpreter | ipa-test-interpreter-simulator | ipa-debug-interpreter-simulator ) ( CONNECT_OPTIONS? | LISTEN_OPTIONS? ) ( -sampler )? ANE_LINK_OPTIONS? AOT_MODE_OPTIONS? SIGNING_OPTIONS <output-package> ( <app-desc> PLATFORM-SDK-OPTION? FILE-AND-PATH-OPTIONS | <input-package> PLATFORM-SDK-OPTION? ) adt -package SIGNING_OPTIONS? -target native SIGNING_OPTIONS? <output-package> ( <app-desc> FILE-AND-PATH-OPTIONS | <input-package> ) adt -package SIGNING_OPTIONS? -migrate SIGNING_OPTIONS -target native SIGNING_OPTIONS? <output-package> <app-desc> FILE_OPTIONS PATH-OPTION adt -package SIGNING_OPTIONS? -target bundle SIGNING_OPTIONS? <output-package> ( <app-desc> FILE-AND-PATH-OPTIONS | <input-package> ) adt -package SIGNING_OPTIONS? -target ane <output-package> <ext-desc> ANE_OPTIONS adt -prepare <airi-file> <app-desc> FILE_AND_PATH_OPTIONS adt -sign SIGNING_OPTIONS ( -target ( air | airn | ane ) )? ( <airi-file> | <unsigned-ane-file> ) <output-file> adt -devices PLATFORM-OPTION PLATFORM-SDK-OPTION? adt -installRuntime PLATFORM-OPTION PLATFORM-SDK-OPTION? DEVICE-OPTION? ( -package <apk-file> )? adt -installApp PLATFORM-OPTION PLATFORM-SDK-OPTION? DEVICE-OPTION? -package <apk-file | ipa-file> adt -uninstallRuntime PLATFORM-OPTION PLATFORM-SDK-OPTION? DEVICE-OPTION? adt -uninstallApp PLATFORM-OPTION PLATFORM-SDK-OPTION? DEVICE-OPTION? -appid <app-id> adt -launchApp { PLATFORM-OPTION PLATFORM-SDK-OPTION? DEVICE-OPTION? ( -debuggerPort port )? -appid <app-id> } adt -runtimeVersion PLATFORM-OPTION PLATFORM-SDK-OPTION? DEVICE-OPTION? adt -appVersion PLATFORM-OPTION PLATFORM-SDK-OPTION? DEVICE-OPTION? -appid <app-id> adt -version SIGNING_OPTIONS : -storetype <type> ( -keystore <store> )? ( -storepass <pass> )? ( -alias <aliasName> )? ( -keypass <pass> )? ( -providerName <name> )? ( -tsa <url> )? ( -provisioning-profile <profile> )? FILE_OPTIONS : <fileOrDir>* ( ( -C <dir> <fileOrDir>+ ) | ( -e <file> <path> ) )* ARCH_OPTIONS : -arch (armv7 | x86) CONNECT_OPTIONS : -connect <host> LISTEN_OPTIONS : -listen <port> ANE_LINK_OPTIONS : -hideAneLibSymbols ( yes | no ) ANE_OPTIONS : -swc <swc> ( -platform <name> (-platformoptions <file>)? <fileOrDir>* ( -C <dir> <fileOrDir>+ )* )* FILE-AND-PATH-OPTIONS: ( PATH-OPTION | FILE-OPTIONS ) FILE-AND-PATH-OPTIONS? PATH-OPTION : -extdir <dir> PLATFORM-OPTION : -platform (android | ios) PLATFORM-SDK-OPTION : -platformsdk <platform-sdk-home-dir> DEVICE-OPTION : -device ( deviceID | ios-simulator ) AOT_MODE_OPTIONS : -useLegacyAOT ( yes | no )
Those are the tools you’ll be working with, so get ready. It’s time to cook.
Basic Bundling
To create a bundle with the AIR Captive Runtime (remember, that’s the bit that allows it to run without requiring AIR to be installed), you’ll be using the bundle
target. That means, you’ll want to use the item from the list that adt
graciously provided you with the -target bundle
argument. In case you can’t find it, here it is:
adt -package SIGNING_OPTIONS? -target bundle SIGNING_OPTIONS? <output-package> ( <app-desc> FILE-AND-PATH-OPTIONS | <input-package> )
Got that? Let’s break it down.
-
SIGNING_OPTIONS
This is where you slap that self-signed certificate in. -
output-package
This is the name of the file to output, likedist\windows
. -
app-desc
The AIR descriptor XML (should be generated by FlashDevelop) -
FILE-AND-PATH-OPTIONS
This one can be tricky, but it’s a list of all the assets required by your application XML, includingmygame.swf
.
Bundling In Action
Now that you know the tools, let’s look at a sample game, Super Sample. You would probably have a bundle call that looks very similar to this:
$ adt -package -tsa none -storetype pkcs12 -keystore "cert\supersample.p12" -storepass sspassword123 -target bundle dist\windows application.xml -e "bin/supersample.swf" supersample.swf -C "icons/desktop/icons" . -extdir lib
Let’s dissect this call.
$ adt -package
Call the AIR Developer Tool, instructing it to package an application for distribution.
-tsa none -storetype pkcs12 -keystore "cert\supersample.p12" -storepass sspassword123
Here are the signing options.
-tsa none -storetype pkcs12 -keystore "cert\supersample.p12" -storepass sspassword123
This section instructs the tool to not check the certificate timestamp. This can be useful if your computer isn’t connected to the internet or if your certificate file is out of date (shame on you).
-tsa none -storetype pkcs12 -keystore "cert\supersample.p12" -storepass sspassword123
This section tells the tool what certificate to use for signing. The conversation might go something like this:
The certificate is a [PKCS 12] pkcs-12 certificate. The certificate is found at
cert\supersample.p12
. The password for the certificate is “sspassword123.”
-target bundle dist\windows application.xml
This tells the tool to create an executable bundle with the AIR Captive Runtime in the dist\windows
directory based on the information in application.xml
. The XML file will describe the name of the application as well as any other files needed by the application. Since the application’s name is stored in application.xml
, it will use this to name the resulting executable (in this case, it’ll probably be supersample.exe
).
-e "bin/supersample.swf" supersample.swf -C "icons/desktop/icons" . -extdir lib
Here are the file and path options. As I mentioned before, these can be tricky, but I’m sure you’ll get the hang of them. The main thing you need to know are the two flags, -e
and -C
. -e
describes an explicit file, while -C
describes a directory change. Here we go.
-e "bin/supersample.swf" supersample.swf -C "icons/desktop/icons" . -extdir lib
The bundle should include the file located at
bin/supersample.swf
, and this file should be in the root directory and namedsupersample.swf
.
If for some reason you needed to change the location or name of the file, you can do that as well. Supposing you goofed real bad and built your file to output-files/goofed.swf
and set your application.xml
to look for swfs/main.swf
, you could change this to:
-e output-files/goofed.swf swfs/main.swf
-e "bin/supersample.swf" supersample.swf -C "icons/desktop/icons" . -extdir lib
This describes an entire folder (in this case, this folder contains the icons to use for the executable file).
Switch to the directory
icons/desktop/icons
. Now, put all the icons there into the root of the bundle.
Again, you can change the output, .
, to point to the proper location in your bundle.
-e "bin/supersample.swf" supersample.swf -C "icons/desktop/icons" . -extdir lib
This specifies any external libraries you may need in your application. This is often used when working with Native Extensions, (such as FRESteamWorks fresteamworks, which provides your AIR application access to the steamworks libraries).
Look for Native Extensions in the
lib
directory and bundle those on up as well.
Advanced Bundling Maneuvers
Once you’ve gotten the hang of bundling your AIR applications, make life easier on yourself with simple tools.
Power Bundling
It can be a pain to keep a command window open, and typing all your stuff in sucks. Make life easier by writing a little batch script like this:
bundle.bat
@echo off
set SIGNING_OPTIONS=-storetype pkcs12 -keystore "cert\supersample.p12" -storepass sspassword123
set APP_XML=application.xml
set DIST_PATH=dist
set DIST_NAME=windows
set FILE_OR_DIR=-e "bin/supersample.swf" supersample.swf -C "icons/desktop/icons" .
set OUTPUT=%DIST_PATH%\%DIST_NAME%
if not exist "%DIST_PATH%" md "%DIST_PATH%"
set AIR_PACKAGE=adt -package -tsa none %SIGNING_OPTIONS% -target bundle %OUTPUT% %APP_XML% %FILE_OR_DIR% -extdir lib
call where adt
call adt -version
echo $ %AIR_PACKAGE%
call %AIR_PACKAGE%
This little bad-boy will not only bundle Super Sample by just double-clicking on it, but will also check the location and version of adt
, create your output folder (if it doesn’t exist) and show you the exact command it is calling. Additionally, editing it is easy since all the variables are broken out.
Automatic Build & Bundle
Once you’ve created a bundling script like bundle.bat
, you can instruct FlashDevelop to bundle that puppy upon successful build of your game — now that’s a next-level bundling maneuver!
In FlashDevelop, open your project’s properties and cruise over to the Build tab. Add your bundle batch file to the “Post-Build Command Line” area, making sure that “Always execute” is off (this prevents bundle.bat
from being run if the build fails).
Now, whenever you build, FlashDevelop will also bundle for you!
If you want to get really fancy and always test your bundled application, hit up that Output tab, select “Run Custom Command…” under “Test Project” and click “Edit…” From there, enter the location of your bundle and save that beast. Now, when you run your application from FlashDevelop, it’ll run your standalone executable. Wow!