20200704のUnityに関する記事は11件です。

VCIが消失その1

結論

コライダーとgravityをチェックしよう

背景

VR配信・SNSサービスのバーチャルキャストでは、他のVRSNSに見られない仕組みとしてVCIというものがあります。VCIはバーチャル空間で取り扱えるアイテムであり、好きな場所で好きなタイミングで取り出すことができ、その挙動はスクリプトを用いて制御することでインタラクティブな効果を奏することができます。

そのVCIを制作するにあたって、制作の最初の段階で(特に初心者に多い)結構な割合で作ったはずのVCIを呼び出しても出てこない(見つけられない)事が起きます。

本記事ではこの現象に対する事例分けをし、それぞれの対処方法を紹介し、、、たいのですが、結構多岐にわたるので、そのうちの一つをご説明します。

作者のモチベーションが続けばもっと書きます

対象

all user(主に初心者)

症状

出したのに出てこない、もしくは
一瞬だけ出現するけど、すぐ消えてしまう

原因

出現した瞬間無限落下している

どういうことか

重力設定がオンになっているうえで、接触判定を入れていないと起きる。

unityでは、どういうわけか、これがデフォルトで「落ちるが地面を突き抜ける」状態になっているため、設定間違いが非常に起きやすい。(作者は未だによくやる)

解決策

下図の"use gravity"のチェックを外そう。
image.png
なんならその下のisKinematicのチェックも入れておくと良いと思います。最初のうちはどのプロパティがどういう意味があるのか理解できないと思うので、とりあえずGravityをオフisKinematicをオンにしておけば大体の用は済みます。

その上でコライダーを入れよう。バーチャルキャストでは使えるコライダーは"box collider"、"capsule collider"、"sphere collider"の三種類のみなので気をつけよう。

上記のどちらかのみ行っていれば消えることはないのですが、最初のうちは両方意識しましょう。

ちなみに

一旦落ちてしまうと、アイテムの更新を行うのは極めて困難です。素直に一旦タイトルに戻って読み直しましょう。
作者はフールプルーフとして、VCI作成時にsimple vciで作成設定して、最初から入ってるオブジェクトを消さずにおきます。こうすると更新用の(消失しない)アイテムが必ず手元に残るので安全です。

あと、他の事例でも言えることですが、床が透明だと気づきやすいです。落っこちていく様子が見えるので。基本的に僕は何かあったら、床のないところで原因を考えることが多いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

nreal developerページをざっと読んでみた (RGB Camera編)

nreal developerのページをざっと流し見して、日本語でも読みやすいように補助的な記事を書いていきます。この記事はRGB Camera編です。

https://developer.nreal.ai/develop/unity/rgb-camera
こちらと一緒に見ていきましょう!


他の記事
nreal developerページをざっと読んでみた (Discover編)
nreal developerページをざっと読んでみた (Quickstart編)
nreal developerページをざっと読んでみた (Image Tracking編)
nreal developerページをざっと読んでみた (Controller編)


目的

NRSDKを介してNreal LightグラスからRGBカメラデータを取得するサンプルアプリを作成する方法を学びます。

Open the Sample Scene (サンプルシーンを開く)

Assets > NRSDK >Demos > RGBCameraシーンを開きましょう。

Build and Run the Sample App(サンプルアプリをビルド)

ケーブルつないでBuild and Run。

Assets > NRSDK > Demos > CameraCapture > Scripts > CameraCaptureController.csを見るとRBGカメラによってTextureを取得する処理が書かれています。

public RawImage CaptureImage;

  private void Start()
  {
      RGBCamTexture = new NRRGBCamTexture();
      CaptureImage.texture = RGBCamTexture.GetTexture();
      RGBCamTexture.Play();
  }

アウトプット

RBGカメラで取得した画像をRawImageに代入。

image.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

nreal developerページをざっと読んでみた (Controller編)

前回はImageTracking編

今回はController編で
https://developer.nreal.ai/develop/unity/controller
を見ていきます!

Nreal Light controllerとNreal phone controller2つありますが、チュートリアルではNreal Light controllerの方を見ていくみたいです。

Introduction (導入)

Nreal Light controllerは、Bluetoothを介してNreal Lightコンピューティングユニットまたは携帯電話とペアリングできます。

Nreal phone controllerは、電話をNreal Lightのコンピューティングユニットとして使用する場合にのみ使用できます。

現在、開発者が利用できるのはNreal Light controller(Nreal Developer Kitで入手可能)だけです。Nreal phone controllerは将来使えるようになります。

How to Use the Nreal Light Controller(使い方)

image.png

特徴 説明
3DoF Tracking 3 degrees of freedom
Touchpad 押す = Tigger ボタン. タッチ、スワイプ、クリックを検知できる. カスタマイズ可能(スクロールなど)
App Button 通常押し=コントローラの再調整。長押し時の動作はカスタマイズ可能です(メニューを開くなど)
Home Button 長押し=Nreal Launcherを開きます。通常押し時の動作はカスタマイズ可能です
LED Light コントローラーのステータスを表します

特徴 説明
Charging pins 下向きの3本の充電ピンは、コントローラーをコンピューティングユニットに接続します。 また、計算ユニットからコントローラにデータを送信するためにも使用されます。
Power Switch Nreal Lightコントローラーのオン/オフスイッチは、充電スポットの下にあります。 緑は電源がオン、赤は電源がオフであることを意味します。

Nreal Light Controller LED Guide (LEDの意味)

コントローラーとユニットの接続とバッテリーを表しているようです。
緑であれば接続しているので、使用するときは確認しましょう。

Connecting the Nreal Light Controller(接続しよう)

  1. Neal LightコンピューティングユニットまたはAndroid携帯電話のBluetoothがオンになっていることを確認します。 (Nreal LightコンピューティングユニットのBluetoothはデフォルトでオンになっています。)

  2. Nreal Lightコントローラーの電源スイッチをオンにします。 緑色のライトが点滅し始めるまで、タッチパッドを押し続けます。

  3. Nreal Lightコントローラーをコンピューティングユニットに置くと、自動的にペアリングされます。

  4. ペアリングが成功すると、LEDの点滅が止まり、緑色に点灯します。 この手順を完了できない場合は、Bluetoothレコードリスト内の他のすべてのコントローラーを削除して、再試行してください。

Charging the Nreal Light Controller (充電しよう)

こんな感じでくっつければ充電されます。

How to Use Your Phone as a Controller(携帯をコントローラーにしよう)

各ボタンの機能はNreal Light Controllerと同じです!

Tutorial: Nreal Light Controller(やってみよう)

Input in Unity (これだけは覚えとこう)

  • ControllerProvider
    • 様々なコントローラーの情報(回転、ボタン押下、ジャイロなど)をNRInputに提供する
  • EventSystem
    • Unityの標準EventSystemとの統合し、ユーザインタラクションを可能にする。
      • UnityのEventSystemとは別みたい
    • NRIputは、アプリケーションの実行時に自動的にEventSystemを生成します。
  • Visualizations
    • コントローラー、Ray、レチクルのビジュアライズの機能はデフォルトであります。

Enabling NRInput (NRInputを使えるようにする)

ここからは https://developer.nreal.ai/develop/unity/controller もちゃんと見つつ進めよう。

  1. NRInputプレハブをシーンに追加
  2. NRInputのインスペクターのOverride Camera Centerの項目に、Center Cameraオブジェクトをドラッグ&ドロップする
  3. これでNreal Light controllersを使ったインタラクションをユーザが体験することができるようになりました。

Raycast Modes in NRInput (Raycastモードが2つあるよ)

RaycastingにはGaze(視線)モードRayモードの2つある。

  • Gaze(視線)モード
    • レイキャストはユーザーの額の中心から始まり、前方に放射します。 したがって、光線は見えなくなります。
  • Rayモード
    • 光線はコントローラーの中心から始まります。

Current Controller Features (現在のコントローラの特徴取得)

NRSDKは複数のタイプのコントローラーをサポートするため、現在のコントローラーがサポートする機能を理解するのに役立つメソッドがスクリプト化されています。

サンプル

    bool supportsPosition = NRInput.GetControllerAvailableFeature(ControllerAvailableFeature.CONTROLLER_AVAILABLE_FEATURE_POSITION);
    bool supportsGyro = NRInput.GetControllerAvailableFeature(ControllerAvailableFeature.CONTROLLER_AVAILABLE_FEATURE_GYRO);

Nreal Light controllerとNreal phone controllerどちらを使っているか気にせず特徴を取得しています。

Some Common Usage of NRInput(一般的な使い方)

void Update()
    {
        //returns a int value of how many available controllers currently
        NRInput.GetAvailableControllersCount();

        //returns true if a Trigger button is currently pressed
        NRInput.GetButton(ControllerButton.TRIGGER);

        //returns true if a Trigger button was pressed down this frame
        NRInput.GetButtonDown(ControllerButton.TRIGGER);

        //returns true if a Trigger button was released this frame
        NRInput.GetButtonUp(ControllerButton.TRIGGER);

        //returns Vector3.zero if controller is 3DoF, otherwise returns the position of the domain controller
        NRInput.GetPosition();

        //returns the rotation of the domain controller
        NRInput.GetRotation();

        //returns the ControllerType of the domain controller
        ControllerType controllerType = NRInput.GetControllerType();

        //returns a Vector2 value of the touchpad, x(-1f ~ 1f), y(-1f ~ 1f);
        NRInput.GetTouch();

        //returns a Vector3 value of gyro if supports
        NRInput.GetGyro();

        //to get what hand mode is using now, left-hand or right-hand
        ControllerHandEnum domainHand = NRInput.DomainHand;

        //the same as NRInput.GetRotation()
        NRInput.GetRotation(NRInput.DomainHand);

        //returns the rotaion of the left hand controller
        NRInput.GetRotation(ControllerHandEnum.Right);
    }

Get Frequently Used Anchors (アンカーの取得)

サンプル

    Transform gazeAnchor = NRInput.AnchorsHelper.GetAnchor(ControllerAnchorEnum.GazePoseTrackerAnchor);
    Transform leftRayAnchor = NRInput.AnchorsHelper.GetAnchor(ControllerAnchorEnum.LeftRayAnchor);

Raycasters in NRInput (レイキャスト)

  • 3つのRaycastがNRInputプレハブに含まれています。
    • 1つがGazeモード用で、2つがRayモード用です。
  • raycasterクラスはUnityのBaseRaycasterクラスを継承しています
  • 選択したレイキャスターの最も遠いレイキャスティング距離は、インスペクターから直接変更できます
    • そのマスクのパラメーターを変更することで、どのオブジェクトを操作可能にするかをLayerで定義することもできます。

Building a Project with User Input (インプットを用いたプロジェクトをビルドしよう)

  • 目標
    • コントローラーを立方体に向けると、その色が緑に変わります。
    • トリガーボタンを押すと、キューブの色がランダムに変化します。
    • 立方体はコントローラーと共に回転します。
    • UGUIボタンを押して、回転のために立方体をアクティブまたは非アクティブにします。

最終アウトプット

image.png

コントローラからのRayでCubeを選択したり、ボタンを押すことができます。

注意点

基本Building a Project with User Inputの通り進めてください。
しかし、一つ罠があります。

途中で登場するCubeInteractiveTest.csは、usingを正しく書かないとエラーが出ます。次のように書きましょう。

CubeInteractiveTest.cs
using UnityEngine;
using UnityEngine.EventSystems; //忘れない
using NRKernal; //忘れない

public class CubeInteractiveTest : MonoBehaviour, IPointerClickHandler, IPointerEnterHandler, IPointerExitHandler
{
    private MeshRenderer m_MeshRender;
    void Awake () {
        m_MeshRender = GetComponent<MeshRenderer>();
    }

    void Update()
    {
        //get controller rotation, and set the value to the cube transform
        transform.rotation = NRInput.GetRotation();
    }

//when pointer click, set the cube color to random color
    public void OnPointerClick(PointerEventData eventData)
    {
        m_MeshRender.material.color = new Color(Random.Range(0f, 1f), Random.Range(0f, 1f), Random.Range(0f, 1f));
    }

//when pointer hover, set the cube color to green
    public void OnPointerEnter(PointerEventData eventData)
    {
        m_MeshRender.material.color = Color.green;
    }

//when pointer exit hover, set the cube color to white
    public void OnPointerExit(PointerEventData eventData)
    {
        m_MeshRender.material.color = Color.white;
    }
}

Next Step

次はDesign Guide編です!見てね

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】1分21秒でできるボタンクリックで音を鳴らすにはAudioSourceでやるべし!

ボタンおしたら音がなる方法

1.ボタンを追加
2.AudioSourceを追加
3.スクリプトかく
4.OnClickをいじる

注意点

書いた日:2020年7月4日
unityのバージョン:2019.3.14

完成イメージ

左上のラッパのボタンを押すとクラクションがなります。
(ARのアプリを作っているので背景が緑ですが気にしないでください。)
スクリーンショット 2020-07-04 18.07.38.png

やり方

(1)ボタンを追加します
【UI > Button】
私の場合はボタンをいくつか配置しているのでCunvasの小要素にボタンを作ります。
スクリーンショット 2020-07-04 18.12.01.png
テキストがいらないのでデリートします。
スクリーンショット 2020-07-04 18.22.27.png
ボタンのかたちを整えます
写真右側のインスペクターのスケールで変更します
スクリーンショット 2020-07-04 18.25.16.png
位置をととのえます
左上の前後左右の矢印っぽいところをおすとシーンビューから簡単に移動することができます。
スクリーンショット 2020-07-04 18.26.34.png
写真をセットします。
Assets直下にimagesファイルを作成し、好きな画像を保存します。
スクリーンショット 2020-07-04 18.27.45.png
Materialをつくります。
Assets直下にMaterialファイルをつくります。
MaterialファイルにMaterialを新規に作成します。
スクリーンショット 2020-07-04 18.29.15.png
Materialに写真を追加していきます
インスペクターのShaderをLegacy Shaders > Diffuseにする。
スクリーンショット 2020-07-04 18.33.13.png
写真を追加します。右側のselectをおします。
スクリーンショット 2020-07-04 18.36.51.png
任意の写真を選択します。
スクリーンショット 2020-07-04 18.37.38.png
写真を選択したら、Shaderを再度変更します。
UI > Defaultに変更してください
スクリーンショット 2020-07-04 18.39.36.png
ボタンに移動してください。
スクリーンショット 2020-07-04 18.40.50.png
右側インスペクターImageを変更します。
Source Imageをnoneに
Materialに先ほど作成したMaterialを設定してください。
設定した画像はこちらです。
先ほどまで四角の白いボタンがhornのボタンに変わりました。
スクリーンショット 2020-07-04 18.42.29.png

Audio Sourceを追加します

ButtonにAudioSourceを追加します。
スクリーンショット 2020-07-04 18.43.51.png
Assets直下にSoundsファイルを作り音声を追加します。
スクリーンショット 2020-07-04 18.50.52.png
AudioClipに音声を追加します。
私の場合はクラクションです。
スクリーンショット 2020-07-04 18.52.00.png
Play On Awake を無効にします。(写真は有効になっています)
有効な状態だと再生ボタンを押した瞬間になります。
ループしたい人はLoopを有効にしてください。

スクリプトを書きます

Assets直下にScriptsファイル
その中になんでもいいのでスクリプトをつくってください。
私の場合はHornSoundsです。
スクリーンショット 2020-07-04 18.54.34.png
以下のスクリプトをコピペして書き換えてください。

HornSounds.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class HornSounds : MonoBehaviour
{
    //hornsoundは任意の名前でOKです、それ以外は変えないでください。
    private AudioSource hornsound;

    void Start()
    {
        hornsound = GetComponent<AudioSource>();
    }
    //ボタンをクリックした時のスクリプトです。
    public void OnClick()
    {
        hornsound.PlayOneShot(hornsound.clip);
    }

}

こんなかんじです
スクリーンショット 2020-07-04 19.04.35.png

Buttonにアタッチします。
スクリーンショット 2020-07-04 19.06.22.png

ButtonのOn Clickをいじる

On ClickにButtonオブジェクトを追加します。
スクリーンショット 2020-07-04 19.08.41.png
HornSounds > OnClickにしてください。
スクリーンショット 2020-07-04 19.14.26.png
完成です!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

nreal developerページをざっと読んでみた (Image Tracking編)

前回はQuickstart編

今回はImage Tracking編で
https://developer.nreal.ai/develop/unity/image-tracking
を見ていきます!

Introduction (導入)

画像追跡機能には画像やロゴなどを使用できる。
順序としては

  1. Unityの画像データベースに登録
  2. アプリをビルド
  3. 画像を印刷
  4. Nrealで画像を見る

の順番となりそうです。

Capabilities (何できる?)

  • 所定の位置に固定された画像の検出と追跡
  • 動いている画像の検出と追跡
  • 追跡開始後、画像の位置、回転、スケールの推定を継続的に行う
  • 1つの環境内で10個の画像を記憶できる。 しかし、現在のバージョンでは、これらの画像を同時に追跡することはできない。したがって、各フレームで追跡できる画像は1つだけです。さらに、同じ画像の複数インスタンスも追跡しません。

複数同時画像トラッキングに関して、今はできないようです。

Requirements (これはやってね)

Image Selection Checklist(画像選択のチェックリスト)

  • 最低限
    • jpeg形式
    • グレースケール or RGBカラー
    • dpi値が150以上
    • 印刷された画像は1m×1m未満
  • より良い推定のために
    • 特徴点が分散している
      • 真っ白or真っ黒な画像はやめよう
    • 自己類似度が低い画像
      • 同じ模様の繰り返しのような画像は避けよう
    • NRSDKは追跡品質を評価できる
      • 一定以上の追跡頻出でなければならない
  • オリジナルの画像をデザインする場合
    • Adobe PhotoshopではなくAdobe Illustratorでエクスポートするほうがおすすめ

Image Tracking Condition Requirement (画像追跡する時のお願い)

  • 光沢な印刷紙より、光の反射が少ない印刷紙が良い
  • 画像追跡を初める良い方法は、画像を少し角度を付けて表示しながら、画像を平坦で歪みのない状態に保つことが良い。

良い画像、悪い画像の例
image.pnghttps://developer.nreal.ai/develop/unity/image-tracking

Image Tracking Tutorial (やってみよう)

nreal developerページをざっと読んでみた (Quickstart編)
でQuickStartしている前提で進めます。

Build and Run the Sample App (早速試そうぜ)

ビルドする前に、Build SettingでAssets > NRSDK > Demos > ImageTrackingのシーンが選択されていることを確認してください。

ケーブルを繋いでBuild and Run。

すると、指定の画像を見るとトラッキングするようになります。
指定の画像はAssets>NRSDK>Demos>TrackingImage>Imagesにあります。

貼り付けた画像_2020_07_04_17_36.png

Create the Database File (画像データベースを作ろう)

Unity内で画像を選択して右クリックして、Create/NRSDK/TrackingImageDatabaseを選択すると新しく画像データベースが生成される。

ポイントは
- Quality Scoreが65以上が好ましい
- Width(m)は実際の画像のサイズを等しくする必要がある

Set the Database File (データベースファイルをセットしよう)

プロジェクトウィンドウからAssets/NRSDK/にてNRKernalSessionConfigを開く。
そして、Tracking Image Database部分を入れ替える。

image.png

Get Tracked Image (追跡された画像を取得)

Assets > NRSDK > Demos > TrackingImage > Scripts > MarkerDetecter.csを見て、

NRSession.GetTrackables<NRTrackableImage>(m_NewMarkers, TrackableQueryFilter.New);

これが取得している例だよ。

m_NewMarkersにトラッキングした画像情報が入るみたいです。

Next Step

次はNreal Controller編、見てね!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Unityのリベンジ (1) 気になる本

Unityのリベンジ!
図書館で「Unityでつくる建築VR入門」を借りました。
半分はUnityの使い方、初心者向け。
後半からプラグイン[SteamVR]をインポート。VRですよ。
まだペラっと本をめくった状態ですが、出来そう。
リベンジなので最初からやってみます。
AutoCADで土木、建築系、たまに歯車、軸などの
CADオペレーターをしていました。
Unityはスキルアップになるかなと。
CADオペの面接でVRを見せつけようと考えているのですよ。
Unity_建築VR.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

nreal developerページをざっと読んでみた (Quickstart編)

nreal developerページをざっと読んでみた (Discover編)も見てね。

今回は https://developer.nreal.ai/develop/unity/android-quickstart
を読んでいきます!

Quickstart for Android (さあはじめよう)

まずサンプルシーンをNrealにビルドしてみようという感じですね。

Getting Started

Hardware Checklist (ハードウェアチェックリスト)

  • A Nreal Computing Unit (Android端末)
  • A pair of Nreal Light glasses (ARメガネ)
  • USB-C cable (NrealとPCをつなぐUSB-Cのケーブル)

Nreal Computing Unitはこれ
image.png

Software Checklist (ソフトウェアチェックリスト)

  • Unity 2018.2.X or higher with Android Build Support
  • Download NRSDKForUnity_1.2.1
    • NRSDKForUnity_1.2.1.unitypackage
  • Android SDK 8.0 (API Level 26) or later
    • installed using the SDK Manager in Android Studio

Unity,Android Studio,unitypackageを準備しましょう。バージョンを間違えないように。
(バージョンについては最新の公式ページを常に確認するようにしましょう。この記事が古くなる可能性があります)

Creating a Unity Project (プロジェクト作ろう)

  • Unityプロジェクトを新しく作る
  • Player Settings > Other Settings > Scritping Runtime Version to .net 4.x Equivalentに変更
  • NRSDKForUnity_1.2.1.unitypackageをimport

Hello MR - Your First Sample App (サンプルシーンでApp作ろう)

Assets > NRSDK > Demos > HelloMRをつくるよ。

Configure Build Settings(ビルドセッティングの設定)

  • File > Build Settingsを開く
  • Switch PlatformでAndroidに変更
  • Player Settingsを開き以下のように変更
Setting Value
Player Settings > Resolution and Presentation > Default Orientation Portrait
Player Settings > Other Settings > Auto Graphics API false
Player Settings > Other Settings > Graphics APIs OpenGLES3
Player Settings > Other Settings > Package Name オリジナルの名前にしよう 例えば, com.nreal.helloMR
Player Settings > Other Settings > Minimum API Level Android 8.0 or higher
Player Settings > Other Settings > Target API Level Android 8.0 or higher
Player Settings > Other Settings > Write Permission External(SDCard)
Player Settings > Other Settings > Allow 'unsafe' code true
Project Settings > Quality > V Sync Count Don't Sync

Connect to Nreal Device

PCとNrealをUSB-Cケーブルでつなごう。

Build and Run

Nrealへapkファイルをビルドするパターンは2種類あります。

  • UnityのBuild後、PCとNrealをケーブルとつなぎ、adb install -r {apkName}.apkを実行する
    • adbコマンドについては調べてみましょう
  • PCとNrealをケーブルでつなぎ、UnityでBuild and Runを行う

また、ケーブル経由のビルドと、Wifi経由のビルドがあります。Wifi経由のビルドの方法を書きます。

Wifi経由の接続の仕方

こちらを参考に
https://developer.android.com/studio/command-line/adb

  • PCとNrealの両方がアクセス可能なWi-Fi ネットワークに接続します。
  • PCとNrealをケーブルで接続
  • ポート 5555でTCP/IP接続をリッスンするようにターゲットデバイスを設定します。
    • adb tcpip 5555
  • PCとNrealをつないでいるケーブルを離します。
  • NrealのIPアドレスを確認します
  • 確認した IP アドレスを使用して、デバイスに接続します。
    • adb connect 192.168.xxx.yyyy:5555
  • 接続されたか確認する
    • adb devicesと打ってList of devices attached 192.168.1.50:5555 deviceと出れば成功

ビルド成功!

SucessかSucessfulみたいな文字列を観測できたら成功です!
nrealを起動して確認してみましょう。

気になったポイント

If it is the first time you run this app, you need to authrize the app by some tools like scrcpy.

最初にビルドした場合、アプリの承認が必要なようです。(私はすでに承認済みだったため必要ありませんでした)

(Optional) Use Android Logcat to view logged messages. We recommend using WiFi Android Debug Bridge (adb) to connect to your PC so that you do not have to be connected through the data cable most of the time.

Androidのログ表示機能が使えるみたいです。
https://developer.android.com/studio/command-line/logcat?hl=ja

Next Step

次回はImage Tracking編

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

nreal developerページをざっと読んでみた (Discover編)

nreal developerのページをざっと流し見して、日本語でも読みやすいように補助的な記事を書いていきます。この記事はDiscover編です。


nreal developerページをざっと読んでみた (Discover編)
nreal developerページをざっと読んでみた (Quickstart編)
nreal developerページをざっと読んでみた (Image Tracking編)
nreal developerページをざっと読んでみた (Controller編)


image.png

https://developer.nreal.ai/develop/discover/introduction-nrsdk

構成

  • Discover
    • Introducing NRSDK
      • Spatial Computing
      • Optimized Rendering
      • Multi-modal Interactions
      • Developer Tools
      • Third-party Integration
    • Nreal Developer Kit
    • Core Features
      • Spatial Computing
        • 6DoF Tracking
        • Plane Detection
        • Image Tracking
      • Optimized Rendering
        • Warping
      • Multi-modal Integrations
        • Nreal Light Controller (3DoF)
        • Nreal Phone Controller (3Dof)
      • Developer Tools
        • Testing Tool (Emulator)
        • Observer View
      • Third-party Integration
  • Develop
    • Quickstart for Android
    • Image Tracking
    • Controller
    • Design Guide
    • Observer View
    • RGB Camera
    • Emulator: Testing your app
    • Video Capture
    • Customize Phone Controller

気になる言葉

普段聞き慣れない言葉について調べてみました (6DoF Trackingなどは自分で調べてみてね)

Multi-modal Interactions

複数の入力モードを選択でき、インタラクションできるという意味のようです。
Nrealがカメラからの情報やハンドトラッキングだけでなく、コントローラでの入力も選択できる(しかも、Nreal Light ControllerとNreal Phone Controllerを選択できる)ということなのだと予想しています。

参考: http://www.jp.honda-ri.com/research_areas/ci/intelligence04/index.html

Third-party Integration

NRSDK is designed as an open platform and can be integrated with third-party SDKs as plug-ins. For example, data from the RGB camera is available for third-party SDKs to run face detection algorithms.
https://developer.nreal.ai/develop/discover/core-features

NRSDKはオープンなプラットフォームとして設計されるので、他のサードパーティ製のSDKとも結合可能ですよという。例えば、RBGカメラでのデータを顔検出アルゴリズムSDKなどと組み合わせることができるということですね。

NRSDKから取得できる値で面白いことしましょう!というのを感じます。

Warping

日本語に訳すと「歪み」です。

Instead of polling tracking data at the very beginning of each frame to render the image, NRSDK uses the last predicted position of the Nreal glasses to warp the rendered image, reproject, and send to display before every VSync.
https://developer.nreal.ai/develop/discover/core-features

Google翻訳すると

各フレームの最初に追跡データをポーリングして画像をレンダリングする代わりに、NRSDKはNrealメガネの最後に予測された位置を使用して、レンダリングされた画像をワープし、再投影し、すべてのVSyncの前に表示に送信します。

私はわかりませんでした...予想するに、トラッキングなど頑張ってレンダリングが遅くなると酔いが発生するのですが、それが起きないように、うまく色んなデータを使ってレンダリングの遅延が発生しないような工夫をしているようです。

Observer View

NRSDK's observer view allows users to share their mixed reality experience with others who are holding a phone or tablet running ARCore. These shared experiences can be streamed or recorded.
https://developer.nreal.ai/develop/unity/observer-view

簡単に言うと、Nrealでの体験をARCore機能を持つタブレットへ共有できるようです。
ARメガネだと体験者以外何が起きているのだろう、という現象がおきるのでこれは嬉しいですね!

Next Step

次回はQuickstart編!見てね

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

nreal developerページをざっと読んでみた (概要編)

nreal developerのページをざっと流し見しました。

image.png

https://developer.nreal.ai/develop/discover/introduction-nrsdk

構成

  • Discover
    • Introducing NRSDK
      • Spatial Computing
      • Optimized Rendering
      • Multi-modal Interactions
      • Developer Tools
      • Third-party Integration
    • Nreal Developer Kit
    • Core Features
      • Spatial Computing
        • 6DoF Tracking
        • Plane Detection
        • Image Tracking
      • Optimized Rendering
        • Warping
      • Multi-modal Integrations
        • Nreal Light Controller (3DoF)
        • Nreal Phone Controller (3Dof)
      • Developer Tools
        • Testing Tool (Emulator)
        • Observer View
      • Third-party Integration
  • Develop
    • Quickstart for Android
    • Image Tracking
    • Controller
    • Design Guide
    • Observer View
    • RGB Camera
    • Emulator: Testing your app
    • Video Capture
    • Customize Phone Controller

気になる言葉

普段聞き慣れない言葉について調べてみました (6DoF Trackingなどは自分で調べてみてね)

Multi-modal Interactions

複数の入力モードを選択でき、インタラクションできるという意味のようです。
Nrealがカメラからの情報やハンドトラッキングだけでなく、コントローラでの入力も選択できる(しかも、Nreal Light ControllerとNreal Phone Controllerを選択できる)ということなのだと予想しています。

参考: http://www.jp.honda-ri.com/research_areas/ci/intelligence04/index.html

Third-party Integration

NRSDK is designed as an open platform and can be integrated with third-party SDKs as plug-ins. For example, data from the RGB camera is available for third-party SDKs to run face detection algorithms.
https://developer.nreal.ai/develop/discover/core-features

NRSDKはオープンなプラットフォームとして設計されるので、他のサードパーティ製のSDKとも結合可能ですよという。例えば、RBGカメラでのデータを顔検出アルゴリズムSDKなどと組み合わせることができるということですね。

NRSDKから取得できる値で面白いことしましょう!というのを感じます。

Warping

日本語に訳すと「歪み」です。

Instead of polling tracking data at the very beginning of each frame to render the image, NRSDK uses the last predicted position of the Nreal glasses to warp the rendered image, reproject, and send to display before every VSync.
https://developer.nreal.ai/develop/discover/core-features

Google翻訳すると

各フレームの最初に追跡データをポーリングして画像をレンダリングする代わりに、NRSDKはNrealメガネの最後に予測された位置を使用して、レンダリングされた画像をワープし、再投影し、すべてのVSyncの前に表示に送信します。

私はわかりませんでした...予想するに、トラッキングなど頑張ってレンダリングが遅くなると酔いが発生するのですが、それが起きないように、うまく色んなデータを使ってレンダリングの遅延が発生しないような工夫をしているようです。

Observer View

NRSDK's observer view allows users to share their mixed reality experience with others who are holding a phone or tablet running ARCore. These shared experiences can be streamed or recorded.
https://developer.nreal.ai/develop/unity/observer-view

簡単に言うと、Nrealでの体験をARCore機能を持つタブレットへ共有できるようです。
ARメガネだと体験者以外何が起きているのだろう、という現象がおきるのでこれは嬉しいですね!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RhinoInside と Unity を使ったゲーム作り

はじめに

これは2020/07/04に行われたTokyo AEC Industry Dev Group でのハンズオンをもとにした資料になります。
RhinoInside と Unity を使ったボールをゴールへ運ぶゲームのつくり方になります。
参考のデータはこちらのGitHubのリポジトリにあります。RhinoInsideGame

そもそもRhinoInsideとは?という方はこちらを参照してください。
Rhinocerosという3DCADソフトの機能をほかのソフトで利用する仕組みになります。

完成品のイメージ

実際に作っていきましょう!!

0. 環境構築

  • RhinoInsideのリポをクローンしておく(いくつかのファイルを使う)
    • ここからクローンorダウンロード
  • RhinoWIP
    • ここからダウンロード
  • Unity2019.4.1.f1

    • ここからダウンロード
  • Rider2020.1 (スクリプトエディタ)

    • コードを書くエディタです。VisualStudioやVSCodeなどでもよいです。
    • Unity関連のデータをダウンロードをしておいてください。
    • Riderの場合
      • 特に追加でダウンロードするものはないです
    • Visual Studioの場合
      • Visual Studio Installerから以下のUnityに関するものをインストール
    • VS Codeの場合
      • ExtensionsからDebugger for Unity をインストール


    + エディタの設定はUnityの以下から設定

1. UnityでRhinoを使う

1.1 RhinoInsideを起動できるようにする

  • Asset下にScriptsという名前のフォルダを作成してそこに"Convert.cs"を入れる。"LoftSurface.cs"を作る
  • Asset下にPluginsという名前のフォルダを作成してそこに"RhinoCommon.dll"を入れる。
    • Convert.cs、RhinoCommon.dllはクローンしたリポの中に入っています。
  • Asset下にEditorというフォルダを作成して、"RhinoInsideUI.cs"を作る
    • Editorという名前のフォルダ名は特殊な扱いを受けるのでフォルダ名は間違えないで入れてください。
  • 以下を書いて、エディタからRhinoを起動してみる
using System;
using System.IO;

using UnityEngine;
using UnityEditor;

using Rhino.Runtime.InProcess;

[ExecuteInEditMode]
public class RhinoInsideUI : MonoBehaviour
{
    [MenuItem("Rhino/Start RhinoInside")]
    public static void StartRhinoInside()
    {
        string rhinoSystemDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Rhino WIP", "System");
        var path = Environment.GetEnvironmentVariable("PATH");
        Environment.SetEnvironmentVariable("PATH", path + ";" + rhinoSystemDir);
        GC.SuppressFinalize(new RhinoCore(new string[] { "/scheme=Unity", "/nosplash" }, WindowStyle.Minimized));
    }
}

1.2 RhinoでSurfaceを作る

  • ロフトサーフェスを作る
    • まずはRhino内で作ってみる。

1.3 RhinoInside でSurfaceを作る

  • 次にRhinoInside を使って作ってみる。
    • コントロールポイントをまず作る
public class RhinoInsideUI : MonoBehaviour
{
    public static void StartRhinoInside()
    {
        // 省略
    }

    [MenuItem("Rhino/Create Loft Surface")]
    public static void Create()
    {
        var surface = new GameObject("Loft Surface");
        surface.AddComponent<LoftSurface>();
        CreateLoft(surface);
    }

    private static void CreateLoft(GameObject surface)
    {
        surface.AddComponent<MeshFilter>();

        // Surfaceの色の設定
        var material = new Material(Shader.Find("Standard"))
        {
            color = new Color(1.0f, 0.0f, 0.0f, 1.0f)
        };
        surface.AddComponent<MeshRenderer>().material = material;
        // 影を落とさないようにする
        surface.GetComponent<MeshRenderer>().receiveShadows = false;

        // コントロールポイントの色の設定
        var cpMaterial = new Material(Shader.Find("Standard"))
        {
            color = new Color(0.2f, 0.2f, 0.8f, 1f)
        };

        // コントロールポイントの作成
        for (int i = 0; i < 4; i++)
        {
            for (int j = 0; j < 4; j++)
            {
                int num = 4 * i + j;
                var controlSphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
                controlSphere.name = "Sphere" + num;
                controlSphere.transform.parent = surface.transform;
                controlSphere.transform.localScale = new Vector3(0.5f, 0.5f, 0.5f);
                controlSphere.transform.position = new Vector3( i * 5f, 0, j * 5f);
                controlSphere.GetComponent<MeshRenderer>().material = cpMaterial;
            }
        }
    }
}
  • 作ったコントロールポイントを使ってロフトサーフェスを作る
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Rhino.Geometry;

#if UNITY_EDITOR
using UnityEditor;
#endif

#if UNITY_EDITOR
[ExecuteInEditMode]
#endif
public class LoftSurface : MonoBehaviour
{
    void Update()
    {
        var controlPoints  = new List<List<Vector3>>();

        int i = 0;
        List<Vector3> controlPointsRow = null;
        foreach (UnityEngine.Transform controlSphere in transform)
        {
            if ((i++ % 4) == 0)
            {
                controlPointsRow = new List<Vector3>(4);
                controlPoints.Add(controlPointsRow);
            }
            controlPointsRow.Add(controlSphere.position);
        }
        gameObject.GetComponent<MeshFilter>().mesh = CreateLoft(controlPoints);
    }

    private UnityEngine.Mesh CreateLoft(List<List<Vector3>> controlPoints)
    {
        if (controlPoints.Count > 0 )
        {
            var profileCurves = new List<Curve>();
            foreach (var controlPointsRow in controlPoints)
            {
                profileCurves.Add(Curve.CreateInterpolatedCurve(controlPointsRow.ToRhino(), 3));
            }
            Brep brep = Brep.CreateFromLoft(profileCurves, Point3d.Unset,Point3d.Unset, LoftType.Normal, false)[0];
            Rhino.Geometry.Mesh mesh = Rhino.Geometry.Mesh.CreateFromBrep(brep, MeshingParameters.Default)[0];
            return mesh.ToHost();
        }
        return null;
    }
}

これで"Rhino/Start RhinoInside" をした後に、"Rhino/Create Loft Surface"を押すとロフトサーフェスが作成されるはずです。

ここまでの内容は、part1 のフォルダのデータになっています。

1.4 Unityのデバッグの仕方

  • Unityにエディタをアタッチすることでデバッグできます

これで RhinoInside は終わり。あとはUnityのみになります。


2. ゲーム化する

2.1 ボールを弾ませる

  1. Ballを作成する
  2. play▶してみる
    • 何も行らない…
  3. RigidBodyをアタッチする
    • 重力で落ちていくが貫通する…
  4. LoftSurfaceにMeshColliderをアタッチする
    • Ballが弾まない…
  5. Materialsフォルダを作成してそこにPhysicMaterialを作成する。
    • Bouncesを任意の値にして、BallとLoftSurfaceにアタッチする。
  6. ボールが弾む!!
  7. コントロールポイントを動かしてみる
    • コライダーが反映されない…
  8. 動的にMeshColliderをアタッチできるようにする。
    • LoftSurface.csに以下を追記
    • アタッチされているゲームオブジェクトにMeshColliderがあれば削除し、新しいMeshColliderを設定する SetMeshCollider メソッドを追加している
public class LoftSurface : MonoBehaviour
{
    void Start()
    {
        SetMeshCollider(gameObject);
    }

    private void SetMeshCollider(GameObject obj)
    {
        if (obj.GetComponent<MeshCollider>() != null)
        {
            DestroyImmediate(gameObject.GetComponent<MeshCollider>());
        }
        obj.AddComponent<MeshCollider>();
        obj.GetComponent<MeshCollider>().material = new PhysicMaterial("SurfMaterial")
        {
            bounciness = (float) 1.0
        };
    }

    void Update()
    {
        SetMeshCollider(gameObject);
        var controlPoints  = new List<List<Vector3>>();
        // 以下省略...

2.2 ゲームオーバー時にゲームを再スタートできるようにする

  1. SampleScene の名前を GameScene に変える
  2. Cube を作成する
    • 名前を Respawn にする
  3. LoftSurfaceの下の方に適当な距離をとって、X と Z の Scale を100にする
    • ここに当たらないと再スタートしないので、位置に注意
  4. リスポンの判定に使うのみで、レンダーする必要なので MeshRenderer を非アクティブにする
  5. Add Component で Respawn.cs を追加する。
    • コライダーに入ってきたら実行するメソッドOnCollisionEnterを使う
    • シーンを読み込む形で再スタートを実装する
using UnityEngine;
using UnityEngine.SceneManagement;

public class Respawn : MonoBehaviour
{
    private void OnCollisionEnter(Collision other)
    {
        SceneManager.LoadScene("GameScene");
    }
}

2.3 ゴールを作る

  1. Cube で作成する
    • 名前を Goal にする
  2. ゴールにしたい個所に配置する
    • スケールも好きな値に設定する
    • 単純にこれがゲームの難しさになるので注意
  3. Add Component で Goal.cs を追加する。
  4. ゲームクリア時の画面を作成(次のところでまとめて作成するので後回し)
  5. ゲームクリアなのでBallを消す
    • SerializeField をつけるとエディタ上から値を設定できるようになる
    • Ballをエディタ上で設定する
public class Goal : MonoBehaviour
{
  [SerializeField] private GameObject ball;
  private void OnCollisionEnter(Collision other)
  {
    ball.SetActive(false);
  }
}

2.4 現状の確認

  • ここまで作るとUnityはこんなになっているはずです

  • ここまでのデータは part2 のフォルダ に入っているものになっています

3. UIを作っていく

3.1 クリア画面を作る

  1. Hierarchyで右クリックして、UIからTextを選ぶとHierarchyにCanvasとEventSystemとCanvasの子にTextが作成される

    • CanvasのサイズはGameウインドウのサイズによるので注意してください

  2. Textにクリアを示す文字を入れる

  3. Panelを使って背景を入れる

  4. Panelの名前をGoalPanelにして、Textを子にする

  5. GoalPanelを非アクティブにする

  6. 2.3で作成したGoal.csに下記を追記して、BallがGoalに入った時にGoalPanelをアクティブにして表示されるようにする

    • エディタからGoalPanelをセットしておく
public class Goal : MonoBehaviour
{
  [SerializeField] private GameObject ball;
  [SerializeField] private GameObject goalPanel; // 追加
  private void OnTriggerEnter(Collider other)
  {
    goalPanel.gameObject.SetActive(true); // 追加
    ball.SetActive(false);
  }
}

3.2 リスポンの確認画面を作成する

  1. 3.1と同様にTextとPanelを使って確認画面を作成する
  2. 2.2で作成した Respawn.cs を以下のように書き換える
    • BallがRespawnの枠内に入ったらボールを消して、リスポン確認画面を表示させる
    • Updateでは_retryがtrueかつ右クリックが押されたらGameSceneをロードさせる
public class Respawn : MonoBehaviour
{
    [SerializeField] private GameObject ball;
    [SerializeField] private GameObject respawn;
    private bool _retry = false;

    private void OnCollisionEnter(Collision other)
    {
        respawn.SetActive(true);
        ball.SetActive(false);
        _retry = true;
    }

    void Update ()
    {
        if (Input.GetMouseButtonDown (0) & _retry == true)
        {
            SceneManager.LoadScene("GameScene");
        }
    }
}

3.3 コントロールポイントの座標をスライダーで変更できるようにする

  1. UIからSliderを作成する
  2. Anchorsを左の中央にする
  3. SliderのMinValueを-10、MaxValueを10にする
  4. MoveSphere.csを作成してSliderにアタッチする
public class MoveSphere : MonoBehaviour
{
    [SerializeField] private GameObject sphere;
    private Slider _slider;

    private void Start()
    {
      _slider = gameObject.GetComponent<Slider>();
      _slider.value = 0;
    }

    public void Move()
    {
      // gameobject.transform.position.y は値が変えられないのでいったんposを介して値を変える
      var pos = sphere.transform.position;
      pos.y = _slider.value;
      sphere.transform.position = pos;
    }
}
  1. sphere の部分に座標を操作したいコントロールポイントを設定する
  2. SliderのOn Value Changed を設定する

    • ここで設定されたものはスライダーの値が変えられたときに呼ばれる

  3. 各コントロールポイントにスライダーを設定する

3.4 カメラを設定する

  1. MainCamera を選択するとSceneのウインドウの中にカメラのビューが表示される
  2. ゲーム画面にしたい、いい感じのアングルを設定する

3.5 ゲームのスタート画面を作る

  1. Projectウインドウを右クリックしてCreateからSceneを作成する
    • 名前は TitleScene とする
  2. SceneをTitleSceneに切り替える
  3. Respawn画面などでやったようにTitle画面を作成する

  4. Create Empty から空のGameObjectを作り、それにTitleSceneScriptをアタッチする

    • 今はUnityエディタからRhinoInside を起動しているが、ビルドした単体のアプリとしてもRhinoInside を起動できるようにしなければいけないので、StartにRhinoInside を起動する部分をかく
    • Updateには画面をクリックしたら先程まで作っていたGameSceneがロードされるようにしている
using System;
using System.IO;
using UnityEngine;
using UnityEngine.SceneManagement;
using Rhino.Runtime.InProcess;

public class TitleSceneScript : MonoBehaviour
{
  private void Start()
  {
    string RhinoSystemDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Rhino WIP", "System");
    var PATH = Environment.GetEnvironmentVariable("PATH");
    Environment.SetEnvironmentVariable("PATH", PATH + ";" + RhinoSystemDir);
    GC.SuppressFinalize(new RhinoCore(new string[] { "/scheme=Unity", "/nosplash" }, WindowStyle.Minimized));
  }

  void Update () {

    if (Input.GetMouseButtonDown (0)) {
      SceneManager.LoadScene("GameScene");
    }
  }
}

4. ゲームとしてビルドする

  • File - Build Settingsを開く
  • Scene In Build で作成した2つのシーンを登録する
  • Architecture はx86_64 にする(多分デフォルトでこのあたい)
  • PlayerSettings から OtherSettingsから ScriptingBackend をMono、Api Compatibility Level を .Net 4.x にする
  • Buildする

  • 完成!!!!!

5. その他

  • Build SettingでBuildしたものでもスクリプトのデバッグできる設定がある

  • 起動時にRhinoInsideの起動の待ちが気になる
    • 該当箇所を非同期処理に書き換える
    • Task.Runを使って非同期化
    • うまくCancellationTokenを設定できてないので、強制終了しかできない…
public class TitleSceneScript : MonoBehaviour
{
    void Start()
    {
        string RhinoSystemDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Rhino WIP", "System");
        var PATH = Environment.GetEnvironmentVariable("PATH");
        Environment.SetEnvironmentVariable("PATH", PATH + ";" + RhinoSystemDir);
        Task.Run(() =>
           new RhinoCore(new string[] {"/scheme=Unity", "/nosplash"}, WindowStyle.Minimized));
    }
}

まとめ

  • 最終版は final version のものになっています。
  • ほとんどUnityでしたが、うまく動きましたでしょうか。
  • RhinoInside の部分は、RhinoInsideのリポのUnityフォルダのsample1のものを参考にしています。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Virtual Chara開発 - その4

店舗接客などに使える仮想のキャラクターが扱えるアプリ開発をしました。そちらの説明はこちらになります。
https://qiita.com/NestVisual/items/fe4dd168178e27156682

環境:
Windows 10
Unity 2018.4.1
RealSense D435i

Nuitrackの関節の回転制限:

キャラクターの動きに大きな問題があったのは、トラッキングデータが不正確だと手足が不気味な位置に回転してしまいました。これはかなりの頻度で起きて、精度を上げるためにやることはほとんどありませんでした。このような事態が発生した場合に対処するために、2つの解決策を思いつきました。最初の方はかなりわかりやすく、簡単に実装できました。利用者がカメラに対して相対的に移動できる最小距離と最大距離を設定しました。現状利用者とカメラの距離はその2つの値の範囲内にない場合、骨格トラッキングはキャラに反映されません。

sample2.cs
private void Update()
{
    ProcessSkeleton();
}

private void ProcessSkeleton()
{
    nuitrack.Skeleton skeleton = CurrentUserTracker.CurrentSkeleton;
    UserIsWithinRange = (!(userDistanceCm < _minCamDistance) && !(userDistanceCm > _maxCamDistance));

    if (skeleton != null && UserIsWithinRange)
    {
        IsTrackingReflected = true;
        AnimateSkeleton(skeleton);
    }
    else    {IsTrackingReflected = false;}
}

2つ目の解決策の方が難しいでした。関節の回転が不自然の限定に拘束設定すれば、手足はあまり不自然な位置に動かないと思いました。まず、Unityエディタでモデルの関節を手動で回転させて限界を見つけました。最大値と最小値で、不自然に見える限界の回転を記しました。しかし、これらの回転は各関節の局所的なものであったのに対し、Nuitrackからの回転はすべてグローバルなものです。 このため、トラッキングされた回転をモデルの関節に設定する前に、ローカル回転に変換必要があります。トラッキングされた関節のグローバル回転に親関節の回転の逆数を乗算した値をローカル回転に反映させれば良いと考えました。

Quaternion localRotation = worldRotation * Quaternion.Inverse(parentRotation);

しかし、常に手足があらゆる種類の回転で回転していたので、結果は思った通りに完全違いました。色々のテストの後で、ついにNuitrackフォーラムで問題を投稿しました。Nuitrackのスタッフが、掛け算の順番が関係しているのを教えてくれました。それは考えなかったのですが、掛け算の順番を逆にしたら、回転が正しく設定されました。Quaternion は行列なので、掛け算の順番が重要です。これで関節の回転を制限する事に一歩近づきました。

次の問題は、前述の関節回転制限を使用してみたときに発生しました。これらの回転値は、Unity インスペクタでいじって見つけました。Unity インスペクタでは、負と正の両方の回転値を使用ができます。しかし、Unityは実行時に 0~360 の角度しか使用しません。言い換えれば、0~180度のインスペクタ回転値は実行時の値に正しく対応しますが、-180~0度のインスペクタ値は180~360度の実行時の値に対応します。これを考慮するために、eulerAnglesでローカル関節の回転が180を超えるかどうかに応じて、各軸から360を差し引きました。そしたら、最小回転と最大回転の同じ軸値の間で各軸の値をクランプができました Mathf.Clamp(回転x, 最小x, 最大x)。 これは以下のGetConstrictedRotation()メソッドで行います。

private void RotateJointLocal(ModelJoint modelJoint, Quaternion worldRotation, bool setConstraint)
{
    Quaternion parentRotation = Quaternion.identity;
    if (_newRotations.ContainsKey(modelJoint.parentJointType)) {
        parentRotation = _newRotations[modelJoint.parentJointType];
    }
    Quaternion localRotation = Quaternion.Inverse(parentRotation) * worldRotation;
    Vector3 localEuler = localRotation.eulerAngles;

    if (modelJoint.jointType == nuitrack.JointType.Torso)   localEuler.x = 0;
    if (modelJoint.jointType == nuitrack.JointType.Waist)   localEuler.x = 0;

    if (setConstraint)  localEuler = GetConstrictedRotation(localEuler, modelJoint.jointType);

    Vector3 currentEuler = modelJoint.bone.localEulerAngles;

    modelJoint.bone.localEulerAngles = new Vector3(
        Mathf.LerpAngle(currentEuler.x, localEuler.x, _moveSpeed * Time.deltaTime),
        Mathf.LerpAngle(currentEuler.y, localEuler.y, _moveSpeed * Time.deltaTime),
        Mathf.LerpAngle(currentEuler.z, localEuler.z, _moveSpeed * Time.deltaTime));
}

private Vector3 GetConstrictedRotation(Vector3 euler, nuitrack.JointType jointType)
{
    var minRot = _jointMinRotations[jointType];
    var maxRot = _jointMaxRotations[jointType];

    float x = (euler.x > 180) ? euler.x - 360 : euler.x;
    float y = (euler.y > 180) ? euler.y - 360 : euler.y;
    float z = (euler.z > 180) ? euler.z - 360 : euler.z;

    x = Mathf.Clamp(x, minRot.x, maxRot.x);
    y = Mathf.Clamp(y, minRot.y, maxRot.y);
    z = Mathf.Clamp(z, minRot.z, maxRot.z);

    return new Vector3(x, y, z);
}

このように、モデル四肢の回転を制限する事が出来た為、不気味な動きを軽減させる事が出来ました。
次回はキャラのアニメーションに関して投稿します。それではまた

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む