20200807のUnityに関する記事は10件です。

Unity Recorderにカスタムソースを追加する( feat. ADX2 )

Unity Recorderにカスタムの録音・録画ソースを追加したい!

お題がだいぶレアケース ですが、Unityのエディター上で動画や音声を記録できるUnity Recorderにカスタムのソースを追加したい場合のガイドです。

CRI ADX2はUnityに対応した統合型サウンドミドルウェアです。Unity内部のサウンドシステムとは完全に別のライブラリで処理されるため、通常のUnity Recorderの機能では音が記録できません。
そこで、Unity Recorderにカスタムな録音ソースを追加して対応しました。

下図のように、Recordersの要素として「ADX2 Audio」を追加し、動画と一緒に録音できるようにしています。

UnityRecoderInterface.png

パッケージは以下に公開していますので、機能を使いたい人はこちらからどうぞ。

ADX2 for Unity Recorder
https://github.com/TakaakiIchijo/ADX2forUnityRecorder

世の中にUnity Recorderに新しいソースを追加したいと思う人がどれだけいるかは不明なのですが、このパッケージの開発に結構手間取ったので記録します。
(機能拡張に関するドキュメントは見当たらなかったので、ソースを読んで仕組みを把握しました。)

今回のADX2対応ケース以外には、たとえば「XR系の特殊な描画部分を録画したい」「複数ソースの音声をミックスしてから録音したい」あたりが当該すると思います。

なお、ADX2の録音部分については以下の記事で解説しています。

Unity + ADX2環境でゲーム中の音を録音する
https://qiita.com/Takaaki_Ichijo/items/faf96c22740ff5a7587d

Unity Editor上でADX2再生データの音を録音する with Unity Recorder
https://qiita.com/Takaaki_Ichijo/items/853d963b3341b54d6454

動作確認環境

Unity 2020.1.0f1 + Unity Recorder 2.2.0-preview

必要なクラス

Unity Recorderで何らかのソースを記録・主力したい時、以下の4つのクラスが必要です。XXには記録対象の名前を当てはめてください。

XXRecorder

録音処理本体を定義します。

XXRecorderSettings

Recorderウィンドウで設定する値やファイル名などを保存する、ScriptableObject派生クラスです。

XXRecorderEditor

Recorderウィンドウの中に表示する設定項目を定義するCustomEditorアトリビュート付きクラスです。

XXRecorderInputSettings

保存するデータソースの設定をシリアライズするクラスです。

XXRecorderInputSettingsクラスの作成

まずは入力ソースの設定を保存するシリアライズクラスを用意します。
名前空間UnityEditor.Recorder.InputのRecorderInputSettingsの派生クラスとして用意します。
以下はADX2用クラスの例です。

AtomInputSettings.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;

namespace UnityEditor.Recorder.Input
{
    [DisplayName("ADX2")]
    [Serializable]
    public class AtomInputSettings : RecorderInputSettings
    {
        protected override Type InputType
        {
            get { return typeof(RecorderInput); }
        }

        protected override bool ValidityCheck(List<string> errors)
        {
            return true;
        }
    }
}

ADX2はUnity外部のデータをソースとするため、実はこのクラスにはほとんど意味が無いのですが、RecorderSettings派生クラスがRecorderInputSettingsを必要とするため、用意しています。

XXRecorderクラスの作成

録音操作本体を記述します。
GenericRecorder<RecorderSettings>クラスの派生として用意します。
BeginRecordingオーバーライドメソッドで録音録画の初期化を行い、RecordFrameメソッドでフレームごとのキャプチャを行い、EndRecordingで終了処理とファイルの書き出し処理を記述します。

AtomRecorder.cs
using System;
using UnityEngine;

namespace UnityEditor.Recorder
{
    class AtomRecorder : GenericRecorder<AtomRecorderSettings>
    {
       /中略/
        protected override bool BeginRecording(RecordingSession session)
        {
            if (!base.BeginRecording(session))
                return false;
            try
            {
                Settings.FileNameGenerator.CreateDirectory(session);
            }
            catch (Exception)
            {
                Debug.LogError(string.Format( "Atom recorder output directory \"{0}\" could not be created.", Settings.FileNameGenerator.BuildAbsolutePath(session)));
                return false;
            }

        /録音処理の初期化/
                return true;
        }

        protected override void RecordFrame(RecordingSession session)
        {
        /フレームごとの録音処理/
        }

        protected override void EndRecording(RecordingSession session)
        {
        /録音処理の終了・ファイルの保存/
        }
    }
}

基底クラスのSettings.FileNameGeneratorから、エディター内で設定したファイル名にアクセスできます。備え付けのAudioRecorderやMovieRecorderはSettings.fileNameGeneratorにアクセスしているのですが、カスタムソースを作った場合はFileNameGeneratorからアクセスします。

XXRecorderSettingsクラスの作成

RecorderSettingsクラスの派生として用意します。RecorderSettingsアトリビュートで先ほど作成したレコーダー本体と紐づけます。

ADX2拡張の場合はサンプル数をエディタで設定したかったので、カスタムの設定要素としてNumSumplesを追加しています。

AtomRecorderSettings.cs
using System.Collections.Generic;
using UnityEditor.Recorder.Input;
using UnityEngine;

namespace UnityEditor.Recorder
{
    [RecorderSettings(typeof(AtomRecorder), "ADX2Audio")]
    class AtomRecorderSettings : RecorderSettings
    {
        protected override string Extension => "wav";

        public int NumSamples
        {
            get { return numSamples; }
            set { numSamples = value; }
        }

        [SerializeField] private int numSamples = 512;

        [SerializeField] AtomInputSettings m_AtomInputSettings = new AtomInputSettings();

        public override IEnumerable<RecorderInputSettings> InputsSettings
        {
            get { yield return m_AtomInputSettings; }
        }

        public AtomRecorderSettings()
        {
            FileNameGenerator.FileName = "mixdown";
        }
    }
}

XXecorderEditorクラスの作成

さいごにUnity Recorderウィンドウ内の描画を定義するクラスを作成します。
RecorderEditorクラスの派生として用意します。
CustomEditorアトリビュートでXXRecorderSettingsと紐づけます。

AtomRecorderEditor.cs
using UnityEngine;

namespace UnityEditor.Recorder
{
    [CustomEditor(typeof(AtomRecorderSettings))]
    class AtomRecorderEditor : RecorderEditor
    {
        SerializedProperty m_NumSamples;

        static class Styles
        {
            internal static readonly GUIContent NumSamples = new GUIContent("NumSamples");
        }

        protected override void OnEnable()
        {
            base.OnEnable();

            if (target == null)
                return;

            m_NumSamples = serializedObject.FindProperty("numSamples");
        }

        protected override void FileTypeAndFormatGUI()
        {
            EditorGUILayout.PropertyField(m_NumSamples, Styles.NumSamples);
        }
    }
}

SerializedProperty として、カスタム設定要素を定義します。このとき、serializedObject.FindPropertyメソッドで値をフィールド名で引っ張ってきます。

手順は以上です。

カスタムソース由来の音声・動画をミックスするためには

標準のMovieRecorderでは、「Capture Audio」というチェックを入れると音声が動画とミックスされた状態で保存されます。
音声・動画が一体になったMP4などを出力することもできますが、その場合はMovieRecorderのソースにかなり手を入れないといけないため、今回は触っていません。
可能そうではあるので、大量にカスタムソース経由でのキャプチャをしなくてはならない場合は自作の検討もできます。

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

Unityでシリアル通信する際に試したこと

Unityでシリアル通信する際に試したことを、メモとして残します。

開発環境

  • OS: Windows 8.1

構成

  • micro:bitのUSBポートからUARTを送信(19200bps)
  • Unityでは、System.IO.Portsライブラリを使ってUARTを受信
    • Unityからmicro:bitへの送信は行っていない
  • System.IO.Ports.dllはこちらの記事を参考に、ダウンロード&プラグイン
  • こちらのWebサイトを参考に、SerialHandler.csを作成し、Main Cameraにアタッチ
    • ただし、ポートオープン処理にserialPort_.ReadTimeout = 2000;を追記(これを書かないとタイムアウトする)

正常にUART受信ができた条件

Unityのバージョン

  • Unity 2018.3.14f1

.NET関連の設定

[Edit] > [Project Settings...] > Player > Other Settings > Configurationにて、
以下2つのいずれか:

  • 設定1
    • Scripting Runtime Versionが.NET 4.x Equivalent
    • Api Compatibility Levelが.NET 4.x
  • 設定2
    • Scripting Runtime Versionが.NET 3.5 Equivalent
    • Api Compatibility Levelが.NET 2.0

System.IO.Ports.dllのバージョン

以下3つのいずれか:

  • System.IO.Ports.5.0.0-preview.7.20364.11のnet461
  • System.IO.Ports.5.0.0-preview.7.20364.11のnetstandard2.0
  • System.IO.Ports.4.4.0のnetstandard2.0

正常にUART受信ができなかった条件

Unityのバージョン

  • Unity 2019.4.7f1

.NET関連の設定

[Edit] > [Project Settings...] > Player > Other Settings > Configurationにて、
以下2つのいずれも:

  • 設定1
    • Api Compatibility Levelが.NET 4.x
  • 設定2
    • Api Compatibility Levelが.NET Standard 2.0

System.IO.Ports.dllのバージョン

  • System.IO.Ports.5.0.0-preview.7.20364.11のnet461

予想

  • Unityのバージョンが新しいと、System.IO.Portsが正常に動かない
    • Scripting Runtime Versionが設定できるかどうかが鍵?
  • System.IO.Ports.dllのバージョンは関係がない

余談

複数のバージョンのUnityをインストールする際に、こちらのWebサイトを参考にしました。

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

UWAのLua CPUコスト分析サポート

現在、開発者にソースコードのHotFix機能を提供する最も標準的なソリューションがLuaではないでしょうか。Luaの性能改善は、開発者にとってますます重要になってきています。すでにUWA GOT OnlineでLuaメモリ診断の機能をできるようになっていますが、これに加えてLuaのCPUコストの分析機能を追加しました。 UWAホームページに登録すれば、すぐ 「Lua性能診断」を確認できます。
2.png
下図はUWAウェブサイトにあるデモデータ,すでにUWA GOT Onlineサービスで見たことあるかもしれないですが、最新のLua性能分析画面で、分析項目としての「コードの実行効率」の「CPU使用時間」で、一番処理時間かかっているTop10の関数がリストアップされています。
3.png
これらの関数をクリックすると、関数の全体のCPU時間、指定シーンの処理時間及び任意のフレームでの処理時間などの情報を確認できるようになり、開発者が速くボトルネックのポジショニングできるようになります。

UWA GOT OnlineはこれでLuaコードの全面的なCPU処理時間分析、メモリ分析ができるようになります。また、診断の深さや全面的なメモリ割当情報に基づいて、開発者が直接ボトルネック問題を特定し、速く性能改善やゲームの実行効率向上に役に立ちます。
1.png
ご注意頂きたいのは,Luaモードでの診断対象はLua関数のみですが、Luaに割り当てられたC#関数も含まれています。


UWA Technologyは、モバイル/VRなど様々なゲーム開発者向け、パフォーマンス分析最適化ソリューション及びコンサルティングサービスを提供している会社でございます。

UWA公式サイト:https://jp.uwa4d.com
UWA公式ブログ:https://blog.jp.uwa4d.com

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

Fixed Joint2Dの解説

今回はFixed Joint2Dについて解説していきます。

Fixed Joint2Dとは

Fixed Joint2Dとは、2つのオブジェクトの相対位置を固定する機能です。
Fixedの意味は「修繕」や「所定」など、
Fixed Jointになると「固定関節」になるそうです。

Fixed Joint2Dの各パラメータ

FixedJoint2D_param.png
やっぱりパラメータが多いですね。
どうやらJoint2Dと名の付くものは「Joint2D」や「AnchoredJoint2D」といったクラスを継承しており、似通ったパラメータになっているそうです。
なので、今回は少し解説を省こうと思います。

・Auto Configure Connected Anchor

ここのチェックボックスにチェックを入れると、下のConnected Anchorが自動的に決定されます。
Distance Joint2Dではこれを使うのはオススメしませんでしたが、Fixed Joint2Dではこれを使うことをオススメします。Fixed Joint2DのConnected Anchorはプロジェクトを実行するとAnchorと同じ場所に移動し、アタッチしたオブジェクトもそれにつられて移動してしまうからです。ちょっと言葉では理解しにくいと思いますが、実際にやってみたら分かると思います。

・Frequency

Damping Ratioの前にFrequencyの解説をします。
簡単に言うと関節の硬さです。実際には、力を加えたときに1秒間に何回振動するか、つまり周波数のパラメータです。
数字が小さいほど柔らかくなりますが、0になると完全に固まってしまいます

・Damping Ratio

振動の幅がどのくらいの割合で減っていくかを決める場所です。
0~1までの値をとり、1に近づくほど振動幅が減りやすくなります。
注意してほしい点は、0でもある程度は振動幅が減っていくということです。

・Break Torque

Fixed Joint2Dを破壊するのに必要な回転力を決めるところです。
infinityと入れると絶対に破壊されません。
また、infinityに戻したいときはinfと入力してください。

使い道

私が思いついたのは、チューチュー(2つに折って食べるアイス)を折るゲームです。
あとはプールの飛び込み台など、けっこう色んなところに使えるかと思います。

おわりに

UnityマニュアルのFixed Joint2Dを見てみたら、何かRelative Joint2Dと比較されていたので次回はRelative Joint2Dの解説をしたいと思います。
もしかしたらFixed Joint2DとRelative Joint2Dの比較記事になってしまうかもしれませんが、それもいいかもしれませんね。

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

UVミラーリングを使ってテクスチャの表示をする

概要

画像を反転してループさせることによって半分に切り取られたリソースを1枚のリソースとして完成させる

これを
kusa2.png

こう
kusa.png

シェーダーを書く

※テクスチャの設定はrepeatであることを前提とする

テクスチャを通常表示する場合

UV座標は 0 ~ 1 で入力される

float2 uv = input.uv;

スクリーンショット 2020-08-07 13.59.10.png

テクスチャを反転したい場合

入力されるUV座標を 1 ~ 0 に変換するだけ
今回はUのみ反転させる

float2 uv = 1 - input.uv.x;

スクリーンショット 2020-08-07 14.02.00.png

通常表示と反転表示を組み合わせる

半分のリソースを1枚のリソースとして表示したい場合
通常表示のテクスチャと反転表示のテクスチャを組み合わせることで可能になりそう
0 ~ 1 で通常表示
1 ~ 0 で反転表示
ということがわかったので、UV座標をなんとかして 1 ~ 0 ~ 1 という数値にしてあげたい

まずはUV座標の値を変更する

以下計算式を使うと UV座標が 0 ~ 1 → -1 ~ 1 に変換できる

uv.x = uv.x * 2 - 1

これでUV座標は -1 ~ 1 に変換されたが、マイナスの値が出てしまっているためこのままではダメ

変更したUVの値から負の数を取り除く

UV座標の変換のタイミングで絶対値を求める関数を使う

uv.x = abs(uv.x * 2 - 1)

こうすることによって

-1 ~ 1 に変換された値を 1 ~ 0 ~ 1 という値に変換することが可能になる

結果

スクリーンショット 2020-08-07 14.43.24.png

あとは画像のサイズを綺麗に調整したりして完成

お わ り

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

Composite Collider2Dが使えないときの解決方法

子オブジェクトからRigidbody2Dを外す

Composite Collider2Dを使おうとしたら、親オブジェクトのInspectorに
CompositeCollider_Comment_Parent.png

"Outline geometry is composed of edges and will not preserve the original collider's center-of-mass or rotational inertia. The CompositeCollider2D is attached to a Dynamic Rigidbody 2D so you may need to explicitly set there if they are required."

という警告のような文章が出てきて、子オブジェクトのInspectorには
CompositeCollider_Alart_Child.png

"This collider will not function with a composite until there is a CompositeCollider2D on the GameObject that attached Rigidbody2D is on."

という警告が出てきたときの解決法は、
「子オブジェクトからRigidbody2Dを外す」こと
です。
親オブジェクトにRigidbody2Dがすでにあるはずなので子オブジェクトにはRigidbody2Dは要らないということですね。

おわりに

子オブジェクトのRigidbody2Dをはずしていなかったことが原因でComposite Collider2Dが使えずに挫折した人も多いかもしれません。この記事が少しでもそういう人たちに読んでもらえたらとても嬉しいです。
私も最初は挫折したうちの一人でした。ですが、ゲーム制作をしている途中でどうしてもComposite Collider2Dを使いたい場面が出てきて、そこで色々と試してこの問題を解決することができました。
最初はInspector内に出てきた警告文をGoogle翻訳で解読しようとしましたが、意味不明な文章にしかならず、翻訳せずに直接検索してみても欲しい答えが見つからず、とても苦労した思い出があります。
誰かにきけばすぐに分かったかもしれませんが、私にはそんな当ても勇気もなくただただ一人で抱え込んでいました。
そんな人のために私はこの記事を書きました。

もしここまで読んで、何か文句があるなら、ここまで読んだ自分自身を責めてください。
もしここまで読んで、共感してくださったなら、ありがとうございます。

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

UnityでRTSPビデオストリーミング再生

はじめに

RTSP配信された映像をUnityでストリーミングしたい場合があります。
しかし、Unityには標準でRTSP再生をサポートしているコンポーネントはありません。
今回はgujadot氏のRTSP_Unity_Pluginを用いてUnityにおいてRTSPストリーミングを導入する方法を解説します。

用いるソフトウェア

実装手順

DLL生成編

  1. Visual StudioにVisual C++をインストールしておく。
  2. FFmpegのダウンロードページにアクセスする。dev/shareの両方をダウンロード。
  3. RTSP_Unity_Pluginをダウンロード。RTSPUnityPlugin.vcxproj(VC++ Project)をVisual Studioで開く。
  4. プロジェクトフォルダ内にffmpegフォルダを作成する。以下のような構成になる。
  5. 作成したffmpeg以下にffmpeg/include、ffmpeg/libの2フォルダを作成する。
    各フォルダに次の通り、ダウンロードしたFFmpegの内容をコピーする。
    ffmpeg/include <- copy <- FFmpeg/win64-dev/include
    ffmpeg/lib <- copy <- FFmpeg/win64-dev/lib と FFmpeg/win64-shared/bin
  6. Visual StudioのエクスプローラからRTSPUnityPluginのプロパティを開く。
  7. [構成プロパティ]-[C/C++]-[全般] を開く。
  8. [追加のインクルードディレクトリ]に以下を入力。

    $(SolutionDir)\ffmpeg\include;%(AdditionalIncludeDirectories)
    
  9. [追加の#usingディレクトリ]に以下を入力。

    $(SolutionDir)\ffmpeg\lib
    
  10. [構成プロパティ]-[リンカー]-[全般] を開く。

  11. [追加のライブラリディレクトリ]に以下を入力。

    $(SolutionDir)\ffmpeg\lib;%(AdditionalLibraryDirectories)
    
  12. [構成プロパティ]-[デバッグ] を開く。

  13. [コマンド]に以下を入力。

    $(TargetPath)
    
  14. [構成プロパティ]-[リンカー]-[入力] を開く。

  15. [追加の依存ファイル]に以下を入力。

    avcodec.lib
    avdevice.lib
    avfilter.lib
    avformat.lib
    avutil.lib
    postproc.lib
    swresample.lib
    swscale.lib
    
  16. [構成プロパティ]-[ビルドイベント]-[リンク前のイベント] を開く。

  17. [コマンドライン]に以下を入力。

    copy "$(SolutionDir)ffmpeg\lib\avcodec-57.dll" "$(TargetDir)"
    copy "$(SolutionDir)ffmpeg\lib\avdevice-57.dll" "$(TargetDir)"
    copy "$(SolutionDir)ffmpeg\lib\avfilter-6.dll" "$(TargetDir)"
    copy "$(SolutionDir)ffmpeg\lib\avformat-57.dll" "$(TargetDir)"
    copy "$(SolutionDir)ffmpeg\lib\avutil-55.dll" "$(TargetDir)"
    copy "$(SolutionDir)ffmpeg\lib\postproc-54.dll" "$(TargetDir)"
    copy "$(SolutionDir)ffmpeg\lib\swresample-2.dll" "$(TargetDir)"
    copy "$(SolutionDir)ffmpeg\lib\swscale-4.dll" "$(TargetDir)"
    
  18. ビルドする。

以上の手順を踏むと、build\x64\Release\RTSPUnityPlugin.dllが生成されます。

Unity編

DLLの配置

上記の手順で生成されたReleaseフォルダをAssets/Plugins下に配置する。

オブジェクトへの適用

ストリームしたいマテリアルのオブジェクトに次のスクリプトをアタッチする。
(setRTSPTexture.cs)

setRTSPTexture.cs
using UnityEngine;
using System;
using System.Collections;
using System.Runtime.InteropServices;


public class SetRTSPTexture : MonoBehaviour {

    [DllImport("RTSPUnityPlugin")]
    private static extern void SetTimeFromUnity(float t);
    [DllImport("RTSPUnityPlugin")]
    private static extern IntPtr GetRenderEventFunc();
    [DllImport("RTSPUnityPlugin")]
    private static extern void SetTextureAsRTSPSink(string rtsp_uri,System.IntPtr texture, int h, int w);


    public string g_rtspUri = "rtsp://localhost:8554/stream";

    IEnumerator Start()
    {
        CreateTextureAndPassToPlugin();
        yield return StartCoroutine("CallPluginAtEndOfFrames");
    }

    private void CreateTextureAndPassToPlugin()
    {
        // Create a texture
        Texture2D tex = new Texture2D(256, 256, TextureFormat.RGBA32, false);
        // Set point filtering just so we can see the pixels clearly
        tex.filterMode = FilterMode.Point;
        // Call Apply() so it's actually uploaded to the GPU
        tex.Apply();

        // Set texture onto our material
        GetComponent<Renderer>().material.mainTexture = tex;

        // Pass texture pointer to the plugin
        SetTextureAsRTSPSink(g_rtspUri, tex.GetNativeTexturePtr(), tex.width, tex.height);
    }

    private IEnumerator CallPluginAtEndOfFrames()
    {
        while (true)
        {
            // Wait until all frame rendering is done
            yield return new WaitForEndOfFrame();

            // Set time for the plugin
            SetTimeFromUnity(Time.timeSinceLevelLoad);

            // Issue a plugin event with arbitrary integer identifier.
            // The plugin can distinguish between different
            // things it needs to do based on this ID.
            // For our simple plugin, it does not matter which ID we pass here.
            GL.IssuePluginEvent(GetRenderEventFunc(), 1);
        }
    }
}

これでUnityを実行すると、RTSPを受信してオブジェクトのテクスチャで再生されます。

最後に

このプラグインを用いればTHETAなどのネットワークカメラから映像をライブ配信できるので、Unityでビデオチャットのような面白い機能が実現できそうです。素晴らしいプラグインを提供してくださったgujadot氏に感謝!

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

UnityのTouchScriptでシングルタップとダブルタップを取得する

概要

Unityで高機能なタッチ処理を提供してくれるTouchScriptで、シングルタップとダブルタップを取得する設定です。
別オブジェクトに設定するのは簡単ですが、同じオブジェクトがシングルタップとダブルタップの両方に対応するのは少し厄介だったので紹介します。
(公式のサンプルも別オブジェクトに設定していた・・・)

環境

Unity 2019.2.16.f1
TouchScript 9.0

設定方法

Inspector設定

今回はuGUIでImageを作り、Imageがシングルタップされた時とダブルタップされた時で別の処理をします。
inspector1.png
RectTransformの値は適当

inspector2.png
TapGestureの詳細
シングルタップ用とダブルタップ用で2つのTapGestureをアタッチします。

  • シングルタップ用TapGestureの設定
    • Number of Taps RequiredをOneにする
    • Require Other Gesture to FailのチェックをONにして、Valueにダブルタップ用のTapGestureを指定する
      • ※自分の環境だとONにしてすぐコンポーネントをドラッグしてもセット出来ない謎現象が起きました
      • ※自分自身のオブジェクトをHierarchyからドラッグ、ゲームを再生してすぐ停止、コンポーネントをドラッグという謎点順で行けました・・・
  • ダブルタップ用TapGestureの設定
    • Number of Taps RequiredをTwoにする
    • Limit Timeを0.5にする
      • ダブルタップと判定するしきい値なので、ご自由に調整下さい

スクリプト

using TouchScript.Gestures;
using UnityEngine;

// シングルタップとダブルタップの両方を取得する
public class TouchTest : MonoBehaviour
{
    [SerializeField]
    private GameObject image; // Inspectorで自分自身をセットしておく

    private TapGesture m_tapGesture;
    private TapGesture m_doubleTapGesture;

    // Awake→OnEnable→Startなので、Awakeで設定する
    // そもそもSerializeFieldでそれぞれのコンポーネントを持っててもOK
    private void Awake()
    {
        // 1つめがシングルタップ、2つめがダブルタップに反応する
        var tapGestures = image.GetComponents<TapGesture>();
        m_tapGesture = tapGestures[0];
        m_doubleTapGesture = tapGestures[1];
    }

    // メモリリークを避けるために、OnEnableで有効化してOnDisableで無効化
    private void OnEnable()
    {
        m_tapGesture.StateChanged += HandleTap;
        m_doubleTapGesture.StateChanged += HandleDoubleTap;
    }

    private void OnDisable()
    {
        m_tapGesture.StateChanged -= HandleTap;
        m_doubleTapGesture.StateChanged -= HandleDoubleTap;
    }

    // シングルタップ時のイベント
    private void HandleTap(object sender, System.EventArgs e)
    {
        if (m_tapGesture.State != Gesture.GestureState.Recognized)
        {
            return;
        }

        Debug.Log("tap");
    }

    // ダブルタップ時のイベント
    private void HandleDoubleTap(object sender, System.EventArgs e)
    {
        if (m_doubleTapGesture.State != Gesture.GestureState.Recognized)
        {
            return;
        }

        Debug.Log("doubletap");
    }
}

このスクリプトをImageオブジェクトにアタッチして、SerializeFieldにImageオブジェクト自身をセットすれば、タップとダブルタップを取得出来ると思います。

  • 一回目のタップから[Limit Time]秒経過→シングルタップイベント
  • 一回目のタップから[Limit Time]秒経過前に二回目のタップ→ダブルップイベント

になるので、Limit Timeは使いやすいと思う値に設定して下さい。

参考文献

https://github.com/TouchScript/TouchScript/issues/217

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

VSCodeでUnityのIntelisenseが効かなくなった時の対応

Unity x VSCodeで開発をしていてIntelisenseが効かなくて困った時の対応です

環境

Unity 2020.1.1f1
VSCode 1.47.3

対応

  1. UnityのEigt -> Preferences -> External Tools
  2. "Generate.csproj files for:"のチェックを全て外して"Regenerate Project Files"を選択
  3. リロードする
    VSCode上で"Ctrl+Shift+P"を押下し"Developer:Reload Window"でOK

おわり

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

UnityのSteamVR plugin(2.6.1)を使ってVRChatぽい移動を再現(Valve Index)

 Unity上でネイティブVR空間を作ってみようと思いました。ちなみに空間を作り上げてVR体験するだけだったら、VRChatclusterでワールドを作った方が手軽だし共有もしやすいのでおすすめです。

  • Unity Editor ... 2019.2.21f1
  • SteamVR plugin ... 2.6.1
  • 使用デバイス ... Valve Index

まずはプロジェクト作成

image.png

SteamVR pluginのimport

image.png

image.png

 よくわからないけど脳死で「Accept All」を押します。(いいのか?)

image.png

 以降、ずっと出てくるこのウィンドウは無視します。特に弊害がないので。(いいのか?)

カメラプリファブの設置

image.png

 Assets > SteamVR > Prefabsにある[Camera Rig]のプリファブをヒエラルキーに置きます。これがプレイヤーの頭となります。元からあったMain Cameraは削除します。

image.png

 これは本質的には必要ない作業ですが、地平線だけだと味気ないので、PlaneとSphereを設置しています。

 再生ボタンを押します。

image.png

 何も考えずにYesを押します。

image.png

 何も考えずにSave and Generateを押します。

image.png

 Save

image.png

 もう一度再生ボタンを押せば、これだけでもうVR空間の中を歩き回れます。しかし、コントローラで歩き回れるようにもしたいです。

コントローラの設定

image.png

 ここでSteamVR Input

image.png

 これらの文字の羅列の意味は後で分かってきますので、とりあえずOpen building UI

image.png

 編集。

image.png

 ここで、先程の文字の羅列が、コントローラの入力と対応したものであったことがわかります。VRChatライク(非Holoport)な移動をするためには、十字スティックの入力を2次元座標として受け取る必要がありますが、デフォルトの配列ではそうはなっていないようなので、追加します。

image.png

Thumb Stickの+ボタンを押して「ジョイスティック」を追加します。

image.png

 ここで「位置」にアクションを割り当てる……と行きたいところですが、何も選択できない状態になっているはずです。位置のデータ形式はVector2ですが、Vector2を入力として受け取るアクションが用意されていないためです。という訳で、先程のSteamVR Inputに戻ってコマンドを追加します。

image.png

image.png

 +ボタンでアクションを追加し、適当な名前にします(ここでは仮にMoveとしています)。また、Typevector2にします(ここ重要)。変更が終わったらSave and generateを忘れずに。

image.png

 今度は選択できるようになりました。

image.png

image.png

 割り当てた後は、「デフォルトバインドの置換」から「保存」を忘れずに。

スクリプトの作成

 下のようなスクリプトを用意します。ここでは仮にMove.csとしておきます。

using UnityEngine;
using Valve.VR;

public class Move : MonoBehaviour
{
    private SteamVR_Action_Vector2 move = SteamVR_Actions._default.Move;
    float MoveSpeed = 0.1f;

    private void Start()
    {
    }

    private void Update()
    {
        Vector2 pos = move.GetLastAxis(SteamVR_Input_Sources.LeftHand);
        transform.position += transform.forward * pos.y * MoveSpeed;
        transform.position += transform.right * pos.x * MoveSpeed / 5;
    }
}

image.png

 [CameraRig]にアタッチします。

 これで左コントローラを動かせばその通りにカメラ(=プレイヤー)が動くようになったと思われますが、少し不自然だと思います。なぜかというと、頭の向きと進行方向が一致していないからです。[CameraRig]はあくまで体の向きであり、実際の頭の向きはその下にあるCameraというオブジェクトだからです。ちゃんと見ている方向を基準にするべく、以下のようなコードにします。

using UnityEngine;
using Valve.VR;

public class Move : MonoBehaviour
{
    private SteamVR_Action_Vector2 move = SteamVR_Actions._default.Move;
    float MoveSpeed = 0.1f;
    private GameObject Head;

    private void Start()
    {
        Head = GameObject.Find("Camera");
    }

    private void Update()
    {
        Vector2 pos = move.GetLastAxis(SteamVR_Input_Sources.LeftHand);
        this.transform.position += Head.transform.forward * pos.y * MoveSpeed;
        this.transform.position += Head.transform.right * pos.x * MoveSpeed / 5;
    }
}

 しかし、これでもまだ片手落ちで、これだと頭が少しでも見上げていたり見下ろしていたりすると空中方向の移動が発生してしまいます。コライダーとか重力の処理の話はおいといて、とりあえずこのサンプルをそれっぽく動かすために、y座標は常に0にするように追加処理を加えます。

using UnityEngine;
using Valve.VR;

public class Move : MonoBehaviour
{
    private SteamVR_Action_Vector2 move = SteamVR_Actions._default.Move;
    float MoveSpeed = 0.1f;
    private GameObject Head;

    private void Start()
    {
        Head = GameObject.Find("Camera");
    }

    private void Update()
    {
        Vector2 pos = move.GetLastAxis(SteamVR_Input_Sources.LeftHand);
        transform.position += Head.transform.forward * pos.y * MoveSpeed;
        transform.position += Head.transform.right * pos.x * MoveSpeed / 5;
        Vector3 loc = transform.position;
        loc.y = 0;
        transform.position = loc;
    }
}

回転方向の刻み

 ジョイスティックだけだと方向転換ができないので、VRChatライクに右コントローラのジョイスティックに回転を割り当てます。

image.png

 ジョイスティックの左入力と右入力についてはデフォルトで設定があるので、それをそのまま使います。以下のようなコードにします。

using UnityEngine;
using Valve.VR;

public class Move : MonoBehaviour
{
    private SteamVR_Action_Vector2 move = SteamVR_Actions._default.Move;
    private SteamVR_Action_Boolean left = SteamVR_Actions._default.SnapTurnLeft;
    private SteamVR_Action_Boolean right = SteamVR_Actions._default.SnapTurnRight;
    float MoveSpeed = 0.1f;
    private GameObject Head;

    private void Start()
    {
        Head = GameObject.Find("Camera");
    }

    private void Update()
    {
        Vector2 pos = move.GetLastAxis(SteamVR_Input_Sources.LeftHand);
        transform.position += Head.transform.forward * pos.y * MoveSpeed;
        transform.position += Head.transform.right * pos.x * MoveSpeed / 5;
        Vector3 loc = transform.position;
        loc.y = 0;
        transform.position = loc;

        if (left.GetStateDown(SteamVR_Input_Sources.RightHand))
        {
            transform.Rotate(new Vector3(0, -20, 0));
        }

        if (right.GetStateDown(SteamVR_Input_Sources.RightHand))
        {
            transform.Rotate(new Vector3(0, 20, 0));
        }
    }
}

 これでだいぶ直感的に動けるようになりました。

なぜこの記事を書いたか

  • 同じことを解説しているページはあったが、ジョイスティックでの移動をシンプルに実装するという点で優位性があると思った
  • バージョンが細かく分かれており、2.6.1で書いている記事はなかった
  • Indexで書いている記事もなかった

お世話になった記事

https://framesynthesis.jp/tech/unity/htcvive/
https://qiita.com/sakano/items/d87a9b11c23a9bbe166f

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