土鍋で雑多煮

UnityでXR・ゲーム開発をしています。学んだことや備忘録、趣味の記録などを書いていきます。

MENU

【UI Toolkit】UI ToolkitのUI上にカーソルがあるときにRaycastをブロックする

はじめに

どうも、土鍋です。

通常のPhysics.RaycastはUIがあるかを検知してくれません。そのため、UIがあっても貫通して後ろのオブジェクトにRayが当たってしまいます。
従来のuGUIでもそうですが、uGUIはこの記事のようにEventSystemでブロックすることが可能です。

しかし、UI ToolkitはEventSystemを使用しないのでこの方法が使えません。
そこで今回はUI ToolkitでもRaycastをブロックできるようにします。

現状の状態

現状のRaycastではこのようにUI上でクリックしても貫通してその裏のオブジェクトの情報を取ってきてしまいます。

コードはこんな感じのをupdateで実行しています。

if (Input.GetMouseButtonDown(0)) //マウスがクリックされたら
{
    var ray = camera.ScreenPointToRay(Input.mousePosition);

    foreach (RaycastHit hit in Physics.RaycastAll(ray))
    {
        Debug.Log(hit.transform.name);
    }
}

実装していく

UI上にカーソルがあるかのEventCallbackを登録する

どのタイミングでブロックするかを設定したいので、UI上にカーソルが入ったときと出たときのEventCallbackを登録していきます。

private void Start()
{
    var root = GetComponent<UIDocument>().rootVisualElement;
        
    root.RegisterCallback<MouseOverEvent>(Enter);
    root.RegisterCallback<MouseOutEvent>(Exit);
}

VisualElementに対してRegisterCallbackでマウスの状態に合わせてイベントを発火するように設定しています。
ここでは

  • MouseOverEvent → マウスがUI上に入った瞬間
  • MouseOutEvent → マウスがUI上から出た瞬間

にそれぞれEnterとExitのメソッドを実行するようにしています。

ブロック状態の保持

UIの上にカーソルが来た際にブロック状態を変化させることができれば良いので、boolでそれを保持できるようにします。
今回はシングルトンでブロック状態を保持するクラスを作りました。
これを適当なクラスでnewしてあげれば、どこからでもブロックするか否かを設定・参照できます。

public class PlayerStatusHolder
{
    public static PlayerStatusHolder I;
    public bool isBlockClickRaycast;
    
    public PlayerStatusHolder()
    {
        I = this;
        isBlockClickRaycast = false;
    }
}

RegisterCallbackで設定していたメソッドの中でboolの値を変えれば、ブロック状態を変化させることができます。

private void Enter(MouseOverEvent evt)
{
    PlayerStatusHolder.I.isBlockClickRaycast = true;
}

private void Exit(MouseOutEvent evt)
{
    PlayerStatusHolder.I.isBlockClickRaycast = false;
}

Raycastをブロックする

あとはこれをRaycast処理の前で監視して、trueだったらRaycast処理を実行しないようにすれば、UIによるRaycastブロックができます。

Update()
{
    if(PlayerStatusHolder.I.isBlockClickRaycast) return;

    ~Raycast処理~
}

完成

参考

qiita.com

light11.hatenadiary.com