- 投稿日:2020-08-07T21:54:29+09:00
Unity Recorderにカスタムソースを追加する( feat. ADX2 )
Unity Recorderにカスタムの録音・録画ソースを追加したい!
お題がだいぶレアケース ですが、Unityのエディター上で動画や音声を記録できるUnity Recorderにカスタムのソースを追加したい場合のガイドです。
CRI ADX2はUnityに対応した統合型サウンドミドルウェアです。Unity内部のサウンドシステムとは完全に別のライブラリで処理されるため、通常のUnity Recorderの機能では音が記録できません。
そこで、Unity Recorderにカスタムな録音ソースを追加して対応しました。下図のように、Recordersの要素として「ADX2 Audio」を追加し、動画と一緒に録音できるようにしています。
パッケージは以下に公開していますので、機能を使いたい人はこちらからどうぞ。
ADX2 for Unity Recorder
https://github.com/TakaakiIchijo/ADX2forUnityRecorder世の中にUnity Recorderに新しいソースを追加したいと思う人がどれだけいるかは不明なのですが、このパッケージの開発に結構手間取ったので記録します。
(機能拡張に関するドキュメントは見当たらなかったので、ソースを読んで仕組みを把握しました。)今回のADX2対応ケース以外には、たとえば「XR系の特殊な描画部分を録画したい」「複数ソースの音声をミックスしてから録音したい」あたりが当該すると思います。
なお、ADX2の録音部分については以下の記事で解説しています。
Unity + ADX2環境でゲーム中の音を録音する
https://qiita.com/Takaaki_Ichijo/items/faf96c22740ff5a7587dUnity 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.csusing 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.csusing 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.csusing 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.csusing 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のソースにかなり手を入れないといけないため、今回は触っていません。
可能そうではあるので、大量にカスタムソース経由でのキャプチャをしなくてはならない場合は自作の検討もできます。
- 投稿日:2020-08-07T18:42:04+09:00
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サイトを参考にしました。
- 投稿日:2020-08-07T16:50:19+09:00
UWAのLua CPUコスト分析サポート
現在、開発者にソースコードのHotFix機能を提供する最も標準的なソリューションがLuaではないでしょうか。Luaの性能改善は、開発者にとってますます重要になってきています。すでにUWA GOT OnlineでLuaメモリ診断の機能をできるようになっていますが、これに加えてLuaのCPUコストの分析機能を追加しました。 UWAホームページに登録すれば、すぐ 「Lua性能診断」を確認できます。
下図はUWAウェブサイトにあるデモデータ,すでにUWA GOT Onlineサービスで見たことあるかもしれないですが、最新のLua性能分析画面で、分析項目としての「コードの実行効率」の「CPU使用時間」で、一番処理時間かかっているTop10の関数がリストアップされています。
これらの関数をクリックすると、関数の全体のCPU時間、指定シーンの処理時間及び任意のフレームでの処理時間などの情報を確認できるようになり、開発者が速くボトルネックのポジショニングできるようになります。UWA GOT OnlineはこれでLuaコードの全面的なCPU処理時間分析、メモリ分析ができるようになります。また、診断の深さや全面的なメモリ割当情報に基づいて、開発者が直接ボトルネック問題を特定し、速く性能改善やゲームの実行効率向上に役に立ちます。
ご注意頂きたいのは,Luaモードでの診断対象はLua関数のみですが、Luaに割り当てられたC#関数も含まれています。
UWA Technologyは、モバイル/VRなど様々なゲーム開発者向け、パフォーマンス分析と最適化ソリューション及びコンサルティングサービスを提供している会社でございます。
UWA公式サイト:https://jp.uwa4d.com
UWA公式ブログ:https://blog.jp.uwa4d.com
- 投稿日:2020-08-07T16:15:15+09:00
Fixed Joint2Dの解説
今回はFixed Joint2Dについて解説していきます。
Fixed Joint2Dとは
Fixed Joint2Dとは、
2つのオブジェクトの相対位置を固定する
機能です。
Fixedの意味は「修繕」や「所定」など、
Fixed Jointになると「固定関節」になるそうです。Fixed Joint2Dの各パラメータ
やっぱりパラメータが多いですね。
どうやら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の比較記事になってしまうかもしれませんが、それもいいかもしれませんね。
- 投稿日:2020-08-07T14:45:33+09:00
UVミラーリングを使ってテクスチャの表示をする
概要
画像を反転してループさせることによって半分に切り取られたリソースを1枚のリソースとして完成させる
シェーダーを書く
※テクスチャの設定はrepeatであることを前提とする
テクスチャを通常表示する場合
UV座標は 0 ~ 1 で入力される
float2 uv = input.uv;テクスチャを反転したい場合
入力されるUV座標を 1 ~ 0 に変換するだけ
今回はUのみ反転させるfloat2 uv = 1 - input.uv.x;通常表示と反転表示を組み合わせる
半分のリソースを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-07T13:31:25+09:00
Composite Collider2Dが使えないときの解決方法
子オブジェクトからRigidbody2Dを外す
Composite Collider2Dを使おうとしたら、親オブジェクトのInspectorに
"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には
"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翻訳で解読しようとしましたが、意味不明な文章にしかならず、翻訳せずに直接検索してみても欲しい答えが見つからず、とても苦労した思い出があります。
誰かにきけばすぐに分かったかもしれませんが、私にはそんな当ても勇気もなくただただ一人で抱え込んでいました。
そんな人のために私はこの記事を書きました。もしここまで読んで、何か文句があるなら、ここまで読んだ自分自身を責めてください。
もしここまで読んで、共感してくださったなら、ありがとうございます。
- 投稿日:2020-08-07T13:10:06+09:00
UnityでRTSPビデオストリーミング再生
はじめに
RTSP配信された映像をUnityでストリーミングしたい場合があります。
しかし、Unityには標準でRTSP再生をサポートしているコンポーネントはありません。
今回はgujadot氏のRTSP_Unity_Pluginを用いてUnityにおいてRTSPストリーミングを導入する方法を解説します。用いるソフトウェア
- Unity 2019.2.8f1
- Visual Studio 2017
- RTSP_Unity_Plugin
- FFmpeg
実装手順
DLL生成編
- Visual StudioにVisual C++をインストールしておく。
- FFmpegのダウンロードページにアクセスする。dev/shareの両方をダウンロード。
- RTSP_Unity_Pluginをダウンロード。RTSPUnityPlugin.vcxproj(VC++ Project)をVisual Studioで開く。
- プロジェクトフォルダ内に
ffmpeg
フォルダを作成する。以下のような構成になる。- 作成した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
- Visual StudioのエクスプローラからRTSPUnityPluginのプロパティを開く。
- [構成プロパティ]-[C/C++]-[全般] を開く。
[追加のインクルードディレクトリ]に以下を入力。
$(SolutionDir)\ffmpeg\include;%(AdditionalIncludeDirectories)[追加の#usingディレクトリ]に以下を入力。
$(SolutionDir)\ffmpeg\lib[構成プロパティ]-[リンカー]-[全般] を開く。
[追加のライブラリディレクトリ]に以下を入力。
$(SolutionDir)\ffmpeg\lib;%(AdditionalLibraryDirectories)[構成プロパティ]-[デバッグ] を開く。
[コマンド]に以下を入力。
$(TargetPath)[構成プロパティ]-[リンカー]-[入力] を開く。
[追加の依存ファイル]に以下を入力。
avcodec.lib avdevice.lib avfilter.lib avformat.lib avutil.lib postproc.lib swresample.lib swscale.lib[構成プロパティ]-[ビルドイベント]-[リンク前のイベント] を開く。
[コマンドライン]に以下を入力。
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)"ビルドする。
以上の手順を踏むと、
build\x64\Release\RTSPUnityPlugin.dll
が生成されます。Unity編
DLLの配置
上記の手順で生成されたReleaseフォルダをAssets/Plugins下に配置する。
オブジェクトへの適用
ストリームしたいマテリアルのオブジェクトに次のスクリプトをアタッチする。
(setRTSPTexture.cs)setRTSPTexture.csusing 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氏に感謝!
- 投稿日:2020-08-07T11:12:33+09:00
UnityのTouchScriptでシングルタップとダブルタップを取得する
概要
Unityで高機能なタッチ処理を提供してくれるTouchScriptで、シングルタップとダブルタップを取得する設定です。
別オブジェクトに設定するのは簡単ですが、同じオブジェクトがシングルタップとダブルタップの両方に対応するのは少し厄介だったので紹介します。
(公式のサンプルも別オブジェクトに設定していた・・・)環境
Unity 2019.2.16.f1
TouchScript 9.0設定方法
Inspector設定
今回はuGUIでImageを作り、Imageがシングルタップされた時とダブルタップされた時で別の処理をします。
RectTransformの値は適当
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は使いやすいと思う値に設定して下さい。
参考文献
- 投稿日:2020-08-07T09:18:04+09:00
VSCodeでUnityのIntelisenseが効かなくなった時の対応
- 投稿日:2020-08-07T00:58:26+09:00
UnityのSteamVR plugin(2.6.1)を使ってVRChatぽい移動を再現(Valve Index)
Unity上でネイティブVR空間を作ってみようと思いました。ちなみに空間を作り上げてVR体験するだけだったら、VRChatかclusterでワールドを作った方が手軽だし共有もしやすいのでおすすめです。
- Unity Editor ... 2019.2.21f1
- SteamVR plugin ... 2.6.1
- 使用デバイス ... Valve Index
まずはプロジェクト作成
SteamVR pluginのimport
よくわからないけど脳死で「Accept All」を押します。(いいのか?)
以降、ずっと出てくるこのウィンドウは無視します。特に弊害がないので。(いいのか?)
カメラプリファブの設置
Assets > SteamVR > Prefabs
にある[Camera Rig]
のプリファブをヒエラルキーに置きます。これがプレイヤーの頭となります。元からあったMain Camera
は削除します。これは本質的には必要ない作業ですが、地平線だけだと味気ないので、PlaneとSphereを設置しています。
再生ボタンを押します。
何も考えずに
Yes
を押します。何も考えずに
Save and Generate
を押します。
Save
。もう一度再生ボタンを押せば、これだけでもうVR空間の中を歩き回れます。しかし、コントローラで歩き回れるようにもしたいです。
コントローラの設定
ここで
SteamVR Input
。これらの文字の羅列の意味は後で分かってきますので、とりあえず
Open building UI
。編集。
ここで、先程の文字の羅列が、コントローラの入力と対応したものであったことがわかります。VRChatライク(非Holoport)な移動をするためには、十字スティックの入力を2次元座標として受け取る必要がありますが、デフォルトの配列ではそうはなっていないようなので、追加します。
Thumb Stick
の+ボタンを押して「ジョイスティック」を追加します。ここで「位置」にアクションを割り当てる……と行きたいところですが、何も選択できない状態になっているはずです。位置のデータ形式は
Vector2
ですが、Vector2
を入力として受け取るアクションが用意されていないためです。という訳で、先程のSteamVR Input
に戻ってコマンドを追加します。+ボタンでアクションを追加し、適当な名前にします(ここでは仮に
Move
としています)。また、Type
をvector2
にします(ここ重要)。変更が終わったらSave and generate
を忘れずに。今度は選択できるようになりました。
割り当てた後は、「デフォルトバインドの置換」から「保存」を忘れずに。
スクリプトの作成
下のようなスクリプトを用意します。ここでは仮に
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; } }
[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ライクに右コントローラのジョイスティックに回転を割り当てます。
ジョイスティックの左入力と右入力についてはデフォルトで設定があるので、それをそのまま使います。以下のようなコードにします。
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