【Unity】マルチディスプレイにおける座標の扱い


はじめに

Unityでは、スタンドアロンモードのみマルチディスプレイに対応しています。(対応OS:Windows、Mac、Linux)

Windowsでマルチディスプレイ対応のアプリを作成していたところ、
座標の扱いで何度か混乱してしまったため、書き留めておきたいと思います。

環境

  • Windows 10
  • Unity 2019.2.19f1
  • Visual Studio 2019

スクリーン座標とワールド座標

Unityをさわったことがある方ならご存知だと思いますが、Unityの座標にはスクリーン座標系とワールド座標系が存在します。
スクリーン座標系は画面上の座標で、左下隅が (0, 0)となります。
ワールド座標系はUnityのゲーム空間上の座標で、Scene直下にあるオブジェクトであればInspectorのTransform > Position の値となります。

マウス位置(Input.mousePosition)は画面上のどこかを表すものなので、当然スクリーン座標系となります。

マルチディスプレイの場合のマウス位置は?

マルチディスプレイの場合、Input.mousePositionで取得できる座標は、メインディスプレイ基準の座標となります。
例えば、1920 x 1080のディスプレイ2台を以下のように配置していた場合

座標は以下のようになります。

ワールド座標に変換したい

取得したマウス位置の座標をワールド座標に変換するには、
① 画面ごとのスクリーン座標に変換
② 該当する画面のカメラでワールド座標に変換
の2段階の変換を行う必要があります。

    // cameras という変数に、ディスプレイ番号順のカメラが保持されている想定です

    private Vector2? ConvertScreenPointToWorld(Vector2 position)
    {
        // 1. 画面ごとのスクリーン座標に変換
        // ※ 各画面の左下隅を(0, 0)とする座標に変換されます
        var relativePosition = Display.RelativeMouseAt(position);
        // ※ 気持ち悪いですが、z座標にディスプレイ番号がセットされます...
        var displayIndex = (int)relativePosition.z;
        if (displayIndex >= cameras.Count) return null;

        // 2. 該当する画面のカメラでワールド座標に変換
        var camera = cameras[displayIndex];
        return camera.ScreenToWorldPoint(relativePosition);
    }

ちなみに、Unityエディター上では Display.RelativeMouseAt() は動作しないのでご注意ください。