- 投稿日:2020-07-31T14:46:56+09:00
【Unity】GithubActionsでUnityを起動してTestRunner実行後の結果を出力するまでの手順
特定のリポジトリにアセットをpushしたタイミングでバリデーションを走らせたい場面があります。
例えばバリデーションに成功した後でpushするというルールを決めたとしても保証は出来ません。それらのアセットがバリデーション済みの保証はできません。
こういう手動に頼らないといけない部分は自動化を検討したいものです。
そこでGithubActionsを使った納品物(アセット)のバリデーションを確実に走らせるようにしてみます。今回は【Unity】TestRunnerを使ったアセットバリデーションツールを最短経路で作ってみるで作成したアセットバリデーションをGithubActionsで実行できるようにしていきます。
先人に感謝
【Unity】GitHub Actions v2でUnity Test Runnerを走らせて、結果をSlackに報告する【入門】
基本はこちらの記事を参考に進めていってます。
とても参考になりました。ありがとうございます。環境
- Unity2019.4.4f1
GithubActionsでTestRunnerを走らせるまでの準備
- ALFファイルの作成
- ULFファイルの取得
- ULFファイルの暗号化
GithubActionsでUnityを走らせるためにはライセンス認証が必要となります。
そのためにマニュアルアクティベーションを攻略していきます。
参考 : Offline / Manual Activation1.ALFファイルの作成
先のワークフロー図のCreateALFプライベートリポジトリでの作業です。
- Unityを起動してALFファルを作成
- 成果物として出力
このような手順のyamlを作成してGithubActionsを実行します。
CreateALF.yamlname: Create ALF File on: [push] jobs: build: runs-on: ubuntu-latest container: docker://gableroux/unity3d:2019.4.4f1 steps: - run: mkdir artifact # 1.Unityを起動してALFファルを作成 - run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -logfile -createManualActivationFile || exit 0 - run: cp "Unity_v2019.4.4f1.alf" artifact # 2.成果物として出力 - uses: actions/upload-artifact@master with: name: 2019.4.4f1 path: artifactこのワークフローが成功すると、このようにALFが成果物としてダウンロードできるようになります。
自分はCI・GithubActions初学者なのでGithubActionsワークフローの構文について調査しておきます。
文法 内容 備考 name ワークフロー名 任意の名前 on 実行トリガー push, issueなど jobs ジョブリスト build ジョブ名 任意の名前 runs-on (必須)ジョブを実行するマシンの種類指定 container 指定コンテナ steps 一連のタスク すべてのアクションはステップとして実行される run OSのシェルを実行 uses ステップの一部として実行されるアクション with usesで指定したアクションに必要なパラメータ KEY・VALUEのmap その他構文的に分からなかった部分の調査
actions/upload-artifact
actions/upload-artifact
に付随するwith
のの調査です。CreateALF(抜粋).yaml- uses: actions/upload-artifact@master with: name: 2019.4.4f1 path: artifact
actions/upload-artifact
とはGithub公式が提供するビルドの成果物をアップロードするアクションです。引き渡すパラメータにname
とpath
をwithでセットしています。
name
とpath
の必要性の調べ方本家の
actions/upload-artifact
のソースを確認します。
https://github.com/actions/upload-artifact/blob/main/action.yml以下その一部です。
action(抜粋).ymlinputs: name: description: 'Artifact name' required: false path: description: 'A file, directory or wildcard pattern that describes what to upload' required: trueこのようにinputs(入力)に
name
とpath
をパラメータとして必要としている事が分かります。pathはrequired: true
なので必須です。CreateALF.yamlで定義した
with
の内容がactions/upload-artifact
の入力情報として渡されたという事が分かり、スッキリしました。actions/upload-artifactの後ろの「@」について
@
をつけるとバージョンを指定できます。
- @ブランチ名
- @コミットハッシュ
という使い方が出来るようです。
参考 : バージョンされたアクションを使用する例
2.ULFファイルの取得
ULFとはUnityLicenseFileの事で、中身に認証情報が格納されています。
このファイルがオフライン認証を可能にするため重要なファイルです。このファイルを取得する方法です。https://license.unity3d.com/manual にアクセスします。
キャプチャのように先程取得したALFファイルを突っ込んでNextボタンをクリックします。ファイル命名バリデーションが存在する
Unity_v2019.4.4f1.alfでは命名で弾かれてしまいました。
正しくは、Unity_v2019.4.4.alf
です。
ファイル命名に注意が必要です。その後の質問に回答し、無事にULFファイルを取得できました。
ファイル名はUnity_v2019.x.ulf
です。ULFファイルの暗号化
CreateULFプライベートリポジトリでの作業です。
先程取得したULFファイルをこのリポジトリのルートに配置します。
ULFファイル(Unity_v2019.x.ulf)はXMLなので中身が丸見えです。
参考サイトの通り、事前にopensslで暗号化して使用時に復号化して使うことにします。事前にGithub上でパスワードを作成
Settings > Secrets
から暗号化に使うパスワードを作成します。このようにKeyとValueをセットにした鍵を作成することが出来ます。GithubActions上で以下のようにValueのパスワードにアクセスすることが出来ます。
${{ secrets.Key名 }}
実際のコードでは
${{ secrets.CYPHERKEY }}
でアクセスしています。平文のULFファイルを暗号化するGithubActionsが以下です。
CreateULF.yamlname: Create Unity License File on: [push] jobs: job: runs-on: ubuntu-latest steps: - uses: actions/checkout@master - run: mkdir -p artifact - run: openssl aes-256-cbc -e -in Unity_v2019.x.ulf -out artifact/Unity_v2019.x.ulf-cipher -k ${{ secrets.CYPHERKEY }} - uses: actions/upload-artifact@master with: name: ciphers path: artifactワークフローが完了すると成果物として暗号化されたULFが取得できます。
ファイル名を分かりやすくUnity_v2019.x.ulf-cipher
としています。GithubActionsでTestRunnerを実行する
TestRunnerWithGithubActionsパブリックリポジトリでの作業です。
https://github.com/baobao/unity-test-runner-with-github-actions以前執筆した【Unity】TestRunnerを使ったアセットバリデーションツールを最短経路で作ってみるで作成したUnityプロジェクトをGithubActionsでEditorModeTestを走らせます。ソースを丸コピしています。
ちなみにローカルPCでコマンドラインからUnityのEditorModeを実行するコマンドは以下です。これをGithubActionsのジョブに組み込んでいきます。
'/Applications/Unity Hub/2019.4.4f1/Unity.app/Contents/MacOS/Unity' -runTests -projectPath /パス(省略)/プロジェクト名 -batchmode -testPlatform EditMode以下の手順のジョブをyamlに書いてGithubActionsで実行してみます。
- 暗号化されたULFの復号化
- Unity起動 ULFを使ってライセンス認証
- Unity起動 EditorModeTestの実行
- 成果物をartifactディレクトリにアップロード
run-test-runner.ymlname: UnityTestRunnerWithGithubActions on: push: branches: - master jobs: editorTestJob: runs-on: ubuntu-latest container: docker://gableroux/unity3d:2019.4.4f1 steps: - uses: actions/checkout@master # TestRunnerの結果を出力するディレクトリ作成 - run: mkdir -p artifact # 暗号化したULFから復号化したULFを取り出す - run: openssl aes-256-cbc -d -in Unity_v2019.x.ulf-cipher -k ${CYPHER_KEY} >> /Unity_v2019.x.ulf env: CYPHER_KEY: ${{ secrets.cypherkey }} # Unity起動 ULFを使ってライセンス認証 - run: /opt/Unity/Editor/Unity -manualLicenseFile /Unity_v2019.x.ulf -batchmode -nographics -quit || exit 0 # Unity起動 EditorModeTestの実行 # 結果(results.xm;)をpath/to/artifact/に出力 - run: /opt/Unity/Editor/Unity -batchmode -nographics -silent-crashes -logFile -projectPath . -runEditorTests -editorTestsResultFile artifact/results.xml || exit 0 # 成果物をartifactディレクトリにアップロード - uses: actions/upload-artifact@master with: name: test-result path: artifactこのGithubActionsを実行すると以下のようにTestの結果を受け取ることが出来ます。
テストの時間と無料枠
1回のテストに3分半ほど掛かるようです。
Github Freeの場合2000分/月の無料枠です。このレベルのジョブで約570回実行できるという事になります。実用的か現段階では分からないです。
ちなみにGithubActionsの無料枠を使い切った場合は、単純に使えなくなるだけのようですね。
参考
- 投稿日:2020-07-31T00:24:46+09:00
MagicLeapのハンドトラッキングの実装
MagicLeapのハンドトラッキング実装の備忘録
基本的にはここの通りに進めていけば問題ない
前提となるもの
- MagicLeapのmpkをビルドできる段階までのセッティングが完了している
- The LabでのZeroIterationが可能になっている
開発環境
Windows10
Unity 2019.3.7f1
MagicLeapUnityPackage 0.24.1出来上がり
こんな感じに各関節
手首、親指 ~ 小指までの座標の取得
ジェスチャの取得( サンプルの中では利用していない )MagicLeapのハンドトラッキング実装してみた pic.twitter.com/ZSYl1BVXay
— 松本隆介 (@matsumotokaka11) July 30, 2020実装方法
下準備
シーンに配置するGameObject
大体のVRデバイスとかで主流な構成の配置にしてます
CameraRig
ただのルートオブジェクトとして利用していますHead
MagicLeapパッケージ内 Core > Assets > Prefabs > MainCamera
のプレハブをCameraRigの子オブジェクトにしてHeadと名称を変えたものです、この中にDirectionalLightを子オブジェクトとして配置していますが配置するか否かはお好みで
Controller
MagicLeapパッケージ内 Examples > Assets > Prefabs > Controller
のプレハブをCameraRigの子オブジェクトとして配置しています
こちらの記事で紹介したコントローラの入力でホームボタンでアプリを閉じるために利用してます
LHand, RHand
今回のメイン
CameraRigの子オブジェクトとしてGameObjectを作成( Emptyとして )
Thumb( 親指 ), Index( 人差し指 ), Middle( 中指 ), Ring( 薬指 ), Pinky( 小指 )をそれぞれEmptyのGameObjectで作成し、
各指の子オブジェクトThumb, Index, Middleは3個、Ring, Pinkyは2個、SphereObjectを生成、スケールは0.01くらいに設定
スクリプト
このスクリプトはMagicLeap公式サンプルのものに手を加えたものです
以下のスクリプトをLHand, RHandにアタッチ/// <summary> /// ハンドトラッキング. /// </summary> public class HandController : MonoBehaviour { [System.Serializable] public class HandJointData { [SerializeField] GameObject wrist; [SerializeField] GameObject[] thumb; [SerializeField] GameObject[] index; [SerializeField] GameObject[] middle; [SerializeField] GameObject[] ring; [SerializeField] GameObject[] pinky; [SerializeField] Material handMaterial; [SerializeField] Color color; public Vector3 Wrist { get; private set; } public Vector3[] Thumb { get; private set; } public Vector3[] Index { get; private set; } public Vector3[] Middle { get; private set; } public Vector3[] Ring { get; private set; } public Vector3[] Pinky { get; private set; } MLHandTracking.Hand hand; LineRenderer[] lines; public void Initialize( MLHandTracking.Hand _hand) { hand = _hand; Wrist = Vector3.zero; // 各関節 + 手首の分, 配列をとる. Thumb = new Vector3[thumb.Length + 1]; Index = new Vector3[index.Length + 1]; Middle = new Vector3[middle.Length + 1]; Ring = new Vector3[ring.Length + 1]; Pinky = new Vector3[pinky.Length + 1]; lines = new LineRenderer[5]; lines[0] = thumb[0].transform.parent.gameObject.AddComponent<LineRenderer>(); lines[0].positionCount = 4; lines[1] = index[0].transform.parent.gameObject.AddComponent<LineRenderer>(); lines[1].positionCount = 4; lines[2] = middle[0].transform.parent.gameObject.AddComponent<LineRenderer>(); lines[2].positionCount = 4; lines[3] = ring[0].transform.parent.gameObject.AddComponent<LineRenderer>(); lines[3].positionCount = 3; lines[4] = pinky[0].transform.parent.gameObject.AddComponent<LineRenderer>(); lines[4].positionCount = 3; // LineRendererの初期セッティング. foreach (var line in lines) { line.material = handMaterial; line.startColor = color; line.endColor = color; line.startWidth = 0.01f; line.endWidth = 0.01f; } } public void UpdatePositions() { if (hand == null) return; Thumb[0] = hand.Wrist.KeyPoints[0].Position; Thumb[1] = hand.Thumb.KeyPoints[0].Position; Thumb[2] = hand.Thumb.KeyPoints[1].Position; Thumb[3] = hand.Thumb.KeyPoints[2].Position; for (var i = 1; i < Thumb.Length; ++i) { thumb[i - 1].transform.position = Thumb[i]; } lines[0].SetPositions(Thumb); Index[0] = hand.Wrist.KeyPoints[0].Position; Index[1] = hand.Index.KeyPoints[0].Position; Index[2] = hand.Index.KeyPoints[1].Position; Index[3] = hand.Index.KeyPoints[2].Position; for (var i = 1; i < Index.Length; ++i) { index[i - 1].transform.position = Index[i]; } lines[1].SetPositions(Index); Middle[0] = hand.Wrist.KeyPoints[0].Position; Middle[1] = hand.Middle.KeyPoints[0].Position; Middle[2] = hand.Middle.KeyPoints[1].Position; Middle[3] = hand.Middle.KeyPoints[2].Position; for (var i = 1; i < Middle.Length; ++i) { middle[i - 1].transform.position = Middle[i]; } lines[2].SetPositions(Middle); Ring[0] = hand.Wrist.KeyPoints[0].Position; Ring[1] = hand.Ring.KeyPoints[0].Position; Ring[2] = hand.Ring.KeyPoints[1].Position; for (var i = 1; i < Ring.Length; ++i) { ring[i - 1].transform.position = Ring[i]; } lines[3].SetPositions(Ring); Pinky[0] = hand.Wrist.KeyPoints[0].Position; Pinky[1] = hand.Pinky.KeyPoints[0].Position; Pinky[2] = hand.Pinky.KeyPoints[1].Position; for (var i = 1; i < Pinky.Length; ++i) { pinky[i - 1].transform.position = Pinky[i]; } lines[4].SetPositions(Pinky); } } // ジェスチャ. public enum HandPoses { Ok, Finger, Thumb, OpenHand, Fist, NoPose, NoHand, } public enum HandId { RightHand, LeftHand } [SerializeField] HandPoses handPose = HandPoses.NoPose; [SerializeField] HandJointData handData; [SerializeField] HandId handId; MLHandTracking.HandKeyPose[] gestures; MLHandTracking.Hand hand; void Start() { // HandTrackingを開始する. MLHandTracking.Start(); hand = handId == HandId.LeftHand ? MLHandTracking.Left : MLHandTracking.Right; handData.Initialize(hand); gestures = new MLHandTracking.HandKeyPose[6]; // 各ジェスチャを登録. gestures[0] = MLHandTracking.HandKeyPose.Ok; gestures[1] = MLHandTracking.HandKeyPose.Finger; gestures[2] = MLHandTracking.HandKeyPose.OpenHand; gestures[3] = MLHandTracking.HandKeyPose.Fist; gestures[4] = MLHandTracking.HandKeyPose.Thumb; gestures[5] = MLHandTracking.HandKeyPose.NoHand; MLHandTracking.KeyPoseManager.EnableKeyPoses(gestures, true, false); } void OnDestroy() { MLHandTracking.Stop(); } void Update() { handData.UpdatePositions(); if (GetGesture(hand, MLHandTracking.HandKeyPose.Ok)) { handPose = HandPoses.Ok; } else if (GetGesture(hand, MLHandTracking.HandKeyPose.Finger)) { handPose = HandPoses.Finger; } else if (GetGesture(hand, MLHandTracking.HandKeyPose.OpenHand)) { handPose = HandPoses.OpenHand; } else if (GetGesture(hand, MLHandTracking.HandKeyPose.Fist)) { handPose = HandPoses.Fist; } else if (GetGesture(hand, MLHandTracking.HandKeyPose.Thumb)) { handPose = HandPoses.Thumb; } else { handPose = HandPoses.NoPose; } } /// <summary> /// ジェスチャの取得. /// </summary> /// <param name="hand"></param> /// <param name="type"></param> /// <returns></returns> private bool GetGesture( MLHandTracking.Hand hand, MLHandTracking.HandKeyPose type) { if (hand == null) return false; return 0.9f < hand.HandKeyPoseConfidence && hand.KeyPose == type; } }アタッチしたら以下の画像のように各関節のオブジェクトをセット、配列の添え字が若い方が根元に来るように設定
各パラメータの説明
HandPose : ジェスチャのポーズ名( 今回はInspectorに表示しているだけです )
HandData : 各関節のオブジェクトを保持するクラス、HandCenterは利用していません( うまくトラッキングできなかったので外しました )
HandMaterial : 各関節オブジェクトの球をつなぐ線の描画用マテリアルです、今回はMagicLeapパッケージ内のUIBeamを利用しました
Color : 各関節オブジェクトの球をつなぐ線の色です、今回は左は赤、右は緑で設定しています
HandId : 手の左右を決定する識別子Unity の設定
この状態でTheLabでMagicLeapと接続してPlayModeに入るとハンドトラッキングされたオブジェクトの様子が確認できると思います
ただしmpkファイルとして出力する際は以下の設定を行わないと実機ではエラーが出てハンドトラッキング及びジェスチャの取得はできません
公式のチュートリアル通りにやれば設定の仕方まで説明されてたけど必要なメソッドとか確認したらすぐ実行したくなっちゃうのよねEdit > ProjectSettigs > MagicLeap > ManifestSettings の項目を開き
GestureConfig, GestureSubscribeにチェックを入れる ( 公式チュートリアルだと明示的にLowLatencyLightwearにもチェックを入れるように説明されているが現バージョンでは自動で入ってる? )これでmpkを出力して実機でテストするとハンドトラッキングが実装できているはずです
あとがき
これはTheLabからDLしてきたMagicLeapUnityPackageでのサンプルです、MagicLeapToolKitでのハンドトラッキングはまだ触ったことがないので後日記事にできればと思います
- 投稿日:2020-07-31T00:24:46+09:00
MagicLeapのハンドトラッキング実装
MagicLeapのハンドトラッキング実装の備忘録
基本的にはここの通りに進めていけば問題ない
前提となるもの
- MagicLeapのmpkをビルドできる段階までのセッティングが完了している
- The LabでのZeroIterationが可能になっている
開発環境
Windows10
Unity 2019.3.7f1
MagicLeapUnityPackage 0.24.1出来上がり
こんな感じに各関節
手首、親指 ~ 小指までの座標の取得
ジェスチャの取得( サンプルの中では利用していない )MagicLeapのハンドトラッキング実装してみた pic.twitter.com/ZSYl1BVXay
— 松本隆介 (@matsumotokaka11) July 30, 2020実装方法
下準備
シーンに配置するGameObject
大体のVRデバイスとかで主流な構成の配置にしてます
CameraRig
ただのルートオブジェクトとして利用していますHead
MagicLeapパッケージ内 Core > Assets > Prefabs > MainCamera
のプレハブをCameraRigの子オブジェクトにしてHeadと名称を変えたものです、この中にDirectionalLightを子オブジェクトとして配置していますが配置するか否かはお好みで
Controller
MagicLeapパッケージ内 Examples > Assets > Prefabs > Controller
のプレハブをCameraRigの子オブジェクトとして配置しています
こちらの記事で紹介したコントローラの入力でホームボタンでアプリを閉じるために利用してます
LHand, RHand
今回のメイン
CameraRigの子オブジェクトとしてGameObjectを作成( Emptyとして )
Thumb( 親指 ), Index( 人差し指 ), Middle( 中指 ), Ring( 薬指 ), Pinky( 小指 )をそれぞれEmptyのGameObjectで作成し、
各指の子オブジェクトThumb, Index, Middleは3個、Ring, Pinkyは2個、SphereObjectを生成、スケールは0.01くらいに設定
スクリプト
このスクリプトはMagicLeap公式サンプルのものに手を加えたものです
以下のスクリプトをLHand, RHandにアタッチusing UnityEngine; using UnityEngine.XR.MagicLeap; /// <summary> /// ハンドトラッキング. /// </summary> public class HandController : MonoBehaviour { [System.Serializable] public class HandJointData { [SerializeField] GameObject wrist; [SerializeField] GameObject[] thumb; [SerializeField] GameObject[] index; [SerializeField] GameObject[] middle; [SerializeField] GameObject[] ring; [SerializeField] GameObject[] pinky; [SerializeField] Material handMaterial; [SerializeField] Color color; public Vector3 Wrist { get; private set; } public Vector3[] Thumb { get; private set; } public Vector3[] Index { get; private set; } public Vector3[] Middle { get; private set; } public Vector3[] Ring { get; private set; } public Vector3[] Pinky { get; private set; } MLHandTracking.Hand hand; LineRenderer[] lines; public void Initialize( MLHandTracking.Hand _hand) { hand = _hand; Wrist = Vector3.zero; // 各関節 + 手首の分, 配列をとる. Thumb = new Vector3[thumb.Length + 1]; Index = new Vector3[index.Length + 1]; Middle = new Vector3[middle.Length + 1]; Ring = new Vector3[ring.Length + 1]; Pinky = new Vector3[pinky.Length + 1]; lines = new LineRenderer[5]; lines[0] = thumb[0].transform.parent.gameObject.AddComponent<LineRenderer>(); lines[0].positionCount = 4; lines[1] = index[0].transform.parent.gameObject.AddComponent<LineRenderer>(); lines[1].positionCount = 4; lines[2] = middle[0].transform.parent.gameObject.AddComponent<LineRenderer>(); lines[2].positionCount = 4; lines[3] = ring[0].transform.parent.gameObject.AddComponent<LineRenderer>(); lines[3].positionCount = 3; lines[4] = pinky[0].transform.parent.gameObject.AddComponent<LineRenderer>(); lines[4].positionCount = 3; // LineRendererの初期セッティング. foreach (var line in lines) { line.material = handMaterial; line.startColor = color; line.endColor = color; line.startWidth = 0.01f; line.endWidth = 0.01f; } } public void UpdatePositions() { if (hand == null) return; Thumb[0] = hand.Wrist.KeyPoints[0].Position; Thumb[1] = hand.Thumb.KeyPoints[0].Position; Thumb[2] = hand.Thumb.KeyPoints[1].Position; Thumb[3] = hand.Thumb.KeyPoints[2].Position; for (var i = 1; i < Thumb.Length; ++i) { thumb[i - 1].transform.position = Thumb[i]; } lines[0].SetPositions(Thumb); Index[0] = hand.Wrist.KeyPoints[0].Position; Index[1] = hand.Index.KeyPoints[0].Position; Index[2] = hand.Index.KeyPoints[1].Position; Index[3] = hand.Index.KeyPoints[2].Position; for (var i = 1; i < Index.Length; ++i) { index[i - 1].transform.position = Index[i]; } lines[1].SetPositions(Index); Middle[0] = hand.Wrist.KeyPoints[0].Position; Middle[1] = hand.Middle.KeyPoints[0].Position; Middle[2] = hand.Middle.KeyPoints[1].Position; Middle[3] = hand.Middle.KeyPoints[2].Position; for (var i = 1; i < Middle.Length; ++i) { middle[i - 1].transform.position = Middle[i]; } lines[2].SetPositions(Middle); Ring[0] = hand.Wrist.KeyPoints[0].Position; Ring[1] = hand.Ring.KeyPoints[0].Position; Ring[2] = hand.Ring.KeyPoints[1].Position; for (var i = 1; i < Ring.Length; ++i) { ring[i - 1].transform.position = Ring[i]; } lines[3].SetPositions(Ring); Pinky[0] = hand.Wrist.KeyPoints[0].Position; Pinky[1] = hand.Pinky.KeyPoints[0].Position; Pinky[2] = hand.Pinky.KeyPoints[1].Position; for (var i = 1; i < Pinky.Length; ++i) { pinky[i - 1].transform.position = Pinky[i]; } lines[4].SetPositions(Pinky); } } // ジェスチャ. public enum HandPoses { Ok, Finger, Thumb, OpenHand, Fist, NoPose, NoHand, } public enum HandId { RightHand, LeftHand } [SerializeField] HandPoses handPose = HandPoses.NoPose; [SerializeField] HandJointData handData; [SerializeField] HandId handId; MLHandTracking.HandKeyPose[] gestures; MLHandTracking.Hand hand; void Start() { // HandTrackingを開始する. MLHandTracking.Start(); hand = handId == HandId.LeftHand ? MLHandTracking.Left : MLHandTracking.Right; handData.Initialize(hand); gestures = new MLHandTracking.HandKeyPose[6]; // 各ジェスチャを登録. gestures[0] = MLHandTracking.HandKeyPose.Ok; gestures[1] = MLHandTracking.HandKeyPose.Finger; gestures[2] = MLHandTracking.HandKeyPose.OpenHand; gestures[3] = MLHandTracking.HandKeyPose.Fist; gestures[4] = MLHandTracking.HandKeyPose.Thumb; gestures[5] = MLHandTracking.HandKeyPose.NoHand; MLHandTracking.KeyPoseManager.EnableKeyPoses(gestures, true, false); } void OnDestroy() { MLHandTracking.Stop(); } void Update() { handData.UpdatePositions(); if (GetGesture(hand, MLHandTracking.HandKeyPose.Ok)) { handPose = HandPoses.Ok; } else if (GetGesture(hand, MLHandTracking.HandKeyPose.Finger)) { handPose = HandPoses.Finger; } else if (GetGesture(hand, MLHandTracking.HandKeyPose.OpenHand)) { handPose = HandPoses.OpenHand; } else if (GetGesture(hand, MLHandTracking.HandKeyPose.Fist)) { handPose = HandPoses.Fist; } else if (GetGesture(hand, MLHandTracking.HandKeyPose.Thumb)) { handPose = HandPoses.Thumb; } else { handPose = HandPoses.NoPose; } } /// <summary> /// ジェスチャの取得. /// </summary> /// <param name="hand"></param> /// <param name="type"></param> /// <returns></returns> private bool GetGesture( MLHandTracking.Hand hand, MLHandTracking.HandKeyPose type) { if (hand == null) return false; return 0.9f < hand.HandKeyPoseConfidence && hand.KeyPose == type; } }アタッチしたら以下の画像のように各関節のオブジェクトをセット、配列の添え字が若い方が根元に来るように設定
各パラメータの説明
HandPose : ジェスチャのポーズ名( 今回はInspectorに表示しているだけです )
HandData : 各関節のオブジェクトを保持するクラス、HandCenterは利用していません( うまくトラッキングできなかったので外しました )
HandMaterial : 各関節オブジェクトの球をつなぐ線の描画用マテリアルです、今回はMagicLeapパッケージ内のUIBeamを利用しました
Color : 各関節オブジェクトの球をつなぐ線の色です、今回は左は赤、右は緑で設定しています
HandId : 手の左右を決定する識別子Unity の設定
この状態でTheLabでMagicLeapと接続してPlayModeに入るとハンドトラッキングされたオブジェクトの様子が確認できると思います
ただしmpkファイルとして出力する際は以下の設定を行わないと実機ではエラーが出てハンドトラッキング及びジェスチャの取得はできません
公式のチュートリアル通りにやれば設定の仕方まで説明されてたけど必要なメソッドとか確認したらすぐ実行したくなっちゃうのよねEdit > ProjectSettigs > MagicLeap > ManifestSettings の項目を開き
GestureConfig, GestureSubscribeにチェックを入れる ( 公式チュートリアルだと明示的にLowLatencyLightwearにもチェックを入れるように説明されているが現バージョンでは自動で入ってる? )これでmpkを出力して実機でテストするとハンドトラッキングが実装できているはずです
あとがき
これはTheLabからDLしてきたMagicLeapUnityPackageでのサンプルです、MagicLeapToolKitでのハンドトラッキングはまだ触ったことがないので後日記事にできればと思います