20210106のUnityに関する記事は6件です。

【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;
}

出来たもの(失敗作)

D&D_Fail.gif

なんか挙動がおかしい...

  1. ドラッグ対象の基準点
  2. イベントの未発火

上記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)

D&D_Fail2.gif

一見うまく動いてそうです。
しかし、マウスを早く動かしてドラッグターゲットよりマウスカーソルをはみ出すと、ドラッグターゲットがマウスについて来なくなりました。
その後MouseDown状態でもないのにマウスにくっついて来るようにもなっています。

はい、失敗です。

2.イベントの未発火

原因はイベントの伝播が出来ていないことです。

ドラッグターゲットからマウスを外してもPointerMoveEventPointerUpEventは発火してもらわないと困ります。

その後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);
    }
}

出来たもの(成功)

D&D.gif

意図通りのドラッグアンドドロップが完成しました。

最後に

全体ソースはGistにアップしています。

環境

  • Unity2020.2.0f1
  • UI Toolkit 1.0.0-Preview.13
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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 Profiler

OVRメトリックツール

まずは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レベルを操作できるようにします。

ざっくり過ぎましたが、開発が進みましたらより詳細な情報を記事にしたいと思います。

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

【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である

D&D.gif

これでドラッグアンドドロップは実装できそうですね。


  1. イベントは他にも存在します 

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

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

参考

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

【Unity】UI ToolkitのMouseDown・MouseUpの最小実装

【Unity】UI ToolkitをランタイムUIとして使ってみるに引き続き、
Unityの次世代UIシステムにあたるUI Toolkitについて調査していきます。

あくまでランタイムUIとして使う想定の検証です。

今回作るもの

マウス押下している時に色が変わるサンプルを作っていきます。
demo.gif

環境

  • 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にアップしています。

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

【Unity for Mac】VSCodeでC# Extensionの設定

UnityのScript EditorでVSCode

  • C# Extensionが機能しない
  • F12やCommand+クリックで関数の定義へジャンプしない
  1. C#のエクステンションをインストールする(エクステンションショートカット:Command+Shift+X)
  2. 設定を開く(設定ショートカット:Command+,)
  3. Search settingsに「mono」とタイプすると、Extensionsの項目に「C# configuration」がヒットするので開く
  4. 「Use Global Mono」の設定を「always」にする
  5. 「Mono Path」の設定(Edit in settings.json)を開き、「omnisharp.monoPath」を入力する
  6. VSCodeを再起動する

omnisharp.monoPath の確認方法

ターミナルを開き以下のコマンドで確認する

which mono

mono バージョンの確認方法

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