最新文章
Cocos2d-x游戏开发实例详解7:对象释放时机
03-25 13:59
Cocos2d-x游戏开发实例详解6:自动释放池
03-25 13:55
Cocos2d-x游戏开发实例详解5:神奇的自动释放
03-25 13:49
Cocos2d-x游戏开发实例详解4:游戏主循环
03-25 13:44
Cocos2d-x游戏开发实例详解3:无限滚动地图
03-25 13:37
Cocos2d-x游戏开发实例详解2:开始菜单续
03-25 13:32
unity3d 一键打包
在开发 Unity3D 项目时,我们常常会面临在不同平台、不同渠道进行打包的需求。例如在 Android 平台下,存在各种不同分辨率、不同内存大小的设备,同时还有不同的渠道包,且不同渠道使用的 SDK 也可能不同。这种情况下,代码很难实现自适应,除非通过批量打包并提供各个平台的预定义标签 #define 来解决。
1. Unity 预定义标签
Unity 默认提供了一些预定义标签,这些标签可以帮助我们在不同的环境下进行条件编译,以下是一些常见的预定义标签:
UNITY_EDITOR:表示在编辑器模式下。UNITY_STANDALONE:适用于 PC、Mac、Linux 平台。UNITY_IPHONE:用于 iOS 模式。UNITY_ANDROID:针对 Android 模式。
官方提供的这些标签属于较大范围的标签。若我们希望在 UNITY_ANDROID 下添加自定义的渠道标签,如 QQ、UC、CMCC 等,该如何操作呢?在 ProjectSetting 打包界面,每个平台都有 Scripting Define Symbols 选项,我们可以在此处添加每个平台对应的自定义标签(多个标签用“;”号隔开)。如果 Android 和 iOS 平台都需要添加相同的渠道标签,则需分别在各自平台的 Scripting Define Symbols 选项中添加。
在代码中,若 Scripting Define Symbols 中未出现的标签,默认是不启用的,就像 #define Test 一样,会自动被注释掉。
2. 脚本批量打包实现
以下是实现脚本批量打包的代码示例:
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
using System;
using System.IO;
public class MyEditorScript
{
// 得到工程中所有场景名称
static string[] SCENES = FindEnabledEditorScenes();
// 一系列批量 build 的操作
[MenuItem("Custom/Build Android QQ")]
static void PerformAndroidQQBuild()
{
BulidTarget("QQ", "Android");
}
[MenuItem("Custom/Build Android UC")]
static void PerformAndroidUCBuild()
{
BulidTarget("UC", "Android");
}
[MenuItem("Custom/Build Android CMCC")]
static void PerformAndroidCMCCBuild()
{
BulidTarget("CMCC", "Android");
}
[MenuItem("Custom/Build Android ALL")]
static void PerformAndroidALLBuild()
{
BulidTarget("QQ", "Android");
BulidTarget("UC", "Android");
BulidTarget("CMCC", "Android");
}
[MenuItem("Custom/Build iPhone QQ")]
static void PerformiPhoneQQBuild()
{
BulidTarget("QQ", "IOS");
}
[MenuItem("Custom/Build iPhone UC")]
static void PerformiPhoneUCBuild()
{
BulidTarget("UC", "IOS");
}
[MenuItem("Custom/Build iPhone CMCC")]
static void PerformiPhoneCMCCBuild()
{
BulidTarget("CMCC", "IOS");
}
[MenuItem("Custom/Build iPhone ALL")]
static void PerformiPhoneALLBuild()
{
BulidTarget("QQ", "IOS");
BulidTarget("UC", "IOS");
BulidTarget("CMCC", "IOS");
}
// 这里封装了一个简单的通用方法
static void BulidTarget(string name, string target)
{
string app_name = name;
string applicationPath = Application.dataPath.Replace("/Assets", "");
string target_dir;
string target_name;
BuildTargetGroup targetGroup;
BuildTarget buildTarget;
if (target == "Android")
{
target_dir = applicationPath + "/TargetAndroid";
target_name = app_name + ".apk";
targetGroup = BuildTargetGroup.Android;
buildTarget = BuildTarget.Android;
}
else if (target == "IOS")
{
target_dir = applicationPath + "/TargetIOS";
target_name = app_name;
targetGroup = BuildTargetGroup.iOS;
buildTarget = BuildTarget.iOS;
}
else
{
throw new ArgumentException("Invalid target platform: " + target);
}
// 每次 build 删除之前的残留
if (Directory.Exists(target_dir))
{
if (File.Exists(target_name))
{
File.Delete(target_name);
}
}
else
{
Directory.CreateDirectory(target_dir);
}
// 根据不同渠道设置相关信息
switch (name)
{
case "QQ":
PlayerSettings.bundleIdentifier = "com.game.qq";
PlayerSettings.bundleVersion = "v0.0.1";
PlayerSettings.SetScriptingDefineSymbolsForGroup(targetGroup, "QQ");
break;
case "UC":
PlayerSettings.bundleIdentifier = "com.game.uc";
PlayerSettings.bundleVersion = "v0.0.1";
PlayerSettings.SetScriptingDefineSymbolsForGroup(targetGroup, "UC");
break;
case "CMCC":
PlayerSettings.bundleIdentifier = "com.game.cmcc";
PlayerSettings.bundleVersion = "v0.0.1";
PlayerSettings.SetScriptingDefineSymbolsForGroup(targetGroup, "CMCC");
break;
}
// 开始 Build 场景
GenericBuild(SCENES, target_dir + "/" + target_name, buildTarget, BuildOptions.None);
}
private static string[] FindEnabledEditorScenes()
{
List<string> EditorScenes = new List<string>();
foreach (EditorBuildSettingsScene scene in EditorBuildSettings.scenes)
{
if (!scene.enabled) continue;
EditorScenes.Add(scene.path);
}
return EditorScenes.ToArray();
}
static void GenericBuild(string[] scenes, string target_dir, BuildTarget build_target, BuildOptions build_options)
{
EditorUserBuildSettings.SwitchActiveBuildTarget(build_target);
string res = BuildPipeline.BuildPlayer(scenes, target_dir, build_target, build_options);
if (res.Length > 0)
{
throw new Exception("BuildPlayer failure: " + res);
}
}
}
3. 不同性能手机资源配置
在开发过程中,我们可能希望在性能高的手机上使用一套优质资源,在性能低的手机上使用一套相对较差的资源。这就需要我们先了解 Unity 会打包哪些资源,哪些资源不会被打包。
3.1 Resources 文件夹
Resources 文件夹是一个只读文件夹,通过 Resources.Load() 方法来读取对象。由于该文件夹下的所有资源都可以在运行时加载,因此其中的所有内容都会被无条件地打包到发布包中。建议该文件夹下只存放 Prefab 或一些 Object 对象,因为 Prefab 会自动过滤掉对象上不需要的资源。例如,若将模型文件和贴图文件都放在 Resources 文件夹中,即使某些贴图文件未被使用,也会被打包到发布包中;而使用 Prefab 则可避免这种情况,从而减小发布包的大小。
3.2 StreamingAssets 文件夹
StreamingAssets 文件夹同样是只读文件夹,但与 Resources 文件夹有所不同。Resources 文件夹下的资源会进行压缩和加密,若不采用特殊方法,无法获取原始资源;而 StreamingAssets 文件夹下的所有资源不会被加密,会原封不动地打包到发布包中,便于获取其中的文件。因此,StreamingAssets 适合存放二进制文件,而 Resources 更适合存放 GameObject 和 Object 文件。需要注意的是,StreamingAssets 只能通过 WWW 类来读取。
3.3 Hierarchy 视图引用的资源
在 Hierarchy 视图对象中引用过的资源文件,也会被无条件地打包到发布包中。若部分文件既不在 Resources 文件夹下,也不在 StreamingAssets 文件夹下,且未被 Hierarchy 视图中的游戏对象引用,则这些资源不会被打包到发布包中。
为了实现不同包对应不同资源包的需求,我们可以将可配置的资源放在 Resources 或 StreamingAssets 文件夹下,在运行时由程序动态加载。在批量打包前,可在 Project 视图下创建不同包的资源文件夹,然后使用 AssetDatabase 脚本动态将资源拷贝至 Resources 或 StreamingAssets 文件夹中。以下是修改后的打包方法示例:
[MenuItem("Custom/Build Android QQ")]
static void PerformAndroidQQBuild()
{
// 先把资源拷贝到 Resources 或者 StreamingAssets
AssetDatabase.CopyAsset("path", "newPath");
// 然后开始编译版本
BulidTarget("QQ", "Android");
}
通过以上步骤,我们可以实现 Unity3D 的一键打包,并根据不同平台、渠道和设备性能进行资源配置。