Unity脚本生成ipa或apk的方法
2014-07-02 本文已影响9124人
DonaldW
2016更新:本文写于2014。其中值得注意的是,建议默认使用python编写脚本,对跨平台有好处。而并非下文即将提及的、还不那么好地,使用bat、shell来编写。
基础概念
如果不关心概念只关心使用方法,可略过这里,直接去到“准备环境”、“脚本构建使用方法”。
能够使用Unity进行脚本生成ipa或apk(以下统称app)的核心前提是:
- Unity运行时既包括了editor、也包括了compiler;
- Unity运行时既能以界面形式运行、也能以命令行方式运行。可参见Unity命令行官方文档;
- Unity以命令行方式运行时,能够通过形如
-executeMethod MyScript.MyMethod
参数,调用Editor脚本工程里面的某一个静态方法,该静态方法可以引用UnityEngine库、UnityEditor库里的任意函数,包括关键的BuildPipeline.BuildPlayer
函数。可参考BuildPlayer的官方文档; - 也就是说,理论上任何在Unity编辑器里能实现的操作,都能通过命令行实现。(亲可以试一下使用命令行做一个Unity游戏出来:p)
所以,使用Unity进行脚本生成app的核心步骤是:
- 准备SDK环境。如果是生成apk,则机器(Mac或PC)先准备好Android SDK;如果是生成ipa,则Mac先安装好XCode
- 调用脚本,生成中间工程文件。脚本实际是运行Unity命令行,调用用户函数比如
CommandBuild.Build
函数,输入各类参数(比如-ios
/-android
的平台参数、比如-debug
/-release
的版本参数) - 调用脚本,使用SDK将中间工程文件生成app。如果是生成apk,因为Android的开放性,Unity能够将这一步合到第2步中,所以生成apk也就不需要我们去搞第3步了;但如果是生成ipa,因为苹果限制必须使用XCode生成ipa,所以我们需要再用脚本调用XCode,将工程文件生成ipa。
准备环境
准备生成apk的环境
-
机器可以是PC或Mac。但这里只讨论PC。
-
到Android官网下载最新的Eclipse ADT
注:可能出现点击下载按钮没反应的情况,可以更换浏览器再试试。
注:ADT包括了EclipseIDE和SDK本身。事实上我们的确只需要SDK就好了,但试过下载SDK是不行的,因为缺少了里面的platforms。所以用下载工具下载ADT是个省时的选择。 -
解压下载好的压缩包,放到合适的地方。
-
打开Unity,Edit>Preferences>External Tools>Android SDK Location,然后选择你刚才解压文件夹中的sdk文件夹。
-
完毕。现在可以使用Unity构建了。
注:如果构建中途,出现失败形如Error building Player: Win32Exception: ...zipalign.exe...CommandLine='4"的错误,可以把zipalignexe从sdk\build-tools\(你的版本)文件夹拷到sdk\tool文件夹。
准备生成ipa的环境
- 机器必须是Mac
- 到App Store下载并安装最新的XCode
- 准备好开发者帐号、开发者p12文件。(过程略)
脚本使用方法
生成apk
- 修改Environment.bat
- 修改“unity”变量,指定里面的unity路径。
- 修改“debugParam”变量,指定是
-debug
或者-release
- 执行UnityToApk.bat,等待执行完毕
- apk生成在KillerProject\Bin\Android文件夹下
生成ipa
- 修改UnityToXCode.sh里的“debugParam”变量,指定是
-debug
或者-release
。 - 执行UnityToIPA.sh生成ipa文件。也可以执行SvnUnityToIPA.sh即先更新SVN再生成ipa。
附脚本
apk相关脚本
Environment.bat
:: set your own Unity path
set unity="D:\Program Files (x86)\Unity\Editor\Unity.exe"
:: -debug or -release
set debugParam=-debug
set projectPath=%~dp0
UnityToApk.bat
rmdir /q Bin\Android
mkdir Bin\Android
call Environment.bat
echo "Start Build Unity to Apk"
%unity% -batchmode -projectPath %projectPath% -executeMethod CommandBuild.PreBuild %debugParam% -quit -logFile ./PreBuild.log
%unity% -batchmode -projectPath %projectPath% -executeMethod CommandBuild.Build %debugParam% -android -quit -logFile ./BuildApk.log
echo "End Build,please see log PreBuild.log and BuildApk.log"
ipa相关脚本
UnityToXCode.sh
#!/bin/bash
echo "Remove XCodeProject"
rm -rf XCodeProject
path=`pwd`
#-debug or -release
debugParam="-debug"
echo "Start Build Unity to XCodeProject"
/Applications/Unity/Unity.app/Contents/MacOS/Unity -batchmode -projectPath $path -executeMethod CommandBuild.PreBuild $debugParam -quit -logFile ./PreBuild.log
/Applications/Unity/Unity.app/Contents/MacOS/Unity -batchmode -projectPath $path -executeMethod CommandBuild.Build $debugParam -ios -quit -logFile ./BuildXCodeProject.log
echo "End Build,please see log PreBuild.log and BuildXCodeProject.log"
XCodeToIPA.sh
#/bin/sh
usage()
{
echo "usage: $0 AppName XCodeProject"
echo "example: $0 Killer XCodeProject"
}
if [ $# -ne 2 ] ;then
usage
exit 1
fi
path=`pwd`
app_name=$1
xcodeproj_dir=$path/$2
#echo $app_name
#echo $xcodeproj_dir
#echo $path
if [ -d $xcodeproj_dir ] ;then
echo "$xcode_dir exist"
else
echo "dir $xcodeproj_dir doesn't exist"
exit 1
fi
cd $xcodeproj_dir
xcodebuild -sdk iphoneos7.1
echo "end xcodebuild"
cd ./build
mkdir -p ipa/Payload
cp -r ./${app_name}.app ./ipa/Payload
cd ipa
echo "zip $app_name"
zip -r $app_name *
app_file=$path/${app_name}_$(date +%m%d_%H%M).ipa
mv ${app_name}.zip $app_file
echo "Build Successed ipa."
echo "$app_file"
通用Editor脚本代码
CommandBuild.cs
using UnityEngine;
using UnityEditor;
public class CommandBuild
{
private static string[] ms_scenes =
{
"Assets/Scenes/KillerStarter.unity"
};
private static bool ms_isDebugBuild = false;
private static BuildTarget ms_buildTarget = BuildTarget.Android;
private static string XCODE_PROJECT_NAME = "XCodeProject";
private static string BUILD_OUTPUT_ANDROID = "Bin/Android/";
private static void UpdateBuildFlag()
{
string[] args = System.Environment.GetCommandLineArgs();
foreach(string oneArg in args)
{
if (oneArg != null && oneArg.Length > 0)
{
if (oneArg.ToLower().Contains("-debug"))
{
Debug.Log("\"-debug\" is detected, switch to debug build.");
ms_isDebugBuild = true;
return;
}
else if (oneArg.ToLower().Contains("-release"))
{
Debug.Log("\"-release\" is detected, switch to release build.");
ms_isDebugBuild = false;
return;
}
}
}
if (ms_isDebugBuild)
{
Debug.Log("neither \"-debug\" nor \"-release\" is detected, current is to debug build.");
}
else
{
Debug.Log("neither \"-debug\" nor \"-release\" is detected, current is to release build.");
}
}
private static void UpdateBuildTarget()
{
string[] args = System.Environment.GetCommandLineArgs();
foreach (string oneArg in args)
{
if (oneArg != null && oneArg.Length > 0)
{
if (oneArg.ToLower().Contains("-android"))
{
Debug.Log("\"-android\" is detected, switch build target to android.");
ms_buildTarget = BuildTarget.Android;
return;
}
else if (oneArg.ToLower().Contains("-iphone"))
{
Debug.Log("\"-iphone\" is detected, switch build target to iphone.");
ms_buildTarget = BuildTarget.iPhone;
return;
}
else if (oneArg.ToLower().Contains("-ios"))
{
Debug.Log("\"-ios\" is detected, switch build target to iphone.");
ms_buildTarget = BuildTarget.iPhone;
return;
}
}
}
Debug.Log("neither \"-android\", \"-ios\" nor \"-iphone\" is detected, current build target is: " + ms_buildTarget);
}
public static void PreBuild()
{
Debug.Log("PreBuild");
UpdateBuildFlag();
SetKgfDebugActive(ms_isDebugBuild);
}
public static void Build()
{
Debug.Log("Build");
UpdateBuildTarget();
BuildOptions buildOption = BuildOptions.None;
if (ms_isDebugBuild)
{
buildOption |= BuildOptions.Development;
buildOption |= BuildOptions.AllowDebugging;
buildOption |= BuildOptions.ConnectWithProfiler;
}
else
{
buildOption |= BuildOptions.None;
}
string locationPathName;
if(BuildTarget.iPhone == ms_buildTarget)
{
locationPathName = XCODE_PROJECT_NAME;
}
else
{
locationPathName = BUILD_OUTPUT_ANDROID;
System.DateTime time = System.DateTime.Now;
locationPathName += "killer_" + time.Month.ToString("D2") + time.Day.ToString("D2") +
"_" + time.Hour.ToString("D2") + time.Minute.ToString("D2") + ".apk";
}
BuildPipeline.BuildPlayer(ms_scenes, locationPathName, ms_buildTarget, buildOption);
}
public static void PostBuild()
{
Debug.Log("PostBuild");
}
private static void SetKgfDebugActive(bool activated)
{
///非重点,略
}
}