- 投稿日:2021-01-06T19:08:12+09:00
【Unity】UI Toolkitのドラッグアンドドロップ実装を分かりやすく解説してみた
【Unity】UI ToolkitのPointerEventBaseのpositionは何を指すのか?
前回執筆したコチラの続きでUI Toolkitでドラッグアンドドロップの実装をして生きます。普通にやると落ちる落とし穴付きで解説しています。ドラッグする対象にイベントを登録
var document = GetComponent<UIDocument>(); var rootVisualElement = document.rootVisualElement; // ドラッグターゲットとなるVisualElementを取得 var dragTarget = rootVisualElement.Q<VisualElement>("dragTarget"); dragTarget.RegisterCallback<PointerDownEvent>(OnPointerDown); dragTarget.RegisterCallback<PointerMoveEvent>(OnPointerMove); dragTarget.RegisterCallback<PointerUpEvent>(OnPointerUp);
- PointerDownEvent
- PointerMoveEvent
- PointerUpEvent
3種のイベントを登録してコールバック関数をセットしています。
PointerMoveEventはMouseDown・MouseUp関わらず、マウスが動くたびに処理が走ってしまうので、
_isPointerDown
変数を用意してMouseDown or MouseUpの状態を保持するようにしました。普通に書くと以下のようなコードになりましたが、これは失敗です。
private bool _isPointerDown = false; private void OnPointerDown(PointerDownEvent evt) { // ドラッグ状態にする _isPointerDown = true; } private void OnPointerMove(PointerMoveEvent evt) { if (_isPointerDown) { var pos = new Vector2(evt.position.x, evt.position.y); var draggedTarget = (VisualElement) evt.target; // ドラッグターゲットの座標を更新 draggedTarget.style.left = pos.x; draggedTarget.style.top = pos.y; } } private void OnPointerUp(PointerUpEvent evt) { // ドラッグ状態解除 _isPointerDown = false; }出来たもの(失敗作)
なんか挙動がおかしい...
- ドラッグ対象の基準点
- イベントの未発火
上記2点問題があるのです。
1.ドラッグ対象の基準点
VisualElementの基準点は左上です。
private bool _isPointerDown = false; private Vector2 _dragOffset; private void OnPointerDown(PointerDownEvent evt) { _isPointerDown = true; var draggedTarget = (VisualElement) evt.target; // MouseDownしたタイミングのドラッグターゲットの座標 var dragTargetPos = new Vector2( dragTarget.resolvedStyle.left, dragTarget.resolvedStyle.top); // MouseDownしたタイミングのMouseの座標 var mouseDownPosition = new Vector2(evt.position.x, evt.position.y); // ドラッグターゲットの調整値を保持 _dragOffset = dragTargetPos - mouseDownPosition; } private void OnPointerMove(PointerMoveEvent evt) { if (_isPointerDown) { var mousePosition = new Vector2(evt.position.x, evt.position.y); // 現在のマウス座標に対してオフセット値を加算して座標を調整 var pos = mouseDownPosition + _dragOffset; var draggedTarget = (VisualElement) evt.target; draggedTarget.style.left = pos.x; draggedTarget.style.top = pos.y; } } private void OnPointerUp(PointerUpEvent evt) { _isPointerDown = false; }出来たもの(失敗その2)
一見うまく動いてそうです。
しかし、マウスを早く動かしてドラッグターゲットよりマウスカーソルをはみ出すと、ドラッグターゲットがマウスについて来なくなりました。
その後MouseDown状態でもないのにマウスにくっついて来るようにもなっています。はい、失敗です。
2.イベントの未発火
原因はイベントの伝播が出来ていないことです。
ドラッグターゲットからマウスを外してもPointerMoveEvent、PointerUpEventは発火してもらわないと困ります。
その後MouseDown状態でもないのにマウスにくっついて来るようにもなっています。
しかしこの状態ではPointerUpEventが発火していません。
そのため内部的にはまだドラッグ状態ということになります。
※ _isPointerDownの更新がされないよってMouseDown状態でなくてもマウスにくっついて来てしまうという状態になるわけです。
CapturePointerを使う
When a VisualElement captures a pointer, all pointer events are sent to the element, regardless of which element is under the pointer.
Unity - Scripting API: UIElements.PointerCaptureHelper.CapturePointerより
早い話が、ポインター先がどのVisualElementだろうがイベント発火するという事です。
MouseDownのタイミングでCapturePointerを実行して、ポインターのキャプチャをします。
※イベント実行時のクリックIDを内部的に保持するprivate void OnPointerDown(PointerDownEvent evt) { // pointerIdをキャプチャ evt.target.CapturePointer(evt.pointerId); // ~~~~ 略 ~~~~すると、MouseMove時にドラッグターゲットからマウスが外れてもイベントは発火し続けます。
MouseDown判定にキャプチャ状態を使用
private void OnPointerMove(PointerMoveEvent evt) { // // MouseDown判定をポインターをキャプチャしているかどうかで判定する if (evt.target.HasPointerCapture(evt.pointerId)) { // ドラッグオブジェクトの座標を更新する処理 // ~~~~ 略 ~~~~MouseUpしたらキャプチャを解除する
private void OnPointerUp(PointerUpEvent evt) { if (evt.target.HasPointerCapture(evt.pointerId)) { // ポインターのキャプチャを解放 evt.target.ReleasePointer(evt.pointerId); } }出来たもの(成功)
意図通りのドラッグアンドドロップが完成しました。
最後に
全体ソースはGistにアップしています。
環境
- Unity2020.2.0f1
- UI Toolkit 1.0.0-Preview.13
- 投稿日:2021-01-06T15:25:37+09:00
Unity Oculus Quest パフォーマンス最適化で便利なツール
年末にOculus Quest 2を購入してUnityで開発を始めました。
まだ絶賛開発中ですが最適化で使った便利なツールたちをサクッと紹介します。作っているもの
Oculus Riftが発売されたときにホラーゲームを作りました。
Questも2世代目になり、Riftで動かしていたクォリティをQuestにどこまで落とし込めるか試してみました。Oculus Storeで無料で配信中です。ちなみにこれはUE4で作りました。
https://www.oculus.com/experiences/rift/1811961072194113/?locale=ja_JP最適化フロー
大まかな手順としては上から順に以下のような流れで行いました。
・OVRメトリックツール
・Unity Profiler
・Unity Frame Debugger
・Render Doc
・OVR GPU Profiler
・Snapdragon ProfilerOVRメトリックツール
まずはOVRメトリックツールを使用してフレームレートやCPU・GPUの負荷をざっくり把握します。
https://developer.oculus.com/documentation/tools/tools-ovrmetricstool/
開発アプリにオーバーレイでパフォーマンスを知ることができます。
利用方法はAPKを開発PCにダウンロードし、adbでQuestにインストールします。Unity Profiler
https://docs.unity3d.com/ja/2018.4/Manual/ProfilerWindow.html
次にUnity Profilerを使用してもう少し詳細な状況を把握します。
主にCPU側の負荷を詳しく見ることができます。描画が重すぎると「Gfx.WaitForRenderThread」という処理がとても長くなることがあります。
これは描画が追いつかず、フレームをスキップした時のCPU Idle時間です。Unity Frame Debugger
https://docs.unity3d.com/ja/2019.4/Manual/FrameDebugger.html
GPUが遅そうな場合まずUnity Frame Debuggerを用いて描画内容をチェックします。
SRP Butcherがうまく動いているか、StaticBatchingの状況を確認できます。Render Doc
続いてRender Docを用いて描画内容をより詳しくチェックします。
開発PCからQuestに接続してフレームキャプチャができます。
Vulkanを使用した場合、APIの知識が無いと把握することが難しいですが生のドローコールやリソースバインド状況をすべてチェックできます。OVR GPU Profiler
https://developer.oculus.com/documentation/tools/tools-ovrgpuprofiler/
OVRメトリックツールを使用中にadbでプロファイル内容を指定するとログが出力できます。
GPUのキャッシュ使用率、ドローコールごとのラスタライザの描画範囲をチェック可能です。
GPUのハードウェアの知識と、Mobileプラットフォーム特有の仕様(タイルレンダリングなど)の知識が必要です。テクスチャキャッシュヒット率が低い場合はテクスチャの解像度を下げたり、リピートさせてキャッシュヒット率を改善させます。
Snapdragon Profiler
https://developer.qualcomm.com/software/snapdragon-profiler
これはGPUベンダーが提供しているプロファイルツールです。
Render Docとほぼ同じですが、ハードウェアのパフォーマンスカウンタなどより詳細な情報を入手可能です。そのほか
USBケーブルを接続しながら開発を長時間行うとサーマルスロットリングが発生する
adb接続のために通常はケーブルを接続しますが、充電も同時に行われるためQuestがアツアツになってします。
温度が上がりすぎるのを防ぐため、Questが温度上昇を検知するとCPUやGPUのクロックが下がります。
対策としてはadb接続にWifiを用いてワイヤレスにするとか濡れティッシュをかぶせて放熱対策をします。
Wifiでadb接続する忘れがちなテンプレUnity設定
プロジェクトを作り直すたびに忘れがち。
・テクスチャフォーマットをASTCにする
→ ETC2より品質が良いです。よりテクスチャサイズを削減することができます。
・ライトマップ最大解像度を4096にする
→ ライトマップが分割されているとリソースの切り替えが増えます。テクスチャアトラスと同様の原理です。
・XR設定のレンダリングモードをMultiViewにする
→ シングルパスステレオレンダリングを有効にします。そうでないと両目の描画を別々に行いドローコールが2倍になります。
・Player設定のGraphics APIをVulkanのみにする
→ OpenGL ESよりCPUオーバーヘッドが少ないです。OpenGLESにできてVulkanにできない機能はありません。
・Player設定のGraphics JobとMulti Thread Renderingを有効にする
→ メニーコアの利点をより生かすために複数スレッドに描画コマンドの積み込みを分散させます。
・カメラのFarClipを標準の1000mから可能な限り小さくする
→ カリング効率を上げるため。どうしても厳しい場合Fogを利用してFarClipの切れ目をごまかします。
・アンチエイリアスにMSAAx4を設定し、OVRManagerのUse Recommended MSAA Levelを有効にする
→ OVRManagerでMSAAレベルを操作できるようにします。ざっくり過ぎましたが、開発が進みましたらより詳細な情報を記事にしたいと思います。
- 投稿日:2021-01-06T12:52:35+09:00
【Unity】UI ToolkitのPointerEventBase<T>のpositionは何を指すのか?
ドラッグアンドドロップを実装をするとき、タップまたはクリックした座標が必要になります。
今回は登録したイベントからタップした座標を取得するという事を
UI Toolkit
でやっていきます。PointerEventBase<T>のおさらい
PointerEventBase<T>とは何か?
UI Toolkitで使用されるPointerHOGEHOGEというイベントクラスの親クラスです。
- PointerDownEvent
- PointerMoveEvent
- PointerUpEvent
例えばドラッグアンドドロップを実装する上で使用される上記のクラスはPointerEventBase<T>を継承しています。1
PointerEventBase<T>のpositionプロパティが何を指すのか
ここからが本題です。
/// <summary> /// Gets the pointer position in the Screen or World coordinate system. /// </summary> public Vector3 position { get; protected set; }送られたイベントのpositionプロパティは、コメントアウトにある通りスクリーンまたはワールド座標に変換した座標が帰ってきます。
図の用な感じで左上が(0,0)、右下が(maxX, maxY)という事になります。maxXとmaxYは何?
UIDocument
が参照しているPanelSettings
。
ReferenceResolution
の値が画面右下部分の座標になります。まとめ
- PointerEventBase<T>のpositionはタップしたスクリーン上の座標の事
- スクリーンのサイズはPanel SettingsのReferenceResolutionである
これでドラッグアンドドロップは実装できそうですね。
イベントは他にも存在します ↩
- 投稿日:2021-01-06T11:03:55+09:00
Unity 2019.4 でコマンドラインから sln, csproj ファイルを作成する。
目的
CI から sln ファイルを扱いたかったのですが、通常 Unity プロジェクトの git リポジトリに sln ファイルは含めないです。
コマンドラインから sln ファイルを作成することで CI で sln ファイルを扱うことが出来るようになります。
sln ファイルでやりたいこと
sln ファイルがあれば msbuild が利用できます。
たとえばmsbuild project.sln -warnAsError
を CI 中に仕込むことで、Unity プロジェクトに警告があった場合に CI を失敗させるなどで利用できます。方法
batchmode で UnityEditor.SyncVS.SyncSolution メソッドを実行します。
以下が実行例です。
Unity.exe -batchmode -nographics -logFile - -executeMethod UnityEditor.SyncVS.SyncSolution -projectPath . -quit参考
- 投稿日:2021-01-06T03:42:49+09:00
【Unity】UI ToolkitのMouseDown・MouseUpの最小実装
【Unity】UI ToolkitをランタイムUIとして使ってみるに引き続き、
Unityの次世代UIシステムにあたるUI Toolkitについて調査していきます。あくまでランタイムUIとして使う想定の検証です。
今回作るもの
環境
- Unity2020.2.0f1
- UI Toolkit 1.0.0-Preview.13
PointerDownEventとPointerUpEventを使う
MouseDown・MouseUpに反応するVisualElementに対してイベントを登録(RegisterCallback)していきます。
uxmlファイルから見ていきます。
MouseDownUpTest.uxml<ui:UXML xmlns:ui="UnityEngine.UIElements" editor-extension-mode="False"> <ui:VisualElement name="container" style="height: 100%; width: 100%; background-color: rgba(255, 252, 0, 255);" /> </ui:UXML>
container
という名前のVisualElement
が1つだけ全画面に配置してあるシンプルな構成です。C#側からVisualElementにイベント登録
C#のソースコードです。
MouseDownUpTest.cs// _documentはUIDocumentのこと var visualTree = _document.rootVisualElement; // VisualElement : "conainer"をvisualTreeから検索して取得 VisualElement container = visualTree.Q("container"); // イベント登録 container.RegisterCallback<PointerDownEvent>(OnPointerDown); container.RegisterCallback<PointerUpEvent>(OnPointerUp);
VisualElement
であるcontainerを取得して、RegisterCallback
メソッドでイベントを登録しています。コールバック先でイベントターゲットのVisualElementを取得する
MouseDownUpTest.cs// MouseUpしたら色を赤色にする private void OnPointerDown(PointerDownEvent evt) { var ve = (VisualElement) evt.target; ve.style.backgroundColor = Color.red; } // MouseUpしたら色を黄色にする private void OnPointerUp(PointerUpEvent evt) { var ve = (VisualElement) evt.target; ve.style.backgroundColor = Color.yellow; }ポイントは
(VisualElement) evt.target
の部分でVisualElementにキャストしているところでしょうか。公式サンプルがこうなってるので、それにならった形を取りました。EventSystem(UI Toolkit)が必ず必要
「イベントが通知されないな〜??」
と思った時はEventSystemがシーンに配置してあるかチェックしてみましょう。
必ずシーンに1つ必要です。
最後に
以上UI ToolkitにおけるMouseDown・MouseUpの最小実装でした。
ソースコード全文はGistにアップしています。
- 投稿日:2021-01-06T01:07:28+09:00
【Unity for Mac】VSCodeでC# Extensionの設定
UnityのScript EditorでVSCode
- C# Extensionが機能しない
- F12やCommand+クリックで関数の定義へジャンプしない
- C#のエクステンションをインストールする(エクステンションショートカット:Command+Shift+X)
- 設定を開く(設定ショートカット:Command+,)
- Search settingsに「mono」とタイプすると、Extensionsの項目に「C# configuration」がヒットするので開く
- 「Use Global Mono」の設定を「always」にする
- 「Mono Path」の設定(Edit in settings.json)を開き、「omnisharp.monoPath」を入力する
- VSCodeを再起動する
omnisharp.monoPath の確認方法
ターミナルを開き以下のコマンドで確認する
which monomono バージョンの確認方法
mono -V