Unityでカメラが平行投影の場合にScreenToWorldPointがズレる
初めに
タイトルの通りの話。もっと良い方法があるはずだけど、とりあえず残します。
- Unity 6000.1.12f1
内容
誤差の理由は、スクリーン座標はピクセルなのでピクセルの隙間がある為との事。 じゃあどうするかだが補正する事にした。
補正値の計算
- カメラを原点上空に移動
- Camera.ScreenToWorldPoint をスクリーン中心(new Vector2(Screen.width * 0.5f, Screen.height * 0.5f)) で実行
- 結果を補正値として保存
- カメラ位置を戻す
※ カメラは回転Xを90度にして真下を見ているとする。
※ スクリーンサイズや Camera.orthographicSize が変わる度に計算し直す。
後は Camera.ScreenToWorldPoint を実行する度に補正値を引く。 それでも誤差は残るので確認はする事。許容出来ないなら透視投影にする。
ところで透視投影だと誤差が少ない理由は、カメラからレイを飛ばして計算する関係かららしいが理解出来ていない。
サンプルコード
using UnityEngine;
using System;
public class MyCameraController : MonoBehaviour
{
Camera cameraP;
Vector3 worldDelta;
float prevOrthographicSize = 0f;
float prevScreenWidth = 0f;
float prevScreenHeight = 0f;
void Start()
{
cameraP = GetComponent<Camera>();
UpdateWorldDelta();
}
public Vector3 ScreenToWorld(Vector2 screenPos)
{
UpdateWorldDelta();
return cameraP.ScreenToWorldPoint(screenPos) - worldDelta;
}
public Vector2 WorldToScreen(Vector3 worldPos)
{
UpdateWorldDelta();
return cameraP.WorldToScreenPoint(worldPos + worldDelta);
}
void UpdateWorldDelta()
{
if (Mathf.Approximately(prevOrthographicSize, cameraP.orthographicSize) &&
Mathf.Approximately(prevScreenWidth, Screen.width) &&
Mathf.Approximately(prevScreenHeight, Screen.height)) return;
prevOrthographicSize = cameraP.orthographicSize;
prevScreenWidth = Screen.width;
prevScreenHeight = Screen.height;
var prevPosition = transform.position;
try
{
transform.position = new Vector3(0f, transform.position.y, 0f);
worldDelta = cameraP.ScreenToWorldPoint(new Vector2(Screen.width * 0.5f, Screen.height * 0.5f));
}
finally
{
transform.position = prevPosition;
}
}
}