2022/04/29に開催のハンズオン資料
Nreal Light/Air開発⼊⾨
View Slide
⾃⼰紹介⽒名︓吉永崇(Takashi Yoshinaga)専⾨︓ARを⽤いた医療⽀援や運動計測Volumetric Video (3Dビデオ)コミュニティ︓ARコンテンツ作成勉強会 主催
ARコンテンツ作成勉強会の紹介p 2013年5⽉に勉強会をスタート。p ARコンテンツの作り⽅をハンズオン形式で学ぶp ⼈数は5~10名程度の少⼈数で実施p 参加条件はAR/VRに興味がある⼈(知識不要)p 各地で開催 (福岡、熊本、宮崎、⻑崎、⼤分、 ⿅児島、⼭⼝、広島、札幌、関東)
Twitterと勉強会ページで情報を発信しています@AR_Fukuoka Googleで「AR勉強会」で検索
#AR_Fukuoka#nrealハッシュタグ
今⽇の内容Nreal Light / Airのコントローラを使ったコンテンツ開発の基本⼿順を解説
今⽇のゴールhttps://youtu.be/uyxVuTEpdJw
演習⽤素材のダウンロードhttps://github.com/TakashiYoshinaga/AR-Fukuoka/raw/main/20220429_Nreal3DoF/Samples.zip
ハンズオン⼿順p Unityプロジェクトの作成p NRSDKの基礎p オブジェクトのクリック検知p オブジェクトのマニピュレーションp 何もないところでのクリック検知p uGUI操作
ハンズオン⼿順オブジェクト配置p Unityプロジェクトの作成p NRSDKの基礎p オブジェクトのクリック検知p オブジェクトのマニピュレーションp 何もないところでのクリック検知p uGUI操作
ハンズオン⼿順エミュレータ実機p Unityプロジェクトの作成p NRSDKの基礎p オブジェクトのクリック検知p オブジェクトのマニピュレーションp 何もないところでのクリック検知p uGUI操作
ハンズオン⼿順p Unityプロジェクトの作成p NRSDKの基礎p オブジェクトのクリック検知p オブジェクトのマニピュレーションp 何もないところでのクリック検知p uGUI操作リセットボタン
ハンズオンスタート
Unity Hub起動Unity Hub
プロジェクトの作成 (1/6)①Projects②NewProject
プロジェクトの作成 (2/6)EditorVersionを開く
プロジェクトの作成 (3/6)2020.3.xxf1この資料は2020.3.xを前提に解説をしています
プロジェクトの作成 (4/6)①3D②Project name③保存場所
プロジェクトの作成 (5/6)Create project
プロジェクトの作成 (6/6)Unity Editorが起動すればOK
表⽰オブジェクトのインポート (1/5)Assets
表⽰オブジェクトのインポート (2/5)①Import Package②Custom Package
表⽰オブジェクトのインポート (3/5)Scan.unitypackageSamplesフォルダ
表⽰オブジェクトのインポート (4/5)Import
表⽰オブジェクトのインポート (5/5)ScanフォルダができればOK
表⽰オブジェクトをシーンに追加 (1/4)BreadBreadをHierarchyにドラッグ&ドロップ
表⽰オブジェクトをシーンに追加 (2/4)⼩さすぎてBreadが⾒えない︖
表⽰オブジェクトをシーンに追加 (3/4)Breadをダブルクリック
表⽰オブジェクトをシーンに追加 (4/4)クローズアップされる
ハンズオン⼿順エミュレータ実機p Unityプロジェクトの作成p NRSDKの基礎p オブジェクトのクリック検知p オブジェクトのマニピュレーションp ヘッドトラッキングの活⽤p 何もないところでのクリック検知p uGUI操作
プラットフォームの設定 (1/4)①File②Build Settings
プラットフォームの設定 (2/4)①Androidを選択②Switch Platform
プラットフォームの設定 (3/4)Androidに切り替わる
プラットフォームの設定 (4/4)閉じる
NRSDKのインストール (1/4)①Assets②Import Package③Custom Package
NRSDKのインストール (2/4)NRSDKをダブルクリック
NRSDKのインストール (3/4)Import
NRSDKのインストール (4/4)NRSDKフォルダができればOK
Nreal⽤カメラの設定 (1/3)MainCameraを削除
Nreal⽤カメラの設定 (2/3)Assets→ NRSDK→ Prefabs
Nreal⽤カメラの設定 (3/3)NRCameraRigHierarchyにドラッグ&ドロップ
表⽰出⼒の確認Gameタブをドラッグして右に移動(次⾴参照)
表⽰出⼒の確認 (1/2)こんな感じ
表⽰出⼒の確認 (2/2)カメラとCGが同じ位置(0,0,0)にあるため何も⾒えない状態
オブジェクトの位置を調整 (1/4)①Breadをクリック②Positionを下記に変更0,-0.08,0.8
オブジェクトの位置を調整 (2/4)BreadがSceneからフレームアウトするかもBreadが視界内に表⽰
オブジェクトの位置を調整 (3/4)Breadをダブルクリック
オブジェクトの位置を調整 (4/4)視点が調整され、カメラとの位置関係も確認可能
Sceneの視点調整カメラが⼿前に来るように視点を変えると実⾏画⾯と⽐較しやすいかもしれない
現状を保存Ctrl/command + S
エミュレーターで動作確認 (1/4)Play
エミュレーターで動作確認 (2/4)[W][S]: 前後移動[A][D]: 左右移動[Space]+ドラッグ︓⾸振り
エミュレーターで動作確認 (3/4)再度Playをクリックして停⽌
エミュレーターで動作確認 (4/4)再⽣ボタンが⻘くなければOK
コントローラの追加 (1/2)Assets→ NRSDK→ Prefabs
コントローラの追加 (2/2)NRInputHierarchyにドラッグ&ドロップ
エミュレーターでのコントローラ使⽤ (1/5)Play
エミュレーターでのコントローラ使⽤ (2/5)コントローラ画⾯※実際はスマホ画⾯に表⽰されるコントローラオブジェクト
エミュレーターでのコントローラ使⽤ (3/5)①Gameタブ内をクリック②Shiftを押したままマウスカーソルを動かすとポインタも連動して動く
エミュレーターでのコントローラ使⽤ (4/5)①Shiftから指を離すとポインタは動かなくなる②コントローラ画⾯をクリックすると●が表⽰されクリックしたことがわかる
エミュレーターでのコントローラ使⽤ (5/5)停⽌
補⾜︓3DoFを前提に開発 (for Light)①NRCameraRig②NRHMD Pose TrackerのTrackingTypeを[3DoF]に変更※今回は不要
実機で動作確認 (1/7)NRSDK
実機で動作確認 (2/7)Project Tips
実機で動作確認 (3/7)Accept All
実機で動作確認 (4/7)Close Window
実機で動作確認 (5/7)スマホ/コンピュータユニットとPCを接続
実機で動作確認 (6/7)①File②Build And Run
実機で動作確認 (7/7)①インストーラー名②保存
動作確認
シーンを名前を付けて保存 (1/3)①File②Save As
シーンを名前を付けて保存 (2/3)①Scene名をSample␣0②保存
シーンを名前を付けて保存 (3/3)①Assets②Sample 0があることを確認
シーンの複製 (1/4)Sample 0を選択して Ctrl + D
シーンの複製 (2/4)Sample 1が⽣成される
シーンの複製 (3/4)Sample 1をダブルクリック
シーンの複製 (4/4)Sample 1に切り替わればOK
スクリプトの追加 (1/5)①Bread②Add Component
スクリプトの追加 (2/5)①検索エリアを空に②New Script
スクリプトの追加 (3/5)①スクリプト名︓TapAndRotate②Create and Add
スクリプトの追加 (4/5)TapAndRotateが追加されるAssetsフォルダにもTapAndRotate.csファイルができる
スクリプトの追加 (5/5)TapAndRotateをダブルクリック
オブジェクト回転のスクリプト記述public class TapAndRotate : MonoBehaviour{//回転速度(初期値: 20 deg/sec)public float speed = 20.0f;//初期化で使⽤(今回は使わない)void Start(){ }//毎フレーム更新void Update(){//Rotate(x軸,y軸,z軸,回転基準)transform.Rotate(0, speed * Time.deltaTime, 0, Space.World);}}
現状を保存ソースコードを保存Ctrl/command + S
動作確認Play
動作確認回転するここで速度を変更
動作確認停⽌
回転On/Offの条件を追加//回転速度(初期値: 20 deg/sec)public float speed = 20.0f;//回転のOn/Offbool rotate = false;//初期化で使⽤(今回は使わない)void Start(){ }//毎フレーム更新void Update(){if (rotate){transform.Rotate(0, speed * Time.deltaTime, 0, Space.World);}}trueになるまで回転しない
コントローラでオブジェクトをクリック (1/6)using UnityEngine;//NRSDKの読み込みusing NRKernal;//クリックなどのイベント検出using UnityEngine.EventSystems;public class TapAndRotate : MonoBehaviour{//回転速度(初期値: 20 deg/sec)public float speed = 20.0f;//回転のOn/Offbool rotate = false;void Start(){ }void Update(){if (rotate) {/*スペースの都合により省略*/}}}
コントローラでオブジェクトをクリック (2/6)using UnityEngine;using NRKernal;using UnityEngine.EventSystems;public class TapAndRotate : MonoBehaviour, IPointerClickHandler{//回転速度(初期値: 20 deg/sec)public float speed = 20.0f;//回転のOn/Offbool rotate = false;void Start(){ }void Update(){if (rotate) {/*スペースの都合により省略*/}}}クリック関連のインターフェース
コントローラでオブジェクトをクリック (3/6)IpointerClickHandlerにカーソルを合わせるヒントの中から「インターフェイスを実装」を選択
コントローラでオブジェクトをクリック (4/6)OnPointerClick関数が追加される
コントローラでオブジェクトをクリック (5/6)using UnityEngine;using NRKernal;using UnityEngine.EventSystems;public class TapAndRotate : MonoBehaviour, IPointerClickHandler{//回転速度(初期値: 20 deg/sec)public float speed = 20.0f;//回転のOn/Offbool rotate = false;//このスクリプトが貼られたオブジェクトクリックすると呼ばれる関数public void OnPointerClick(PointerEventData eventData){throw new System.NotImplementedException();}void Start(){ }void Update(){/*スペースの都合により省略*/}}OnPointerClick関数内のコードを削除
コントローラでオブジェクトをクリック (6/6)using UnityEngine;using NRKernal;using UnityEngine.EventSystems;public class TapAndRotate : MonoBehaviour, IPointerClickHandler{//回転速度(初期値: 20 deg/sec)public float speed = 20.0f;//回転のOn/Offbool rotate = false;//このスクリプトが貼られたオブジェクトクリックすると呼ばれる関数public void OnPointerClick(PointerEventData eventData){rotate = !rotate;}void Start(){ }void Update(){/*スペースの都合により省略*/}}回転⽤フラグのOn/Offを切り替え
動作確認Shift + ドラッグでポインタを移動してオブジェクトに合わせる
動作確認Shiftから⼀旦指を離す
動作確認コントローラ画⾯上でマウスクリック
動作確認いくらクリックしても回転しない︕
原因ポインターとの接触判定を⾏うコライダーがCG側に設定されていない※Cubeとかのサンプルでは初めからコライダーが設定されているので忘れがち
コライダーの設定 (1/4)Breadをクリック
コライダーの設定 (2/4)Add Component
コライダーの設定 (3/4)Boxで検索Box Collider
コライダーの設定 (4/4)Center: 0 0.05 0Size: 0.2 0.1 0.2
動作確認ポインターを合わせるコントローラ画⾯をクリック
動作確認今度は回転する※再クリックで停⽌
動作確認(動画)
現状を保存Unityのシーンの現状を保存Ctrl/command + S
プロジェクトの複製 (1/3)Sample 1を選択してCtrl/command + D
プロジェクトの複製 (2/3)Sample 2をダブルクリック
プロジェクトの複製 (3/3)Sample 2になっていればOK
不要なスクリプトの削除 (1/2)BreadTapAndRotate横の
不要なスクリプトの削除 (2/2)Remove Component
新規スクリプトの追加 (1/4)Add Component
新規スクリプトの追加 (2/4)検索エリアを空にNew script
新規スクリプトの追加 (3/4)Script名: ManipulationCreate and Add
新規スクリプトの追加 (4/4)Manipulationが追加されるManipulationをダブルクリック
コントローラ押下と解放の検知 (1/5)using UnityEngine;using NRKernal;using UnityEngine.EventSystems;public class Manipulation : MonoBehaviour,IPointerDownHandler, IPointerUpHandler{void Start(){}void Update(){}}コントローラ押下開始 コントローラ押下終了
コントローラ押下と解放の検知 (2/5)IpointerDownHandlerにカーソルを合わせるヒントの中からインターフェイスを実装を選択
コントローラ押下と解放の検知 (3/5)IpointerUpHandlerにカーソルを合わせるヒントの中からインターフェイスを実装を選択
コントローラ押下と解放の検知 (4/5)public class Manipulation : MonoBehaviour, IPointerDownHandler, IPointerUpHandler{public void OnPointerDown(PointerEventData eventData){throw new System.NotImplementedException();}public void OnPointerUp(PointerEventData eventData){throw new System.NotImplementedException();}void Start(){}void Update(){}}
コントローラ押下と解放の検知 (5/5)public class Manipulation : MonoBehaviour, IPointerDownHandler, IPointerUpHandler{public void OnPointerDown(PointerEventData eventData){Debug.Log("Down");}public void OnPointerUp(PointerEventData eventData){Debug.Log("Up");}void Start(){}void Update(){}}
動作確認Console
動作確認Playポインタをオブジェクトに合わせる
動作確認Shiftから⼿を離すコントローラをゆっくりクリック
動作確認ボタンを押下したタイミングと離したタイミングでそれぞれ⽂字が表⽰
不要なコードを削除public class Manipulation : MonoBehaviour, IPointerDownHandler, IPointerUpHandler{public void OnPointerDown(PointerEventData eventData){Debug.Log("Down");}public void OnPointerUp(PointerEventData eventData){Debug.Log("Up");}void Start(){}void Update(){}}
コントローラ押下でオブジェクト移動Breadの親要素なし(=3D空間)親要素を切り替えることでコントローラへの追従のOn/Offを切り替えるコントローラがBreadの親押下押下選択
コントローラ押下でオブジェクト移動 (1/5)//コントローラの位置・姿勢 (あとでUnityEditorから指定)public Transform controller;//デフォルトの(マニピュレーションしていない時の)親オブジェクトpublic Transform base_parent;public void OnPointerDown(PointerEventData eventData){//このオブジェクトの親をコントローラにする(コントローラに追従)transform.parent = controller;}public void OnPointerUp(PointerEventData eventData){//このオブジェクトをコントローラから解放するtransform.parent = base_parent;}void Start(){//最初にこのオブジェクトの親Transformを覚えさせておくbase_parent = transform.parent;}
コントローラ押下でオブジェクト移動 (2/5)スクリプトとUnityEditor上のコントローラを関連づけよう
コントローラ押下でオブジェクト移動 (3/5)NRInput→ Right→ ControllerTrack→ ModelAnchorを⾒つける
コントローラ押下でオブジェクト移動 (4/5)BreadをクリックManipulationに注⽬
コントローラ押下でオブジェクト移動 (5/5)ModelAncorControllerにドラッグ&ドロップ
動作確認オブジェクトにポインタを合わせる
動作確認①Shiftから指を離す②Shiftを押してコントローラ画⾯上をドラッグ
実機での動作の様⼦
オブジェクトの前後移動 (1/3)public Transform base_parent;//コントローラで選択されているかどうかbool selected = false;public void OnPointerDown(PointerEventData eventData){//ポインターを合わせてボタンを押したら選択selected = true;//このオブジェクトの親をコントローラにする(コントローラに追従)transform.parent = controller;}public void OnPointerUp(PointerEventData eventData){//指を離したら選択解除selected = false;//このオブジェクトをコントローラから解放するtransform.parent = base_parent;}
オブジェクトの前後移動 (2/3)//コントローラで選択されているかどうかbool selected = false;//移動速度public float moveSpeed = 0.08f;public void OnPointerDown(PointerEventData eventData){ 略 }public void OnPointerUp(PointerEventData eventData){ 略 }void Start(){ 略 }void Update(){if (!selected) return;//タッチ位置を取得し、yの絶対値>0.6なら前後移動Vector2 p = NRInput.GetTouch();if (Mathf.Abs(p.y) > 0.6f){transform.Translate(Mathf.Sign(p.y) * controller.forward* moveSpeed * Time.deltaTime);}}-11
オブジェクトの前後移動 (3/3)//コントローラで選択されているかどうかbool selected = false;//移動速度public float moveSpeed = 0.08f;public void OnPointerDown(PointerEventData eventData){ 略 }public void OnPointerUp(PointerEventData eventData){ 略 }void Start(){ 略 }void Update(){if (!selected) return;//タッチ位置(-1~1)を取得し、yの絶対値>0.6なら前後移動Vector2 p = NRInput.GetTouch();if (Mathf.Abs(p.y) > 0.6f){transform.Translate(Mathf.Sign(p.y) * controller.forward* moveSpeed * Time.deltaTime);}}
動作確認ポインターをオブジェクトに合わせるコントローラ画⾯の上か下周辺でクリック
動作確認ポインターの前後⽅向に移動
オブジェクトの左右回転//移動速度public float moveSpeed = 0.08f;//回転速度public float rotSpeed = -20.0f;/*中略*/void Update(){if (!selected) return;//タッチ位置(-1~1)を取得し、xyの絶対値が0.6以上なら移動/回転Vector2 p = NRInput.GetTouch();if (Mathf.Abs(p.y) > 0.6f){ /* 略(前後移動) */ }else if (Mathf.Abs(p.x) > 0.6f){transform.Rotate(0, Mathf.Sign(p.x) * rotSpeed *Time.deltaTime, 0, Space.Self );}}
動作確認ポインターをオブジェクトに合わせるコントローラ画⾯の右か左周辺をクリック
動作確認回転もできるようになる
ここまでの成果(エミュレーター)
ここまでの成果(実機)
プロジェクトの複製 (1/4)Sample 2をCtrl/command + D
プロジェクトの複製 (2/4)Sample 3が⽣成される
プロジェクトの複製 (3/4)Sample 3をダブルクリック
プロジェクトの複製 (4/4)Sample 3に切り替わればOK
不要なオブジェクトを削除Breadを削除
サンプルのインポート (1/4)Assets→ Import Package→ Custom Package
サンプルのインポート (2/4)ARShooter.unitypackage
サンプルのインポート (3/4)Import
サンプルのインポート (4/4)Shooterが追加されていればOK
壁を表⽰ (1/8)Hierarchyの何もないところを右クリック
壁を表⽰ (2/8)Crate Empty
壁を表⽰ (3/8)①GameObject②名前をWallに変更
壁を表⽰ (4/8)Positionを 0 -0.2 2※壁が表⽰される位置
壁を表⽰ (5/8)Shooter
壁を表⽰ (6/8)WallScriptに注⽬※クリックしないWallのInspectorにドラッグ&ドロップ
壁を表⽰ (7/8)WallScriptが追加される
壁を表⽰ (8/8)Cubeに注⽬(クリックしない)CubePrefabにドラッグ&ドロップ
動作確認壁が表⽰される
弾を発射するスクリプトを作成 (1/5)NRInput→ Right→ ControllerTrack→ ModelAnchorを⾒つける[補⾜]弾を発射したいオブジェクトに次⾴で作るスクリプトを貼り付ける。例としてコントローラを指定しているが、NRCameraRig内のCenterAnhorに貼れば頭から発射も可能
弾を発射するスクリプトを作成 (2/5)Add Component
弾を発射するスクリプトを作成 (3/5)検索エリアを空にNew script
弾を発射するスクリプトを作成 (4/5)Script名: ShootBulletCreate and Add
弾を発射するスクリプトを作成 (5/5)ShootBulletが追加される
発射スクリプトの記述 (1/6)①Assets②ShootBulletをダブルクリック
発射スクリプトの記述 (2/6)using UnityEngine;using NRKernal;public class ShootBullet : MonoBehaviour{//発射する弾の元データpublic GameObject bulletPrefab;//実際に空間に放たれる弾GameObject bullet;void Start(){}void Update(){}}
発射スクリプトの記述 (3/6)void Start(){NRInput.AddClickListener(//右⼿or左⼿の指定ControllerHandEnum.Right,//コントローラの押した箇所に対応するアクションを記述ControllerButton.TRIGGER, () => {//既に弾を発射している場合は⼀旦削除if (bullet != null) DestroyImmediate(bullet);//弾を作って発射位置にセットbullet = GameObject.Instantiate(bulletPrefab);bullet.transform.position = transform.position;//下記設定で⼒を加えるbullet.GetComponent().AddForce(transform.forward * 8, ForceMode.Impulse);});}
発射スクリプトの記述 (4/6)NRInput→ Right→ ControllerTrack→ ModelAnchor
発射スクリプトの記述 (5/6)Shooter
発射スクリプトの記述 (6/6)Bulletに注⽬(クリックしない)BulletPrefabにドラッグ&ドロップ
動作確認弾が発射される
リセットボタンを追加 (1/5)Shooter
リセットボタンを追加 (2/5)CanvasHierarchyにドラッグ&ドロップ
リセットボタンを追加 (3/5)CanvasCanvasのRenderModeがWorldSpaceとなっていることを確認
リセットボタンを追加 (4/5)Add Component
リセットボタンを追加 (5/5)Canvas RaycastCanvas Raycast Target
リセット機能を呼び出す (1/9)Canvasを開く
リセット機能を呼び出す (2/9)Buttonをクリック
リセット機能を呼び出す (3/9)Buttonを⾒つけるOn Clickを⾒つける
リセット機能を呼び出す (4/9)Wallを⾒つける(クリックしない)
リセット機能を呼び出す (5/9)OnClickにドラッグ&ドロップ
リセット機能を呼び出す (6/9)ドロップダウンメニューを開く
リセット機能を呼び出す (7/9)①WallScript②ResetWall
リセット機能を呼び出す (8/9)NRInput→ Right→ ControllerTracker→ LaserRaycaster
リセット機能を呼び出す (9/9)Mask Type: InclusiveMask: UI
参考• クイックスタートhttps://nreal.gitbook.io/nrsdk/discover/quickstart-for-android• コントローラ詳細https://nreal.gitbook.io/nrsdk/nrsdk-fundamentals/controller• 前回のNreal Lightハンズオンhttps://www.slideshare.net/ssuserc0d7fb/nreal-light