20201104のUnityに関する記事は4件です。

HoloLens2×WebRTC×Raspberry Pi Zero Wを使ってRaspberry Pi Zero Wでのカメラ映像をHoloLens2に表示する

概要

WebRTCを使ってRaspberry Pi Zero Wからのカメラ映像をHoloLens2に投影することにより、別の視点からの状況を同時に見れたりすることが出来たりする。今回記事にしたのは環境構築に非常に手間取ったためである。
結論としては、dllをビルドした時の対象アーキテクチャを合わせてあげる必要があった。
使ったハードと技術並べてタイトルにしただけなのになろう系みたいな長文タイトルになってしまった。

はじめに

HoloLens2の背面にカメラを載せて、車のバックモニターみたいな機構を作れないか調べてたらWebRTCと呼ばれる技術が使えそうだということで、実際に簡単なプロジェクトを作ってみることにした。

環境構築

以下の環境、ライブラリを使用した。

  • Windows 10 Home
  • Unity 2019.4.13f1
  • Visual Studio 2019
  • Raspbian GNU/Linux 10
  • Microsoft Mixed Reality Toolkit 2.5.0
  • MixedReality-WebRTC 2.0.0-preview.1
  • MixedReality-WebRTC Samples 2.0.0-preview.1
  • MixedReality-WebRTC-ayame
  • WebRTC シグナリングサービス Ayame Lite
  • WebRTC Native Client Momo
  • Newtonsoft.Json 12.0.3
  • SuperSocket.ClientEngine
  • WebSocket4Net v0.15

プロジェクト作成

Raspberry Pi Zero W編

  1. OSはデフォルトのRaspberry Pi OSを使用。スイッチサイエンス社からOSがプリインストールされているキットが販売されていたため、OSのインストールは割愛する。
  2. WebRTCクライアントのWebRTC Native Client Momoを配置する。Raspberry Pi Zero W用のバイナリが提供されているのでそれをダウンロードする。
  3. 実行方法はSETUP_RASPBERRY_PI.mdを参照。 シグナリングキーとルームIDはHoloLens2側と合わせること。

HoloLens2編

ビルドサポート追加

HoloLens2はUWP環境らしいので以下のようにUWPのビルドサポートを追加する。
28afba3da4a3f6b36c1db373f7cbbbec.png

ライブラリ追加

MixedReality-WebRTC

プロジェクトフォルダ/Packages/manifest.jsonファイルに以下のように追記をする。

  "scopedRegistries": [
    {
      "name": "Microsoft Mixed Reality",
      "url": "https://pkgs.dev.azure.com/aipmr/MixedReality-Unity-Packages/_packaging/Unity-packages/npm/registry/",
      "scopes": [
        "com.microsoft.mixedreality"
      ]
    }
  ],
  "dependencies": {
    "com.microsoft.mixedreality.webrtc": "2.0.0-preview.1",
    "com.microsoft.mixedreality.webrtc.samples": "2.0.0-preview.1",

ライブラリがインポートされるとWindowタブ > Package Managerに追加される。
d97b8859613242f2179b95fc7d5d0c7b.png

MixedReality-WebRTC-ayame

GithubのReleasesからMixedReality-WebRTC-ayama-x.x.x.unitypackageをダウンロードし、インポートする。
README.mdにはWebSocket4Net、SuperSocket.ClientEngine、Newtonsoft.Jsonのdllをインポートすると書いてあるので、それらもインポートする。

WebSocket4Net

v0.15ブランチを使用する。
WebSocket4Net.slnを開き、ビルドタブからWebSocket4Net.Net45のビルドを選択しdllファイルを作成する。作成されたWebSocket4Net.dllをプロジェクト内に配置してInspectorからPlatform settingsでUWPでの設定を変更する。
71be80959f39031b89e92200db311adc.png
SDKをUWPに、ScriptingBackendをIl2Cppに変更している。

SuperSocket.ClientEngine

WebSocket4Netのビルド時にWebSocket4Net.dllと同じディレクトリにSuperSocket.ClientEngine.dllが作成されているので、それをWebSocket4Net.dllと同じようにプロジェクト内に配置してInspectorからPlatform settingsでUWPでの設定を変更する。

Newtonsoft.Json

Newtonsoft.Jsonのサイトからビルド済みのものをダウンロードできる。
netstandard2.0ディレクトリ内にあるNewtonsoft.Json.dllファイルを同様に、プロジェクト内に配置してInspectorからPlatform settingsでUWPでの設定を変更する。

Microsoft Mixed Reality Toolkit

GithubのReleasesからMicrosoft.MixedReality.Toolkit.Unity.Foundation.x.x.x.unitypackageをダウンロードし、インポートする。

シーン作成

MixedReality-WebRTC-ayameにシグナリングサーバーにayameを用いたサンプルシーンがあるのでそれを流用する。今回はRaspberry Pi Zero WからHoloLens2に一方的に映像を送るのでHoloLens2から送る処理は取り除く。
AyameSignalerのシグナリングキーなども設定する。この時、Raspberry Pi Zero Wと同じシグナリングキーとルームIDを設定する。

後々使うかもしれないのでMicrosoft Mixed Reality Toolkitもシーンに追加する。
Mixed Reality ToolkitタブからAdd to Scene and Configure...を選択してオブジェクトを追加する。

ビルド

Build SettingsとPlayer Settingsを設定しビルドを行う。
131fefc23a8b1a292a1f0b86707b2d30.png
38eae218e728d335c29e82f3ad090be0.png

実機確認

https://youtu.be/g7vfVKh0KTQ

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

UnityでSliderを使ってCubeを回したい

SS 192.png Sliderの値の受け渡しのところがよくわからない

やること

タイトル通り、UnityのSliderを使ってCubeを回します。
オブジェクト間の値わたしについて、Sliderを例に理解していきます。
自分が全忘却した時の備えとして、工程を全メモします。
たぶんUnityをインストールしただけでほとんど使い方がわからないという人でもできます。

素材の準備

キャンバス、スライダー、キューブ、キューブ用スクリプトの4つを準備します。すべてデフォルト機能のものです。

① Canvasを準備

スライダーを設置するためのCanvasを準備します。Canvasはカメラの前に置くレイヤーのような画面で、ゲームのスコアなどを表示する面です。

Hierarchyウィンドウの何もないところで「右クリック」→「UI」→「Canvas」

Canvasを設定すると、EventSystemというオブジェクトも同時に生成されます。

② Sliderを準備

Sliderはドラックで数値を入力できる長いスイッチです。
これをCanvasの上に設置します。

Hierarchyウィンドウの「Canvas」の上で「右クリック」→「UI」→「Slider」

設置が終わったらシーンビュー(メインウィンドウ)の上のタブを「Scene」ビューから「Game」ビューに切り替えると、画面にスライダーが設置されていることがわかります。
(GameビューはUnityの再生時に見えるカメラ目線で、Sceneビューは作業用の目線になります。)

③ Cubeを準備

スライダーを使って回転させるCubeを置きます。

Hierarchyウィンドウの何もないところで「右クリック」→「3D Object」→「Cube」

④ Scriptを準備

ProjectウィンドウのAssetsの中の何もないところで「右クリック」→「Create」→「C# Script」を選択し、ファイル名を「SliderDemo」とします。

これで今回使う素材の準備が整いました。

UnityのPublicを理解する

Unityは各オブジェクトに紐づく変数や関数を他のオブジェクトとやりとりしたり、右のInspectorウィンドウに項目としてその値を表示し、書き換えもできるようになっています。
そこがUnityの便利なところなのですが、変数にその機能を持たせるにはPublicというアクセス権を設定する必要があります。という話。大事なのはそこだけです。

仕込み

① CubeにアタッチするC#スクリプトを書く

SliderDemoのファイルを開くと、スクリプトをUnityで使えるようにするための雛形がすでに記述されています。
MonoBehaviourというUnityでしか見ない単語があり気になりますが、Unityで扱えるようにするための標準セットを継承するためのもの、ぐらいに考えておけばよさそうです。
また、Unityのスクリプトには「ファイル名とスクリプト内のclass名を必ず同じにする」という大事なルールがあるので注意が必要です。

内容を追記し、下記のように改めます。

SliderDemo
using UnityEngine;

public class SliderDemo : MonoBehaviour //Unity標準クラスを継承
{
    //スライダーの値を「取得する」ためのパブリック変数を定義する関数
    //取得した値のCubeの回転への反映もここで行います。
    public void SliderDemo_CubeRot(float newValue)
    {
        // キューブを回転させるための変数を宣言
        // Vector3変数は3つの値を格納できる型
        // その変数に現在のオイラー角xyzを代入
        // thisはこのスクリプトをアタッチしたGameObjectを表す
        Vector3 rot = this.transform.localEulerAngles;

        // 縦軸のy成分を代入
        rot.y = newValue;

        // 新しい数値をキューブの角度に代入
        this.transform.localEulerAngles = rot;
    }

    // Start is called before the first frame update
    void Start()
    {
    }

    // Update is called once per frame
    void Update()
    {
    }
}

Cubeを回転させるためのTransformのところは今回はあまり気にしないでください。
void Start()やvoid Update()はカラのままで大丈夫です。

② スライダーの設定をする

HierarchyウィンドウのSliderを選択すると、右のInspectorに設定画面が出ます。
一番下の方にMin Value、Max Valueという項目があるので、ここにスライダで操作したい回転度数の範囲として-180と180を入力しておきます。
ちなみにその下にあるWholo Numbersというチェックボックスですが、これをチェックするとValueの値を整数に制限できるそうです。

仕上げ

オブジェクトやコンポーネントをアタッチして関連づける

オブジェクト(GameObject)というのはUnityの要素の単位で、キャラクターはもちろん、マシンガンの球、光源、ビュー用のカメラまでゲームを構成するあらゆる物体の基本となるものです。またコンポーネントというのはオブジェクトに内包される構成要素で、形、色、衝突判定やスクリプトなどが定義されます。
衝突判定はUnityの物理世界の中で最初から相互に関連するようになっていますが、独自に設定した変数や関数についてはオブジェクト同士で紐付けないと作用がしません。
ということで、

① スクリプトをCubeにアタッチする

Assets内のSliderDemoのスクリプトファイルを、HierarchyウィンドウのCubeにドラッグ&
ドロップでアタッチします。しっかり重ねるとブルーにハイライトするので、その時にドラッグします。(ちゃんと重ねないとグレーのハイライトとなり、その状態ではアタッチされません)
Cubeのインスペクターウィンドウに「SliderDemo(Script)」という項目が追加されていれば成功です。

② スライダーにCubeをアタッチする

ここがミソです。Sliderの値(Value)をどう取り出すかという話。
SliderのInspectorの下にある「On Value Changed(Single)」という項目を使います。ここでプラスを押すと、Valueの渡し先を設定できるようになります。

項目の説明です。
まず左上のプルダウンメニューの「OFF/Editor and Runtime/Runtime Only」の選択ですが、Runtime Onlyを選んでおけば大丈夫です。(Editor and Runtimeを選べば、再生中以外でもスライダーの値が反映されるようになるようです。)

次に、その下のボックスにオブジェクトをアタッチします。
今回はCube(Hierarchyウィンドウ内)をこのボックスにドラック&ドロップします。

すると右側のプルダウンメニューからSliderDemoの項目を選べるようになります。いま関連づけたCubeにはSliderDemoのスクリプトがアタッチされており、その内容である関数が選べるようになっているというわけです。
さっそく「SliderDemo」→「SliderDemo_CubeRot」を選択します。

この設定をすることでスライダーで設定したValueの値(-180~180)が独自に設定した関数「SliderDemo_CubeRot」に渡されるようになりました。

これで準備は完了です。

完成・実行

画面上部の三角ボタンからプログラムを実行します。
スライダーの操作に連携して、キューブが横に回転してくれます。

CubeRot1.gif

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

全集中・Magic Leapの呼吸 肆ノ型 手入力 "かすたむ じぇすちゃ"

なにをするの?

今回作るものとしてはMagicLeapのジェスチャ( 8種類 )を組み合わせてカスタムのジェスチャを作成するというもの


サンプル動画( twitterがおしゃかポンになったときの予備 )

プロジェクトはこちらのリポジトリにあります

下準備

本記事は基本的なビルドまでの手順はなされていることを前提として記事を書いています
ビルド時に利用する認証ファイルとかZeroIterationの設定の仕方とか

開発環境

ProjectSettingsのXR Plug-in Management の MagicLeapの項目にチェックを入れる
image.png

今回はハンドジェスチャを利用するので ProjectSettings > MagicLeap > ManifestSettings より GestureConfig, GestureSubscribe を有効化
image.png

シーンの構築

CameraRigの作成
MagicLeap > Core > Assets > Prefabs からMain Cameraをシーンに配置し、一旦そのMain Cameraオブジェクトを任意のフォルダにプレハブとして保存する( 名前を CameraRig に変更 )
image.png

HandControllerの作成
CameraRigオブジェクトの下にHandControllerオブジェクトを作成し、 MagicLeap-Tools > Code > Input > Hands > HandInput をアタッチします
image.png


このままだと手が描画されておらず確認しづらいのでサンプルにあるHandVisualizerを利用してテストします

HandControllerオブジェクトの下に MagicLeap > Examples > Assets > Prefabs > HandVisualizer オブジェクトを配置し、HandVisualizerコンポーネントのCenterの項目にはnullを指定します( Centerが設定されている場合は球で手の各関節を表示する機能がONになり見た目上邪魔になるため )
image.png

HandVisualizserオブジェクトの子オブジェクトにLHand, RHand オブジェクトを作成し、HandInputシーンから [VISUALIZERS]オブジェクトの中のKeyPointVisualizers 以下のオブジェクトを複製、Right~ はRHandオブジェクトの子オブジェクトに Left~はLHandオブジェクトの子オブジェクトにします
image.png

上の手順を終えたものがこちら、なおCenterオブジェクトはHandVisualizerオブジェクトを複製する際についてきたものなので削除しても構いません
image.png

RHand, LHand それぞれにHandSkeletonVisualizerコンポーネント、AxisVisualizerコンポーネントをアタッチ、BoneColorはお好きな色を設定
image.png

ここまでの動作確認

なぜかGameウィンドウに描画されていなかったのでSceneViewの方のスクショ
image.png

ZeroIterationでの実機上でのスクショ
ML_20201128_19.14.58.jpg

スクリプトの作成

MagicLeapが提供しているデフォルトのジェスチャ( 8種類 )を組み合わせてカスタムジェスチャを作成するスクリプトを作ります
InteractionModel_InputMethods_Gesture_Example_Main.png

スクリプト全容( せっかちな人はこれをコピペ )

using System;
using MagicLeapTools;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.XR.MagicLeap;
using Debug = UnityEngine.Debug;
using Cysharp.Threading.Tasks;
using UniRx;


namespace AdventCalendar
{
    /// <summary>
    /// ハンドコントローラ.
    /// </summary>
    public class HandController : MonoBehaviour
    {

        struct KeyInfo
        {
            public HandPose pose;
            public float time;
        }


        public enum HandPose
        {
            LFinger,
            RFinger,
            LFist,
            RFist,
            LPinch,
            RPinch,
            LThumb,
            RThumb,
            LL,
            RL,
            LOpenHand,
            ROpenHand,
            LOk,
            ROk,
            LC,
            RC,
            LNoPose,
            RNoPose,
            LNoHand,
            RNoHand
        }


        private HandPose handPose;
        private ManagedHand rHand;
        private ManagedHand lHand;
        [SerializeField] private GameObject handVisualizer;
        [field: SerializeField] public bool IsGestureLogOutput { get; set; } = false;




        private async void Start()
        {
            await Setup();            
            SwitchHandVisualize();
        }


        private async UniTask Setup()
        {
            await UniTask.WaitUntil(() => HandInput.Ready);
            rHand = HandInput.Right;
            lHand = HandInput.Left;
            rHand.Gesture.OnKeyPoseChanged += OnHandGesturePoseChanged;
            lHand.Gesture.OnKeyPoseChanged += OnHandGesturePoseChanged;

            // 確認用にジェスチャをDebug.Logに出力.
            this.ObserveEveryValueChanged(_ => handPose).Subscribe(e =>
            {
                if (IsGestureLogOutput)
                    Debug.Log($"Key {e}");
            });
        }


        private void SwitchHandVisualize()
        {
#if UNITY_EDITOR
            handVisualizer.SetActive(true);
#elif !UNITY_EDITOR || UNITY_LUMIN
            handVisualizer.SetActive(false);
#endif
        }


        private void OnHandGesturePoseChanged(
            ManagedHand hand,
            MLHandTracking.HandKeyPose pose)
        {
            bool isLeft = hand.Hand.Type == MLHandTracking.HandType.Left;
            switch (pose)
            {
                case MLHandTracking.HandKeyPose.C: handPose = isLeft ? HandPose.LC : HandPose.RC; break;
                case MLHandTracking.HandKeyPose.Finger: handPose = isLeft ? HandPose.LFinger : HandPose.RFinger; break;
                case MLHandTracking.HandKeyPose.Fist: handPose = isLeft ? HandPose.LFist : HandPose.RFist; break;
                case MLHandTracking.HandKeyPose.L: handPose = isLeft ? HandPose.LL : HandPose.RL; break;
                case MLHandTracking.HandKeyPose.Ok: handPose = isLeft ?  HandPose.LOk : HandPose.ROk; break;
                case MLHandTracking.HandKeyPose.Pinch: handPose = isLeft ? HandPose.LPinch : HandPose.RPinch; break;
                case MLHandTracking.HandKeyPose.Thumb: handPose = isLeft ? HandPose.LThumb : HandPose.RThumb; break;
                case MLHandTracking.HandKeyPose.NoHand: handPose = isLeft ? HandPose.LNoHand : HandPose.RNoHand; break;
                case MLHandTracking.HandKeyPose.NoPose: handPose = isLeft ? HandPose.LNoPose : HandPose.RNoPose; break;
                case MLHandTracking.HandKeyPose.OpenHand: handPose = isLeft ? HandPose.LOpenHand : HandPose.ROpenHand; break;
            }
        }


        /// <summary>
        /// ジェスチャコマンドのオブザーバー作成.
        /// </summary>
        /// <param name="time"></param>
        /// <param name="poseA"></param>
        /// <param name="poseB"></param>
        /// <returns></returns>
        private IObservable<KeyInfo> CreateGestureCommandObserver(
            float time,
            HandPose poseA,
            HandPose poseB)
        {
            // 指定したキーの判定を通知するObserverを返す.
            IObservable<KeyInfo> GetInputObserver(HandPose pose)
            {
                return this.ObserveEveryValueChanged(_ => handPose)
                    .Where(k => k == pose)
                    .Select(k => new KeyInfo{pose = k, time = Time.realtimeSinceStartup});
            }

            var observer = GetInputObserver(poseA);
            observer = observer.Merge(GetInputObserver(poseB))
                .Buffer(2, 1)
                .Where(b => b[1].time - b[0].time < time)
                .Where(b => b[0].pose == poseA && b[1].pose == poseB)
                .Select(b => b[1]);

            return observer;
        }


        /// <summary>
        /// カスタムジェスチャを登録し、登録したカスタムジェスチャが発火されたら実行する.
        /// </summary>
        /// <param name="time"></param>
        /// <param name="poseA"></param>
        /// <param name="poseB"></param>
        /// <param name="callback"></param>
        public void RegisterCustomGesture(
            float time,
            HandPose poseA,
            HandPose poseB,
            UnityAction callback,
            Func<bool> option = null)
        {
            CreateGestureCommandObserver(time, poseA, poseB)
                .Subscribe(e => callback?.Invoke())
                .AddTo(this);
        }

    }
}

スクリプトの解説

先ずカスタムのジェスチャを作成するためにハンドポーズを宣言します、MagicLeapのデフォルトのHandPoseは MLHandTracking.cs 内に定義されています、このままでも使えますが使い勝手が悪い為一旦カスタムのジェスチャを定義してそちらに変換するようにします。

        public enum HandPose
        {
            LFinger,
            RFinger,
            LFist,
            RFist,
            LPinch,
            RPinch,
            LThumb,
            RThumb,
            LL,
            RL,
            LOpenHand,
            ROpenHand,
            LOk,
            ROk,
            LC,
            RC,
            LNoPose,
            RNoPose,
            LNoHand,
            RNoHand
        }


取得したハンドジェスチャをカスタムのジェスチャに変換する処理

        private void OnHandGesturePoseChanged(
            ManagedHand hand,
            MLHandTracking.HandKeyPose pose)
        {
            bool isLeft = hand.Hand.Type == MLHandTracking.HandType.Left;
            switch (pose)
            {
                case MLHandTracking.HandKeyPose.C: handPose = isLeft ? HandPose.LC : HandPose.RC; break;
                case MLHandTracking.HandKeyPose.Finger: handPose = isLeft ? HandPose.LFinger : HandPose.RFinger; break;
                case MLHandTracking.HandKeyPose.Fist: handPose = isLeft ? HandPose.LFist : HandPose.RFist; break;
                case MLHandTracking.HandKeyPose.L: handPose = isLeft ? HandPose.LL : HandPose.RL; break;
                case MLHandTracking.HandKeyPose.Ok: handPose = isLeft ?  HandPose.LOk : HandPose.ROk; break;
                case MLHandTracking.HandKeyPose.Pinch: handPose = isLeft ? HandPose.LPinch : HandPose.RPinch; break;
                case MLHandTracking.HandKeyPose.Thumb: handPose = isLeft ? HandPose.LThumb : HandPose.RThumb; break;
                case MLHandTracking.HandKeyPose.NoHand: handPose = isLeft ? HandPose.LNoHand : HandPose.RNoHand; break;
                case MLHandTracking.HandKeyPose.NoPose: handPose = isLeft ? HandPose.LNoPose : HandPose.RNoPose; break;
                case MLHandTracking.HandKeyPose.OpenHand: handPose = isLeft ? HandPose.LOpenHand : HandPose.ROpenHand; break;
            }
        }


カスタムジェスチャ購読用のObserverを作成する処理
指定時間以内に 1番目のジェスチャ -> 2番目のジェスチャ と連続で呼ばれたイベントを購読するオブザーバーを作成し、返り値で返します

   private IObservable<KeyInfo> CreateGestureCommandObserver(
            float time,
            HandPose poseA,
            HandPose poseB)
        {
            // 指定したキーの判定を通知するObserverを返す.
            IObservable<KeyInfo> GetInputObserver(HandPose pose)
            {
                return this.ObserveEveryValueChanged(_ => handPose)
                    .Where(k => k == pose)
                    .Select(k => new KeyInfo{pose = k, time = Time.realtimeSinceStartup});
            }

            var observer = GetInputObserver(poseA);
            observer = observer.Merge(GetInputObserver(poseB))
                .Buffer(2, 1)
                .Where(b => b[1].time - b[0].time < time)
                .Where(b => b[0].pose == poseA && b[1].pose == poseB)
                .Select(b => b[1]);

            return observer;
        }

動作確認

サンプル用スクリプト

Sample.cs

using UnityEngine;

namespace AdventCalendar
{
    public class Sample : MonoBehaviour
    {

        [SerializeField] private Transform camera;
        [SerializeField] private HandController handController;
        [SerializeField] private GameObject objA;
        [SerializeField] private GameObject objB;
        [SerializeField] private GameObject objC;

        private void Start()
        {
            // 1秒以内に右手グー( RFist ) -> 右手パー( ROpenHand )で発火 SpawnObjA()を処理する.
            handController.RegisterCustomGesture(1f, HandController.HandPose.RFist, HandController.HandPose.ROpenHand, SpawnObjA);

            // 1秒以内に左手グー( LFist ) -> 左手パー( LOpenHand )で発火 SpawnObjB()を処理する.
            handController.RegisterCustomGesture(1f, HandController.HandPose.LFist, HandController.HandPose.LOpenHand, SpawnObjB);

            // 1秒以内に左手OK ( LOk ) -> 右手OK ( ROk )で発火 SpawnObjC()を発火する.
            handController.RegisterCustomGesture(1f, HandController.HandPose.LOk, HandController.HandPose.ROk, SpawnObjC);
        }


        private void SpawnObjA()
        {
            GameObject obj = Instantiate(objA);
            obj.transform.position = camera.position + (camera.forward * 0.5f);
        }


        private void SpawnObjB()
        {
            GameObject obj = Instantiate(objB);
            obj.transform.position = camera.position + (camera.forward * 0.5f);
        }


        private void SpawnObjC()
        {
            GameObject obj = Instantiate(objC);
            obj.transform.position = camera.position + (camera.forward * 0.5f);
        }

    }
}

SpawnObj.cs
テスト用のオブジェクト、生成されてから5秒で破棄するだけの処理

using System.Collections;
using UnityEngine;

namespace AdventCalendar
{
    /// <summary>
    /// スポーンしたオブジェクト用スクリプト、5秒で破棄する.
    /// </summary>
    public class SpawnObj : MonoBehaviour
    {
        private void Start()
        {
            StartCoroutine(AutoDeath());
        }


        private IEnumerator AutoDeath()
        {
            yield return new WaitForSeconds(5f);
            Destroy(gameObject);
        }

    }

}

シーン上に適当なオブジェクト ObjA ~ ObjC を作成しプレハブにし、それぞれにSpawnObjコンポーネントをアタッチ
image.png

動作確認用に Sampleオブジェクトを作成し Sampleコンポーネントをアタッチ、シーン上に配置します、オブジェクトの参照周りは以下の画像の通り
image.png

Sample.csのStart()にてカスタムジェスチャを登録し、イベントが発火されたら登録したオブジェクトを生成するようにしています
今回のサンプルでは

  • 右Fist -> 右OpenHand で ObjA の生成
  • 左Fist -> 左OpenHand で ObjB の生成
  • 左OK -> 右OK で ObjC の生成

を行います

        private void Start()
        {
            handController.RegisterCustomGesture(1f, HandController.HandPose.RFist, HandController.HandPose.ROpenHand, SpawnObjA);
            handController.RegisterCustomGesture(1f, HandController.HandPose.LFist, HandController.HandPose.LOpenHand, SpawnObjB);
            handController.RegisterCustomGesture(1f, HandController.HandPose.LOk, HandController.HandPose.ROk, SpawnObjC);
        }

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

[Unreal Engine4] Material Nodeに対応するHLSL, GLSL

はじめに

北千住さんの記事がとても見やすかったのでその形式を踏襲させていただきました.また,そちらの記事に関数の説明は載っているので参考にしてもらえればと思います.
また,Unity, openFrameworks, TouchDesignerなどでShader言語を書いていて,UnrealEngineも触り始めたという方の作業効率の向上に繋がれば幸いです.

Type

UnrealEngine HLSL GLSL
Constant float float
Constant2Vector float2 vec2
Constant3Vector float3 vec3
Constant4Vector float4 vec4
Texture Sample sampler2D sampler2D

Function

UnrealEngine HLSL GLSL
Floor floor floor
Frac frac fract
Fmod fmod mod
atan2 atan2 atan
lerp lerp mix
Power lerp mix
Abs abs abs
ValueStep step step
SmoothStep smoothstep smoothstep
Clamp clamp clamp
VectorLength length length
Distance distance distance
Normalize normalize normalize

HLSLのみ

UnrealEngine HLSL
Saturate saturate

Sample NodeRecipe

これらの関数の特徴を捉えることが出来るノードレシピの1例です.(参考までに)

注意

  1. OutはすべてMaterialのベースカラーへ繋がっています.
  2. ViewerのQuadのUVは左上が原点です bandicam 2020-11-04 01-18-06-106.jpg

Floor

bandicam 2020-11-04 00-25-27-676.jpg

bandicam 2020-11-04 00-40-33-530.jpg

Frac

bandicam 2020-11-04 00-25-51-635.jpg

bandicam 2020-11-04 00-40-45-211.jpg

Fmod

bandicam 2020-11-04 00-29-21-113.jpg
bandicam 2020-11-04 00-40-54-668.jpg

Lerp

bandicam 2020-11-04 00-43-04-667.jpg

bandicam 2020-11-04 00-42-50-617.jpg

Power

bandicam 2020-11-04 00-31-28-370.jpg

bandicam 2020-11-04 00-43-59-835.jpg

Abs

bandicam 2020-11-04 00-33-49-378.jpg

bandicam 2020-11-04 00-44-18-369.jpg

ValueStep

1th inputによって1.0からoffsetを作って黒の開始させる.つまり,offset = 0.2だと0.8まで黒.

//glslとかに書き換えるとこんな感じ
//1th inputの値をoffsetとすると
v = step(1.0 - offset, x);

bandicam 2020-11-04 01-06-19-711.jpg

bandicam 2020-11-04 01-06-13-851.jpg

SmoothStep

bandicam 2020-11-04 00-34-15-635.jpg

bandicam 2020-11-04 00-45-08-255.jpg

Sign

bandicam 2020-11-04 00-35-24-627.jpg

bandicam 2020-11-04 00-45-35-853.jpg

Saturate

bandicam 2020-11-04 00-36-09-389.jpg

bandicam 2020-11-04 00-46-34-656.jpg

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