- 投稿日:2020-05-25T22:40:34+09:00
スマートデバイスプログラミング④(上からランダムに降ってくるボールを拾ってスコアを競うゲーム)
UnityとGameCanvasのダウンロード
1:UnityHubをDLしてインストール。(一部のwindows環境では拡張子を.exeに変更する必要があります)
2:UnityHubを起動して、Unity2019.3.11f1をインストール。
3:GameCanvasのダウンロード
https://github.com/sfc-sdp/GameCanvas-Unity/
でGameCanvasをダウンロード4:追加画像をダウンロードしてAssetsに追加
http://web.sfc.keio.ac.jp/~wadari/sdp/k04_res.zipGame.csの編集
Assets内のGame.csを別エディタで編集
using Sequence = System.Collections.IEnumerator; /// <summary> /// ゲームクラス。 /// 学生が編集すべきソースコードです。 /// </summary> public sealed class Game : GameBase { const int BALL_NUM = 30; int[] ball_x = new int [BALL_NUM]; int[] ball_y = new int [BALL_NUM]; int[] ball_col = new int [BALL_NUM]; int[] ball_speed = new int [BALL_NUM]; int ball_w = 24; int ball_h = 24; int player_x = 304; int player_y = 448; int player_speed = 3; int player_w = 32; int player_h = 32; int player_img = 4; int score = 0; int time = 1800; int player_col = 4; int combo = 0; /// <summary> /// 初期化処理 /// </summary> public override void InitGame() { gc.SetResolution(640, 480); for(int i =0 ; i < BALL_NUM ; i ++ ) { resetBall(i); } } /// <summary> /// 動きなどの更新処理 /// </summary> public override void UpdateGame() { time = time - 1; for(int i =0 ; i < BALL_NUM ; i ++ ) { ball_y[i] = ball_y[i] + ball_speed[i]; if(ball_y[i]> 480){ resetBall(i); } if(gc.CheckHitRect(ball_x[i],ball_y[i],ball_w,ball_h,player_x,player_y,player_w,player_h)){ if(time>=0){ score=score+ball_col[i];//ballごとに違った点数を加える if(player_col == ball_col[i]) { combo++; score+= combo; } else { combo = 0; } player_col = ball_col[i]; } resetBall(i); } } if(gc.GetPointerFrameCount(0) > 0 ){ if(gc.GetPointerX(0) > 320) { player_x += player_speed; player_img = 4; } else { player_x -= player_speed; player_img = 5; } } if(player_x<=0){ player_x=0; } if(player_x>=616){ player_x=616; } } /// <summary> /// 描画の処理 /// </summary> public override void DrawGame() { gc.ClearScreen(); for(int i =0 ; i < BALL_NUM ; i ++ ){ gc.DrawImage(ball_col[i],ball_x[i],ball_y[i]); } gc.SetColor(0,0,0); if(time>=0){ gc.DrawString("time:"+time,0,0); } else { gc.DrawString("finished!!",0,0); } gc.DrawString("score:"+score,0,24); gc.DrawString("combo:"+combo,0,48); //gc.DrawClipImage(player_img,player_x,player_y,0,64,32,32); if(time>=0){ int u = 32+ ((time%60)/30)*32;//0.5秒ごとにユーザー画像を切り替えx int v = (player_col - 1) *32;//最初4 色に応じてユーザーの色の画像を指定y gc.DrawClipImage(player_img,player_x,player_y, u,v,32,32); } else { gc.DrawClipImage(player_img,player_x,player_y, 96,(player_col - 1) *32,32,32); } } void resetBall(int id){ ball_x[id] = gc.Random(0,616); ball_y[id] = -gc.Random(24,480); ball_speed[id] = gc.Random(3,6); ball_col[id] = gc.Random(1,3); } }
- 投稿日:2020-05-25T22:37:44+09:00
スマートデバイスプログラミング③(制限時間内にボールを反射させてブロックを壊すゲーム)
UnityとGameCanvasのダウンロード
1:UnityHubをDLしてインストール。(一部のwindows環境では拡張子を.exeに変更する必要があります)
2:UnityHubを起動して、Unity2019.3.11f1をインストール。
3:GameCanvasのダウンロード
https://github.com/sfc-sdp/GameCanvas-Unity/
でGameCanvasをダウンロードGame.csの編集
Assets内のGame.csを別エディタで編集
using Sequence = System.Collections.IEnumerator; /// <summary> /// ゲームクラス。 /// 学生が編集すべきソースコードです。 /// </summary> public sealed class Game : GameBase { int ball_x; int ball_y; int ball_speed_x; int ball_speed_y; int player_x; int player_y; int player_w; int player_h; const int BLOCK_NUM = 50; int[] block_x = new int [BLOCK_NUM]; int[] block_y = new int [BLOCK_NUM]; bool[] block_alive_flag = new bool [BLOCK_NUM]; int block_w = 64; int block_h = 20; int time ; /// <summary> /// 初期化処理 /// </summary> public override void InitGame() { gc.SetResolution(640, 480); ball_x = 0; ball_y = 0; ball_speed_x = 3; ball_speed_y = 3; player_x = 270; player_y = 460; player_w = 100; player_h = 20; for(int i =0 ; i < BLOCK_NUM ; i ++ ) { block_x[i] = (i % 10 ) * block_w; block_y[i] = (i / 10 ) * block_h; block_alive_flag[i] = true; } time = 0; } /// <summary> /// 動きなどの更新処理 /// </summary> public override void UpdateGame() { if(countBlock()!=0){ time++; } player_y = gc.GetPointerY(0) - player_h/2; ball_x = ball_x + ball_speed_x; ball_y = ball_y + ball_speed_y; if( ball_x < 0 ) { ball_x = 0; ball_speed_x = -ball_speed_x; } if( ball_y < 0 ) { ball_y = 0; ball_speed_y = -ball_speed_y; } if( ball_x > 616 ) { ball_x = 616; ball_speed_x = -ball_speed_x; } if( ball_y > 456 ) { ball_y = 456; ball_speed_y = -ball_speed_y; } if(gc.GetPointerFrameCount(0) > 0 ){ player_x = gc.GetPointerX(0) - player_w/2; } if(gc.CheckHitRect(ball_x,ball_y,24,24,player_x,player_y,player_w,player_h)){ if(ball_speed_y>0){ ball_speed_y = -ball_speed_y; } } for(int i = 0; i<BLOCK_NUM; i++){ if(gc.CheckHitRect(ball_x,ball_y,24,24,block_x[i],block_y[i],block_w,block_h)){ block_alive_flag[i]=false; } } } /// <summary> /// 描画の処理 /// </summary> public override void DrawGame() { // 画面を白で塗りつぶします gc.ClearScreen(); // 0番の画像を描画します gc.DrawImage(0, 0, 0); gc.DrawImage(1,ball_x,ball_y); gc.SetColor(0, 0, 255); gc.FillRect(player_x,player_y,player_w,player_h); for(int i = 0; i<BLOCK_NUM; i++){ if(block_alive_flag[i]){ gc.FillRect(block_x[i],block_y[i],block_w,block_h); } } gc.DrawString("time:"+time,60,0); if(countBlock()==0){ gc.DrawString("clear",60,30); } } int countBlock(){ int num = 0; for(int i =0 ; i < BLOCK_NUM ; i ++ ){ if(block_alive_flag[i]){ num++; } } return num; } }
- 投稿日:2020-05-25T22:34:30+09:00
スマートデバイスプログラミング②(ランダムにカードを出して得点を競うゲーム)
UnityとGameCanvasのダウンロード
1:UnityHubをDLしてインストール。(一部のwindows環境では拡張子を.exeに変更する必要があります)
2:UnityHubを起動して、Unity2019.3.11f1をインストール。
3:GameCanvasのダウンロード
https://github.com/sfc-sdp/GameCanvas-Unity/
でGameCanvasをダウンロードGame.csの編集
Assets内のGame.csを別エディタで編集
using Sequence = System.Collections.IEnumerator; /// <summary> /// ゲームクラス。 /// 学生が編集すべきソースコードです。 /// </summary> public sealed class Game : GameBase { // 変数の宣言 int money; const int CARD_TYPE = 10; int[] card_count = new int [CARD_TYPE]; string[] card_name = {"A","B","C","D","E","F","G","H","I","J"}; bool isComplete; int new_card ; /// <summary> /// 初期化処理 /// </summary> ///起動時に一回だけ呼ばれる・・初期化設定用 public override void InitGame() { resetValue(); } /// <summary> /// 動きなどの更新処理 /// </summary> //1フレームごとに呼ばれる・動きの処理を入れる public override void UpdateGame() { //タップした時の処理 if (gc.GetPointerFrameCount(0)==1 && ! isComplete) { money -= 100; if (gc.Random(0,3)==0){ new_card = gc.Random(0,4); }else{ new_card = gc.Random(5,9); } card_count[new_card]++; for (int i = 0; i < 5; i++) { if (card_count[i] > 4){ isComplete = true; } } } //長押しした時の処理 if(gc.GetPointerFrameCount(0) >= 120){ resetValue(); } } /// <summary> /// 描画の処理 /// </summary> //1フレームごとに呼ばれる・描画の処理 public override void DrawGame() { gc.ClearScreen(); gc.SetColor(255,0,0); gc.SetFontSize(36); gc.DrawString("money:"+money,60, 40); if(new_card >= 0){ gc.DrawString("new:"+card_name[new_card],60, 80); } for(int i=0 ; i< CARD_TYPE ; i++){ gc.DrawString(card_name[i] + ":" + card_count[i],60, 120+i*80); } if(isComplete ){ gc.DrawString("complete!!",60, 920); } } void resetValue(){ money = 10000; for (int i = 0; i < CARD_TYPE; i++) { card_count[i] = 0; } isComplete = false; new_card = -1; } }
- 投稿日:2020-05-25T22:32:09+09:00
スマートデバイスプログラミング①(制限時間内にクリックした回数を競争するゲーム)
UnityとGameCanvasのダウンロード
1:UnityHubをDLしてインストール。(一部のwindows環境では拡張子を.exeに変更する必要があります)
2:UnityHubを起動して、Unity2019.3.11f1をインストール。
3:GameCanvasのダウンロード
https://github.com/sfc-sdp/GameCanvas-Unity/
でGameCanvasをダウンロードGame.csの編集
Assets内のGame.csを別エディタで編集
using Sequence = System.Collections.IEnumerator; /// <summary> /// ゲームクラス。 /// </summary> public sealed class Game : GameBase { // 変数の宣言 int sec = 0; int time = 600; int score = 0; /// <summary> /// 初期化処理 /// </summary> public override void InitGame() { // キャンバスの大きさを設定します gc.SetResolution(720, 1280); } /// <summary> /// 動きなどの更新処理 /// </summary> public override void UpdateGame() { // 起動からの経過時間を取得します sec = (int)gc.TimeSinceStartup; time = time - 1; if(gc.GetPointerFrameCount(0)==1){ if(time >= 0){ score = score + 1; } } if(gc.GetPointerDuration(0) >= 2.0f){ time =600; score =0; } } /// <summary> /// 描画の処理 /// </summary> public override void DrawGame() { // 画面を白で塗りつぶします gc.ClearScreen(); // 0番の画像を描画します gc.DrawImage(0, 0, 0); // 黒の文字を描画します gc.SetColor(0, 0, 0); gc.SetFontSize(48); gc.DrawString("この文字と青空の画像が", 40, 160); gc.DrawString("見えていれば成功です", 40, 270); gc.DrawRightString($"{sec}s", 630, 10); if(time >= 0 ){ gc.DrawString("time:"+time,60,0); } else { gc.DrawString("finished!!",60,0); } gc.DrawString("score:"+score,60,60); } }
- 投稿日:2020-05-25T11:38:23+09:00
Unityど素人の奮闘記
Unityでドミノ倒し作ってみた.
今回はUnityの物理エンジンを使ってドミノ倒しを作ってみました.
ただ同じ大きさのドミノを倒しても面白くないと思いn番煎じながら1.5倍の大きさのドミノを倒していくものにしました.
参考:https://estorypost.com/social-network/youtube/domino-can-topple-1-5-times-its-size/
もはやリアルで示されているものをわざわざやる必要もないのですが笑Step1 地面をつくる
Createから[3D Object]の[Plane]もしくは[Terrain]を選択します.
今回は自分自身の復習の意味もあってTerrainを選択しました.
Terrainを使う際の注意はTerrainの中央がシーンビューの中央とずれていることです.
一辺が500の正方形なのでtransformでPositionをx=-250,y=0,z=-250にしておきましょう.Step2 自然をつくる
AssetStoreからStandardAssetsをインストールしてください.
驚くほど簡単に山や木,湖がつくれてびっくりします.
今回は一応全てインポートしておきましょう.
山や木を作ることは非常に簡単でTerrainから[Raise or Lower]を選択して左クリックで山ができます.
山をへこませたい時はシフトキーを押しながら左クリックをすればいいです.
木も同様に木のマークをクリックしインポートしたStandardAssetsの素材をEditから加えてタッチすれば出来ます(適当)
山の表面,地面の表面の素材も同様にもはやドラッグするだけです.Step3 ドミノ作成
ドミノの作成で注意することは一つだけです.
それは各辺の長さをr倍した時,体積はr^3倍となることです.そのことにだけ注意すれば問題ないと思います.
あとはAdd ComponentでRigidBodyを追加してあげましょう.
Cubeからドミノを作成した場合,Box Colliderは最初から追加されています.
あとは一番小さなドミノを傾けて...
と言いたいところですがまだ何もコードを書いていないので書きましょう.
すべてのドミノが倒れた時に歓声が起こるコードを書きます.Step4 歓声作成
上のようなソースコードでpublic変数としてAudioを追加できるようにしました.
Audioは効果音ラボさんからダウンロードさせていただきました.
効果音ラボ(https://soundeffect-lab.info/)
あとはCreateから[CreateEmpty]を選択し,[Timekeep]と名前を変更しました.
オブジェクトに先ほどのTimekeepスクリプトを追加し,音声も追加しました.
完成!
これからも勉強していきます...
- 投稿日:2020-05-25T11:36:09+09:00
【xcodeエラー】Unity-iPhone has conflicting provisioning settings.が出た時はこうして下さい
今回のエラー
Unityでスマホアプリを作る際にxcodeでビルドを行いました。
ところが下記のようなエラーが出ました。
(私は1度AppStoreにアプリの審査を出しました。1回目は特に何もせずできましたが2回目に下のようなエラーになりました。)エラー内容
●出現場所
こちらの写真はエラーが終わったあとなのでエラーコードは表示されていません。
一番下の行にエラーが出ていました。
●エラーの内容
Unity-iPhone has conflicting provisioning settings.
(その下に英文が何行か書いてあり、code signing identif を "iPhone Developer"にしなさい。みたいな英文が書いてありました。)対処方法
よくわからないですが
TARGETS > Build Settings > Signing
の値を下の画像のように変えました。
そうしたらできました。
なぜできるようになったかは分かりませんが、
下の画像の項目を1つかえて、チェックしてと繰り返し行いました。
無駄に時間を費やしてしまったなと思ったので、私と同じことが起こらないようにとQiitaにまとめておきました。どなたかのお役に立てれば幸いです。
わからないことがあれば
twitterまで連絡ください。
@e_san_desuyo
- 投稿日:2020-05-25T10:23:43+09:00
Unityメモ
JavaScript
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse ws.on('message',function(message){ const obj = JSON.parse(message); console.log("command: ", obj.command); console.log("level: ", obj.level); if( obj.command == 'move'){ console.log('timeElapsed : '+ obj.timeElapsed) } });Unity
https://docs.unity3d.com/ja/2018.4/Manual/JSONSerialization.html [Serializable] public class MyClass { public string command; public int level; public float timeElapsed; } void Update() { MyClass myObject = new MyClass(); myObject.command = "move"; myObject.level = 1; myObject.timeElapsed = 47.5f; string json = JsonUtility.ToJson(myObject); byte[] data = System.Text.Encoding.ASCII.GetBytes(json); ws.Send(data); }unity > stringからbyte[]への変換
byte[] data = System.Text.Encoding.ASCII.GetBytes(text);
- 投稿日:2020-05-25T10:19:37+09:00
【Unity】非同期処理を理解する〜async/await編〜
async/await とは
- C# の機能として用意されている
- C# 5.0の新機能
- Unity 2018 から C# 6.0 に対応した(それまでは C# 4.0 でした。)
- Task クラスに対して使うものという軽い認識(本当は
INotifyCompletion
インターフェースのIsCompleted
とGetResult
)- Unity で
.NET 4.x
を選択すれば使用可能- async = asynchronous = 非同期
- await を使うメソッドにつけなければならないキーワード
- async をつけただけでは普通のメソッドと挙動は変わらない
- await
- 非同期メソッドを呼び出し、完了するまで実行を中断するキーワード
- 戻り値を取得できる(コルーチンは無理)
- 並列処理だけど、普通のメソッドの呼び出しと同じようにかける = 可読性が上がる
基本的な構文は、
async 戻り値の型 メソッド名(引数) { await ~~~~ }戻り値は、基本的に
Task / Task<T>
型と考えて問題ないかと思います。
(といいながら、サンプルはvoid
で書いたりしています…)機能
Task.Delay(Int32 millisecondsDelay)
引数で指定されたミリ秒待機します。
using System.Threading.Tasks; using UnityEngine; public class Test : MonoBehaviour { void Start() { AsyncSample1(); } async void AsyncSample1() { Debug.Log("AsyncSample1 Start."); await Task.Delay(1000); Debug.Log("AsyncSample1 End."); } }今回のサンプルでは、1000ミリ秒 = 1秒 待機するようにしたので、実行結果は以下のようになります。
ContinueWith
直列処理できます。
非同期処理のメソッドも、ContinueWith
でつなぐことで、直列化することができます。ちなみに
ContinueWith
を使わずに、普通に並列的に処理にしたければ、下のようになイメージになります。using System.Threading.Tasks; using UnityEngine; public class Test : MonoBehaviour { void Start() { AsyncSample1(); AsyncSample2(); } async void AsyncSample1() { Debug.Log("AsyncSample1 Start."); await Task.Delay(1000); Debug.Log("AsyncSample1 End."); } async void AsyncSample2() { Debug.Log("AsyncSample2 Start."); await Task.Delay(1000); Debug.Log("AsyncSample2 End."); } }並列なので、
1.AsyncSample1 Start.
2.AsyncSample2 Start.
3.AsyncSample1 End.
4.AsyncSample2 End.
という結果になります。
ContinueWith
を使って直列処理(AsyncSample1 を実行完了後、 AsyncSample2 を実行)するサンプルは、using System.Threading.Tasks; using UnityEngine; public class Test : MonoBehaviour { void Start() { AsyncSample1().ContinueWith(_ => AsyncSample2()); } async Task AsyncSample1() { Debug.Log("AsyncSample1 Start."); await Task.Delay(1000); Debug.Log("AsyncSample1 End."); } async Task AsyncSample2() { Debug.Log("AsyncSample2 Start."); await Task.Delay(1000); Debug.Log("AsyncSample2 End."); } }注意点は、メソッドの返り値の型が Task になっていることくらいです。
AsyncSample1 Start.
AsyncSample1 End.
AsyncSample2 Start.
AsyncSample2 End.
という結果になります。Task.Run()
引数として与えられた処理を別スレッドで実行します。
用途として、重い処理を非同期にしたいときに使います。ちょっと良い例が思いつかないので、超簡単なサンプルだけ書きます。
Task.Run(() => Debug.Log("重い処理..."));Task.WhenAll()
指定された Task が全て完了してから Task を実行することができます。
using System.Threading.Tasks; using UnityEngine; public class Test : MonoBehaviour { async void Start() { await Task.WhenAll(AsyncSample1(), AsyncSample2()); Debug.Log("All Completed."); } async Task AsyncSample1() { Debug.Log("AsyncSample1 Start."); await Task.Delay(1000); Debug.Log("AsyncSample1 End."); } async Task AsyncSample2() { Debug.Log("AsyncSample2 Start."); await Task.Delay(1000); Debug.Log("AsyncSample2 End."); } }
AsyncSample1()
,AsyncSample2()
の実行が完了したら、All Completed.
を出力するようなサンプルです。
実行結果は、
Task<T>
戻り値が欲しい場合は
Task<T>
を使います。using System.Threading.Tasks; using UnityEngine; public class Test : MonoBehaviour { async void Start() { var str = await AsyncSample1(); Debug.Log(str); } async Task<string> AsyncSample1() { Debug.Log("AsyncSample1 Start."); await Task.Delay(1000); return "AsyncSample1 End."; } }
AsyncSample1()
の結果が返ってくるまで、Debug.Log
されると困るので、await
する必要があります。
(待ち受けするイメージです。)
結果は、
終わりに
Unity 初心者なので、間違いがあったら教えてくれるとありがたいです。
Unity で Task 使うなら、 UniTask 使おう。(理由はいまいち知らない…)
理由は UniTask の記事を書くときに、調べたいと思います。参考文献
https://docs.microsoft.com/ja-jp/dotnet/api/system.threading.tasks.task?view=netcore-3.1
https://torikasyu.com/?p=1554
https://docs.microsoft.com/ja-jp/dotnet/api/system.threading.tasks.task.whenall?view=netcore-3.1
https://www.slideshare.net/UnityTechnologiesJapan/unite-tokyo-2018asyncawait
- 投稿日:2020-05-25T02:13:18+09:00
FinalIK.VRIK.GuessHandOrientations()でアバターの手首の回転を調整する
概要
ハンドコントローラの動きに合わせてIKでアバターを動かそうとするとき、ランタイムでアバターを切り替える場合は特に、各アバターによって手のボーンの初期姿勢が異なるために両手があらぬ方向を向くという問題がある気がします。
今回は、VRoomというHMDを被りながら作業通話やゲームをするツールを作っている過程で、上記の問題にどのように対処したのか、書きながら整理しようと思います。
かなり手探りで進めているので、いろいろ教えていただければ幸いです…HMDを被ったままビデオ通話に参加するための環境ができつつある(嬉しい)
— Ytomi (@kanmichun) April 26, 2020
・HMDを被ったままでのモニター操作
・コントローラーとマウスの持ち換え
・内部でのカメラスイッチ
・楽しい室内移動…などが、とりあえず欲しかった pic.twitter.com/nVjCXWbuQqGitHub : Ytomi4/VRoom
環境
- Unity 2019.3.10
- FinalIK ver1.9
目指すところ
アプリ内でアバターを自由に切り替えることを前提に、プレイヤーの手にアバターの手が重なるよう、ボーンの初期姿勢が異なる様々なアバターに対応可能な調整方法を考える。
解決したい問題の特定
便宜的に、アバターによって手首のボーンの初期姿勢が異なることから起こる問題と表現しましたが、具体的には下画像のような状況を指しています。
画像上段は、各アバターの左手にあたるボーン(VRIK.referencesのLeftHandに格納されるGameObject)の姿勢をGizmoで表示したものです。
左手ボーンのローカル空間において、赤が右方向(1,0,0)、緑が上方向(0,1,0)、青が前方向(0,0,1)であることを示しています。上の例が示す通り、アバターの左手(掌)が左手ボーンのローカル空間でどの方向を向いているかは様々です(モデラーの方の出力による)。
Unitychan(画像左上)1では、左手が左手ボーンのローカル空間における左方向(-1,0,0)を向いており、Cygnet(画像中央上)2では左手ボーンのローカル空間における上方向(0,1,0)を向いています。Unitychan(画像左上)とVita(画像右上)3は、手がそれぞれのボーンのローカル空間で左方向(-1,0,0)を向いているという点では同じですが、x軸(赤)を中心に90°回転させたようになっており、たとえば手のひらに対する親指の方向の表現が異なります。
これにより、読み込んだアバターに単純にVRIKコンポーネントをアタッチするだけでは手首があらぬ方向を向きます(画像下段)。
Weightを1にして左手のボーンの回転RotationをTarget(画像ではVIVEコントローラーのモデルの先端)に完全に追従させると、「左手ボーンのローカル空間における右・上・前方向」と「コントローラーのローカル空間における右・上・前方向」とがぴったり重なるように左手ボーンが回転するため、アバターの手があらぬ方向を向いてしまう、と理解しました。上記の理由からアバターによって種々の方向を向く手首を、なんとかコントローラーの前方向を向くようにしたいというのが今回の問題となります。
解決の指針
たとえば、VRIKの公式チュートリアル動画(1:39~)では、コントローラーの位置と回転を反映させたシーン内のGameObjectの子オブジェクトをTargetに設定し、Transformを直接Inspectorからいじるという形で調整しています。
ただし、今回のケースでは、プレイ中のアバターの切り替えを前提としているのでInspectorから直接調整するという手法はとれず、アバター読み込み時にアバターの手のボーンの状態から適当な回転を計算し、親(コントローラー)に対してTargetを回転させる必要があります。
ここでかなり役に立ったのがGuessHandOrientations()でした(リファレンス)。これによって、回転を計算するのに必要な情報である、手首から手のひら方向と手のひらから親指方向とみなせる手のボーンのローカル空間における軸を求められます。内容は決して複雑ではないのですが、言及している記事を自分が見つけられなかった、かつ公式のリファレンスがスカスカだったので、次の話に入る前に少しまとめておきます。
VRIK.GuessHandOrientations()
VRIK.cspublic void GuessHandOrientations() { solver.GuessHandOrientations(references, false); }まず一次的な処理としては、IKSolverVRクラスのインスタンスsolverから
GuessHandOrientations (VRIK.References references, bool onlyIfZero)
を呼び出し、次にsolver.rightHandとsolver.leftHandのwristToPalmAxis
とpalmToThumbAxis
に代入する値(Vector3)を計算しています。それぞれ、手首から手のひら方向・手のひらから親指方向とみなせる手のボーンのローカル空間における軸を意味します。
上の画像の例では、wristToPalmAxis
が(0,1,0)、palmToThumbAxis
が(0,0,1)です。もう少し詳しく見ると、
wristToPalmAxis
は手のボーンから肘にあたる場所のボーンへのベクトルtoForearmと、手のボーンのローカル空間におけるXYZ軸それぞれとの内積を取り、内積が最も小さくなる軸をVector3で返した後で、肘とは反対側を指すようひっくり返した(-1倍した)ものであると説明できます。Final IKのスクリプトから一部抜粋して引用します。IKSolverVR.csleftArm.wristToPalmAxis = GuessWristToPalmAxis(references.leftHand, references.leftForearm);IKSolverVR.csprivate Vector3 GuessWristToPalmAxis(Transform hand, Transform forearm) { Vector3 toForearm = forearm.position -hand.position; Vector3 axis = AxisTools.ToVector3(AxisTools.GetAxisToDirection(hand, toForearm)); if (Vector3.Dot(toForearm, hand.rotation * axis) > 0f) axis = -axis; return axis; }余談ですが、先の説明と引用したコードが示す通り、
wristToPalmAxis
は肘と手首の関係のみから求められ、手のひらと手首の位置関係は関係ないため、たとえば手首が小さく丸まっているようなアバターを想定した場合でも、wristToPalmAxis
がコントローラーの前方向を向くように回転させれば、手首の丸まりを保持したままコントローラーに沿った手のかたちになるといえそうです。
モデラ―の方が出力してくれた肘から手にかけてのニュアンスを(極力)維持したまま、向きをコントローラーにあわせてそれらしく調整できるようになっているかというのはかなり重要な部分だと思います。具体的な解決方法
VRoomのCharacterControl.csでは、アバター読み込み時に以下のような処理を行うことにしました。
- 読み込んだアバターにVRIKコンポーネントをアタッチする
- AutoDetectReferences()でリファレンスを埋める
- AdjustIKTargetsToAvatarsHands()でアバターに合わせてIKTargetの位置・回転を変える
GuessHandOrientations()
で、solver.rightHand
とsolver.leftHand
それぞれのwristToPalmAxis
とpalmToThumbAxis
を求めるwristToPalmAxis
がコントローラーの前方向(0,0,1)を向くような回転leftWristRot
を求め、IKTargetを回転させる
- このとき、あくまで回転させるのはコントローラーの子オブジェクトであるIKTargetのlocalRotationであるため、
wristToPalmAxis
(あるいは回転後のpalmToThumbAxis
)と比較するコントローラーの前方向は常に(0.0,1)と表現できる- コントローラーの前方向(0,0,1)を軸として、
leftWristRot
で回転したpalmToThumbAxis
が、コントローラーの右方向(1,0,0)となす角だけ回る回転leftPalmRotAngle
を求め、IKTargetを回転させる(左手の場合)- さらに回転した
palmToThumbAxis
が左方向(-1,0,0)を向いている場合は、前方向(0,0,1)を軸に180°回転(左手の場合)
- Vector3.Angleの返す
leftPalmRotAngle
はあくまで角度差であるため、90°反対に回転してpalmToThumbAxis
と向くべき方向との間で180°の開きが生まれるパターンがある- 手の位置にオフセットを加える
- オフセットを加えない場合、コントローラーの先端がアバターの手首の位置にあり、不自然に感じる
- オフセットを加える方向はアバターによって異なるので、毎回TargetのlocalPositionはVector3.zeroに戻しておく必要がある
- IKTargetの代入やstretchCurveの初期化など…
CharacterControl.csprivate void AdjustIKTargetsToAvatarsHands() { _vrik.GuessHandOrientations(); //Targetのローカルポジションを(0,0,0)に戻しておく _leftHandIKTarget.transform.localPosition = Vector3.zero; _rightHandIKTarget.transform.localPosition = Vector3.zero; //左手 //wristToPalmAxisがコントローラーの前方向(0,0,1)を向くよう回転 Quaternion leftWristRot = Quaternion.FromToRotation(_vrik.solver.leftArm.wristToPalmAxis, Vector3.forward); _leftHandIKTarget.transform.localRotation = leftWristRot * Quaternion.identity; //palmToThumbAxisがコントローラーの右方向(1,0,0)となす角だけ前方向(0,0,1)を軸に回転 float leftPalmRotAngle = Vector3.Angle(leftWristRot * _vrik.solver.leftArm.palmToThumbAxis, Vector3.right); Quaternion leftPalmRot = Quaternion.AngleAxis(leftPalmRotAngle, Vector3.forward); _leftHandIKTarget.transform.localRotation = leftPalmRot * _leftHandIKTarget.transform.localRotation; //palmToThumbAxisが左方向(-1,0,0)を向いている場合は、前方向(0,0,1)を軸に180°回転 Vector3 leftThumbDirRotated = leftPalmRot * (leftWristRot * _vrik.solver.leftArm.palmToThumbAxis); if (Vector3.Dot(leftThumbDirRotated, Vector3.right) < 0) { _leftHandIKTarget.transform.localRotation = Quaternion.AngleAxis(180, Vector3.forward) * _leftHandIKTarget.transform.localRotation; } //手の位置にオフセットを加える _leftHandIKTarget.transform.localPosition -= _handOffset * Vector3.forward; //右手 //wristToPalmAxisがコントローラーの前方向(0,0,1)を向くよう回転 Quaternion rightWristRot = Quaternion.FromToRotation(_vrik.solver.rightArm.wristToPalmAxis, Vector3.forward); _rightHandIKTarget.transform.localRotation = rightWristRot * Quaternion.identity; //palmToThumbAxisがコントローラーの左方向(1,0,0)となす角だけ前方向(0,0,1)を軸に回転 float rightPalmAngle = Vector3.Angle(rightWristRot * _vrik.solver.rightArm.palmToThumbAxis, Vector3.left); Quaternion rightPalmRot = Quaternion.AngleAxis(rightPalmAngle, Vector3.forward); _rightHandIKTarget.transform.localRotation = rightPalmRot * _rightHandIKTarget.transform.localRotation; //palmToThumbAxisが右方向(1,0,0)を向いている場合は、前方向(0,0,1)を軸に180°回転 Vector3 rightThumbDirRoted = rightPalmRot * (rightWristRot * _vrik.solver.rightArm.palmToThumbAxis); if (Vector3.Dot(rightThumbDirRoted, Vector3.left) < 0) { _rightHandIKTarget.transform.localRotation = Quaternion.AngleAxis(180, Vector3.forward) * _rightHandIKTarget.transform.localRotation; } //手の位置にオフセットを加える _rightHandIKTarget.transform.localPosition -= _handOffset * Vector3.forward; }実際の動き
例に挙げた3体のアバターで確認したところ、プレイヤー側に操作を強いることなく、どのアバターの手もプレイヤーの手と違和感なく重なるようになっていました。
課題
- 確実にベストプラクティスがどこかにあるはずなので、調べる&読む
- 例外をつくらない
leftPalmRot
- 他のボーンとの位置関係から、特定の部位の向いている方向をとるという考え方は応用が利きそう(たとえば指先前方向にRayを飛ばすなど)
- そもそものアバター間の違いが、作られたモデリングソフトの違いによって発生していると思っているが、どうなのだろう
- Quaternionの理解を深めたい
- 投稿日:2020-05-25T01:46:55+09:00
ccacheを導入してUnityのiOSビルドを高速化出来ないか検証してみた
「ccache1がUnityのiOSビルド2でも利用できるのか?」「また、出来るとした場合にはどれくらい効果があるのか?」について調べてみました。
私自身、
ccache
の導入そのものが初だったこともあるので、前半は主に「ccache
のインストール方法や設定項目」などについて記載していき、後半から「Unity上での導入手順及び効果」について記載していければと思います。
(予めccache
を知っている方は前半部分読み飛ばしても良いかも)もし間違いや不足などあればコメントにて教えて頂けると幸いです!
![]()
※まだWIP
書いておいてなんですが...後半の「Unity iOSビルドで導入した際の効果」についてはまだ未調査なところがチラホラと有り、完全に調べきれているとは言い切れないところもあるので、一先ずは「雰囲気で導入してみた際の途中経過までのメモ書き」的な感じで読んで頂ければと思います。。
(未調査項目は必要に駆られたタイミングで調査を再開し、随時追記していく予定..)TL;DR
先にUnity iOSビルドで導入した際の効果について、今の時点での検証結果から纏めます。
(導入関連は特に纏めることが無いので割愛)※Unity iOSビルドで出力される一式を記事中では
xcodeproj
と表記してます。以降の解説でも同様の表記となります。
- Unityから出力した
xcodeproj
にて、一度ビルドした後にCleanしてからリビルドしたらキャッシュヒットしてビルド時間が短くなった- 「Build」と「Archive」は別のキャッシュが作られる
- → 「Build」後 に「Archive」を行っても後者のビルド時間は短くならない
- Unity iOSビルド時に「Append」で
xcodeproj
を更新してもキャッシュヒットは維持される- Unity iOSビルド時に「Replace」で
xcodeproj
を置き換えたらキャッシュヒットしなくなった
- ※「既存のxcodeprojを手動で削除 → ビルド」も同様
結果だけで見ると「Appendでの更新」や「一度ビルドした後のClean/Build」ではキャッシュヒットして高速化出来るものの、Replace相当の手順で
xcodeproj
そのものを置き換えてしまうとキャッシュヒットしなくなるように思われました。とはいえまだ雰囲気で使っているところはある上に、原因については詳細まで追いきれていないので、気が向いたら調査を再開していきたいところ...。
→ 幾つかの未調査項目などはこちらに纏めている。ccacheの導入
macOS上でのccacheの導入方法や各種設定項目についてメモしていきます。
(導入時のOSはmacOS Catalina 10.15.3
)インストール
brew
コマンドからインストールできます。$ brew install ccacheccacheの設定について
幾つか必要そうな情報だけメモします。
詳細はccache -h
でヘルプを参照してください。設定の確認
ccache -p
を叩くことで現在の構成を確認できます。
各項目の概要についてはドキュメントにある「Configuration settings」の章をご確認ください。
ccache -p
の出力結果(クリックで展開)出力結果のプレフィックスに
(default)
と付いている物はデフォルト設定であり、(/Users/<user>/.ccache/ccache.conf)
と付いているものはユーザーが任意で設定変更した項目となります。
※以下で言うとmax_size = 5.0G
が変更されている。# -p, --show-config show current configuration options in # human-readable format $ ccache -p (default) base_dir = (default) cache_dir = /Users/<user>/.ccache (default) cache_dir_levels = 2 (default) compiler = (default) compiler_check = mtime (default) compression = false (default) compression_level = 6 (default) cpp_extension = (default) debug = false (default) depend_mode = false (default) direct_mode = true (default) disable = false (default) extra_files_to_hash = (default) hard_link = false (default) hash_dir = true (default) ignore_headers_in_manifest = (default) keep_comments_cpp = false (default) limit_multiple = 0.8 (default) log_file = (default) max_files = 0 (/Users/<user>/.ccache/ccache.conf) max_size = 5.0G (default) path = (default) pch_external_checksum = false (default) prefix_command = (default) prefix_command_cpp = (default) read_only = false (default) read_only_direct = false (default) recache = false (default) run_second_cpp = true (default) sloppiness = (default) stats = true (default) temporary_dir = (default) umask =キャッシュサイズの変更
キャッシュサイズは
ccache -M
から変更できます。
単位はG
、M
など付ければ指定可能です。# -M, --max-size=SIZE set maximum size of cache to SIZE (use 0 for no # limit); available suffixes: k, M, G, T (decimal) # and Ki, Mi, Gi, Ti (binary); default suffix: G $ ccache -M 1G Set cache size limit to 1.0 GB※設定ファイルから変更する場合
コマンドなどで設定した情報は
ccache.conf
と言うファイルに保存されます。
こちらは幾つか存在しますが、今回は以下のパスにある設定ファイルに変更を加えてます。(詳細は「ドキュメント -> Configuration」を参照)
/Users/<user>/.ccache/ccache.conf
こちらを直接編集することでも設定変更が可能です。
ccache.confmax_size = 1G※環境変数から設定を変更する場合
設定ファイル以外にも環境変数からも設定を変更することが出来ます。
環境変数名は「ドキュメント -> Configuration settings」にある項目名横のカッコ内に記載されてます。(e.g.
max_size (CCACHE_MAXSIZE)
)# 最大キャッシュサイズの変更 $ export CCACHE_MAXSIZE=5Gキャッシュディレクトリの変更
キャッシュディレクトリの変更は以下のコマンドから変更できます。(外部ストレージを指定する例)
こちらもccache.conf
を書き換えたり、環境変数から指定して変更することも可能です。# -o, --set-config=K=V set configuration item K to value V $ ccache -o cache_dir=/Volumes/<外部ストレージ>/ccache
ccache.conf
の書き換えccache.confmax_size = 5G cache_dir = /Volumes/<外部ストレージ>/ccache環境変数から指定
# 最大キャッシュサイズの変更 $ export CCACHE_DIR=/Volumes/<外部ストレージ>/ccache※
-o
コマンドについて
-o
コマンドはKey(設定項目名)
とValue(設定値)
を指定することで変更を適用できるコマンドです。
(イメージ的にはコマンドからccache.conf
を書き換えることが出来る機能)ただし、中には
ccache -M
の様にデフォルトで設定変更がコマンド化されているものもあります。// これら2つは同じ機能 $ ccache -o max_size=1G $ ccache -M 1Gキャッシュデータについて
統計情報の確認
ccache -s
を叩くとキャッシュの統計情報を確認することができます。
各項目の概要についてはドキュメントにある「Cache statistics」の章をご確認ください。# -s, --show-stats show summary of configuration and statistics # counters in human-readable format $ ccache -s cache directory /Users/<user>/.ccache primary config /Users/<user>/.ccache/ccache.conf secondary config (readonly) /usr/local/Cellar/ccache/3.7.9/etc/ccache.conf cache hit (direct) 0 cache hit (preprocessed) 0 cache miss 0 cache hit rate 0.00 % cleanups performed 0 files in cache 0 cache size 0.0 kB max cache size 5.0 GBキャッシュの削除
キャッシュは
ccache -C
で削除できます。# -C, --clear clear the cache completely (except configuration) $ ccache -C Cleared cache※ちなみに、大文字の
-C
と小文字の-c
は別コマンドになっているので注意。# -C, --clear clear the cache completely (except configuration) $ ccache -C # -c, --cleanup delete old files and recalculate size counters # (normally not needed as this is done # automatically) $ ccache -c統計情報のクリア
ccache -s
で出力できる統計情報はccache -z
を叩くことで初期化する事が出来ます。# -z, --zero-stats zero statistics counters $ ccache -z Statistics zeroedUnityのiOSビルドで導入してみた
ここからはUnityのiOSビルドにて導入する際の手順や結果などについて纏めていきます。
導入手順
導入手順については以下の記事を参考にさせていただきました。
順に解説していきます。ちなみに、iOSビルドでは主にUnityから出力された
xcodeproj
に対してccache
を適用し、Xcodeビルドの高速化を行っていく形となります。
→ 逆に言うとUnity側のプロセス(xcodeproj
の出力など)自体はccache
の適用外 と言う認識Xcode側でccacheを呼び出すためのラッパースクリプトを用意
xctoolchain
内にccacheを呼び出すためのラッパースクリプトを用意します。
参考記事に倣ってshell内で完結させてますが、予めccache_wrapper
を別途生成して該当パスに保存 → パーミッションの変更で用意することも可能です。$ sudo vi /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ccache_wrapper ---------------------------------- #!/bin/bash which ccache if [ "$?" -eq 0 ]; then # ※ `CCACHE_SLOPPINESS(sloppiness)`はccache.confで設定することも可能 export CCACHE_SLOPPINESS=pch_defines,time_macros,clang_index_store exec `which ccache` /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -Qunused-arguments "$@" else exec /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -Qunused-arguments "$@" fi ---------------------------------- $ sudo chmod 755 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ccache_wrapperEditor拡張で
xcodeproj
に対してラッパースクリプトを自動設定後はUnity側で以下のEditor拡張を用意します。
こちらを導入することで、iOSビルド時に必要なUser-Defined
等が自動で設定されます。
(上記で用意したccache_wrapper
の設定など)#if UNITY_IOS using System.IO; using UnityEditor; using UnityEditor.Callbacks; using UnityEditor.iOS.Xcode; namespace Samples { static class CcacheSettings { /// <summary> /// xcodeprojにccache向けの設定を適用するサンプル /// </summary> [PostProcessBuild] public static void OnPostprocessBuild(BuildTarget buildTarget, string path) { if (buildTarget == BuildTarget.iOS) { var pbxProjectPath = PBXProject.GetPBXProjectPath(path); var pbxProject = new PBXProject(); pbxProject.ReadFromString(File.ReadAllText(pbxProjectPath)); // refered to: // https://qiita.com/tani-shi/items/e1493e63a02966ef1bac var target = pbxProject.ProjectGuid(); pbxProject.AddBuildProperty(target, "CC", "$(DT_TOOLCHAIN_DIR)/usr/bin/ccache_wrapper"); pbxProject.SetBuildProperty(target, "LDPLUSPLUS", "$(DT_TOOLCHAIN_DIR)/usr/bin/clang++"); File.WriteAllText(pbxProjectPath, pbxProject.WriteToString()); } } } } #endifビルド時間の計測
ビルドに用いる検証用プロジェクトには、以前書いたこちらの記事にあるゲームプロジェクトを利用します。
(実案件レベルとは行かずとも、コンパイル時間を検証するにあたって多少のコード量は欲しかったので)環境/スペック
- macOS Catalina 10.15.3
- MacBook Pro (15-inch, 2018)
- CPU: 2.2 GHz 6コア Intel Core i7
- メモリ 16GB 2400 MHz DDR4
- Unity2019.3.11f1
- Xcode 11.4.1
検証内容
以下に検証項目と期待する動作を定義します。
こちらをccache -zC
でキャッシュ及び統計情報を消した状態で上から順にビルドしていき、ビルド時間やキャッシュヒット率などの変動を見ていきます。
- ①: Unityから出力された
xcodeproj
で「Build → Clean」を3回繰り返す
- → そもそも
ccache
は機能するのか?- ②: ①から継続して「Clean → Archive」を行う
- → ①で得たキャッシュはアーカイブ時にもヒットするのか?
- ③: ①, ②でビルドした
xcodeproj
に対し、Unity側でAppendビルド
- → Unity側でAppendした際にキャッシュは維持されるのか?
- ④: ③の
xcodeproj
に対し、Unity側でReplaceビルド
- →
xcodeproj
を丸々置き換えた際にもキャッシュは維持されるのか?ちなみに今回は「そもそもとして機能するのか?」に注目しているので、(検証としては微妙かもしれませんが...)ビルド毎にコード変更などは加えてません。
記載しているビルド時間はそれを踏まえた結果として見てください。ビルド設定
デフォルトから変更した項目のみ記載します。
各項目の設定についてはドキュメントをご覧ください。compiler_check = content sloppiness = pch_defines,time_macros,clang_index_store①の結果
2回目からキャッシュヒット率が上がっており、ビルド時間が大幅に短くなっていることが確認できました。
この事からccache
自体は正しく機能しているみたいです。
ccache -s
1回目 2回目 3回目 ビルド時間(秒) 202.0sec 25.4sec 24.1sec cache hit (direct) 0 404 808 cache hit (preprocessed) 0 5 5 cache miss 409 409 414 cache hit rate 0.00 % 50.00 % 66.26 % cleanups performed 0 0 0 files in cache 1913 1913 1937 cache size 583.7 MB 583.7 MB 584.2 MB max cache size 5.0 GB 5.0 GB 5.0 GB ②の結果
①の状態から継続してアーカイブを行ってますが、1回目の結果を見る分にはキャッシュヒットが上昇していない上にキャッシュミスが目立っているので、恐らくは①のキャッシュが参照されていないように思われました。 (キャッシュミスの理由についてはまだ未調査...)
2回目からは新たに生成されたキャッシュを見に行っているためか、再びヒット率が上昇してビルド時間が短くなってます。
ccache -s
1回目 2回目 3回目 ビルド時間(秒) 308.7sec 140.6sec 132.5sec cache hit (direct) 808 (prev:808) 1211 1620 cache hit (preprocessed) 5 5 5 cache miss 823 (prev:414) 829 829 cache hit rate 49.69 % 59.46 % 66.22 % cleanups performed 0 0 0 files in cache 3849 3877 3877 cache size 1.8 GB 1.8 GB 1.8 GB max cache size 5.0 GB 5.0 GB 5.0 GB ③の結果
Unity上からAppendして
xcodeproj
を更新した際の結果です。
ビルド時間やキャッシュヒット/ミスの数値を見た感じだと、①と②で生成されたキャッシュがちゃんとヒットしているように思われます。
ccache -s
1回目(Clean → Build) 2回目(Clean → Archive) ビルド時間(秒) 25.0sec 142.1sec cache hit (direct) 2029 (prev:1620) 2438 cache hit (preprocessed) 5 5 cache miss 829 (prev:829) 829 cache hit rate 71.04 % 74.66 % cleanups performed 0 0 files in cache 3877 3877 cache size 1.8 GB 1.8 GB max cache size 5.0 GB 5.0 GB ④の結果
最後にUnity上からReplaceして
xcodeproj
を置き換えた際の結果です。
(ちなみに「既存のxcodeproj
を手動で削除 → ビルド」の手動Replaceも同様の結果です)こちらはBuild/Archive共に殆どキャッシュヒットしていない様に見受けられました。
ccache
の設定は以下の様になっており、更新チェックに関する条件自体は問題無さそうに思われますが...なぜ適用されていないのかまでは追いきれておらず...。
base_dir
は設定していないので、対象は絶対パスで制御される
- → Unityから出力される
xcodeproj
は常に同名で出力しており、パス自体は変わっていないので適用されるはず...- 更新ファイルのチェック方法は
content(ファイル内容を基にチェック)
で設定
- → チェック方法をデフォルトの
mtime(mtimeとsizeのhash)
に戻しても変わらず...- 出力されたIL2CPPのコードにも目立った差分は無い
ccache -s
1回目(Clean → Build) 2回目(Clean → Archive) ビルド時間(秒) 195.6sec 301.8sec cache hit (direct) 2439 (prev:2438) 2440 cache hit (preprocessed) 10 10 cache miss 1232 (prev:829) 1640 cache hit rate 66.53 % 59.90 % cleanups performed 0 2 files in cache 5350 6576 cache size 2.4 GB 3.4 GB max cache size 5.0 GB 5.0 GB 結果を踏まえて
個人的には「Replace相当のビルドを行った際にもキャッシュヒットしてくれると嬉しい3」と言うモチベーションから調査を開始したこともあったので、今の時点での結果としては少し微妙だった...と言ったところです。
原因については特定しきれていないので、気が向いたら調査を再開していきたい...。
未調査項目
幾つか挙げます。
- Xcode側のビルド設定の詳細把握
- Xcode/ccache共に設定に抜けが無いか確認
- ドキュメントにある「Troubleshooting -> Performance」を読む分にはマクロ周りが影響を及ぼしている可能性も考えられる...?
- 後は「揮発性の情報を含む自動生成コード」と言うのも引っ掛かってそうな感じもあったり...
![]()
- 「Cache debugging」にあるデバッグモードを有効にすることで原因調査を進められそう?
- まだ未検証...
補足: Androidは?
「内部的にNDK使ってそうだし、一部のビルドプロセスは高速化出来るかな?」と仮説立てて調べてみましたが...結論から言えばダメそう...?(調査不足の可能性あり)
環境変数の
NDK_CCACHE
などを設定した状態でAndroidビルドを回してみましたが、ccache -s
で統計情報を見る分には何も動いていないように思われました。後はこの状態で
[Export Project]
をON/OFFにしてビルドを行い、双方のビルドレポートを確認した感じだと、どちらも支配的なのはIL2CPP
と言うプロセスでした。
こちらが文字通り「ILからCPPに変換するツール側のプロセス」でコンパイラが一体関与しない類のものであれば、そもそもとして適用自体でき無さそうとも考えられそうです。(IL2CPPをコードレベルで追ったわけではないので予想にはなりますが...)参考/関連サイト
- ccache.dev(公式サイト)
- Ccache - ArchWiki
- ccache でビルド高速化。と設定のポイント
- ccacheを導入してC++のコンパイルを最適化
- ccacheを使って共同作業のビルド時間を改善する
- cocos2d-x(Android NDK)ビルドを高速化する方法とオススメ設定
- C/C++のコンパイルを高速化する|ccache
簡単に言えばC/C++に於けるビルド高速化ツール。ざっくりとした概要はWikipediaの記事参照 ↩
正確に言うとUnityが出力した
xcodeproj
に対するXcodeビルド ↩例えばビルドプロセスを自動化する際に、ビルド毎に影響が残らないように敢えてReplace相当でビルドすることがあるので、ここでキャッシュヒットして高速化されると嬉しかったりする。 ↩