○経緯
ビルド時間短縮のためいくつかのリソースをAssetBundleに固めて使う事にした。
AssetBundleの生成や管理のもろもろはUnityTechnologiesのAssetBundleManagerを使う事にした。
現在作ろうとしているアプリではサーバを立てずに遊べるものなのでAssetBundleはStreamingAssetsに入れる予定となっている。
しかしAssetBundleはプラットホームごとにファイルを生成するため、すべてStreamingAssetsに入れるなんてことはしない。
SwithPlatform時にシンボリックリンクを張り直せばイケるのではないかと思いいたる。
○対象者
- ある程度AssetBundleがどういうものかを知っている方
- bash や cmd が 苦ではない方
○環境
- Windows10 64bit(後ほどMacOSでも動かせるように拡張する予定)
- Unity 5.6.0p1(Android,iOSにSwtichPlatform出来る状態)
- AssetBundleManager
○事前準備
mklinkコマンドが管理者権限が無いと動きません。
自分は疎かったため、System.Diagnostics.Process に付与すれば動くものだと思っていました。
(苦肉でしたが…)エディタのショートカットの詳細プロパティを変更し、常に管理者権限を有効にしました。
○フォルダ構成の簡易図
ProjectRoot/ Unityプロジェクトのルート
├ AssetBundles AssetBundleManagerが生成するフォルダ
│ ├ Android Android用AssetBundle
│ └ iOS iOS用AssetBundle
└ Assets
└ StreamingAssets
└ bundle 実機に転送されるAssetBundle
○やりたい事
例えば AndroidにSwtichPlatformしたのであれば次のコマンドを実行したい。
rmdir "ProjectRoot/Assets/StreamingAssets/bundle"
mklink /D "ProjectRoot/Assets/StreamingAssets/bundle" "ProjectRoot/AssetBundles/Android"
○成果物
using UnityEngine;
using UnityEditor;
/// <summary>
/// Switch Plarform 時に AssetBundle のリンクを作り直す
/// </summary>
[InitializeOnLoad]
public static class RebuildAssetBundleLink
{
static RebuildAssetBundleLink()
{
EditorUserBuildSettings.activeBuildTargetChanged += SwitchBundleLink;
}
static void SwitchBundleLink()
{
if(Application.platform == RuntimePlatform.WindowsEditor)
{
string bundlePath = "\"" + Application.dataPath + "/StreamingAssets/bundle\"";
string linkPath = "\"" + Application.dataPath.Replace("Assets","") + "AssetBundles/" + EditorUserBuildSettings.activeBuildTarget.ToString() + "\"";
RunTerminal(
new string[]
{
"",//最初にゴミが混じるので空コマンドで退避(要調査)
"rmdir " + bundlePath.Replace("/", "\\"),
"mklink /D " + bundlePath.Replace("/", "\\") + " " + linkPath.Replace("/", "\\"),
}
);
}
}
static void RunTerminal(string[] commands)
{
var p = new System.Diagnostics.Process();
p.StartInfo.FileName = System.Environment.GetEnvironmentVariable("ComSpec");
p.StartInfo.Arguments = "/k cd Assets";
p.StartInfo.Verb = "Runas";
p.StartInfo.CreateNoWindow = true;
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.Start();
foreach (string command in commands)
{
p.StandardInput.WriteLine(command);
}
p.StandardInput.Close();
p.BeginOutputReadLine();
p.BeginErrorReadLine();
p.WaitForExit();
p.Close();
}
}
○今後の課題
- MacOS上でも同様の処理を行うようにする。(Macの購入検討…業務で使っているけど私物がないです)
- System.Diagnostics.Process の理解度を深める。
- 事前準備を行わなくとも済む処理に組み替えられないかの検討。(権限周りの勉強)
- 最初のコマンドでゴミが混ざる原因の調査。(ProcessやStandardInputなど)
- この手のエディタスクリプトはCloudBuildでも有効なのかの調査。
○まとめや所感など
- 実装所要時間:3時間ほど(権限周りにつまずいていた…)
- これで開発効率が上がってくれることを祈ります。
- そろそろアプリ上の挙動や表現に関する技術の知識を向上させたい…。
次回は多解像度に対応するUIを組む際の自分なりの最適解をまとめられたらと思います。(未定)