地球儀アプリの操作のように、マウスカーソル位置を中心に拡大・縮小したり、ドラッグで回転させる方法
リポジトリ
https://github.com/yasuhto/ZoomableGlobe
拡大・縮小
マウスカーソル位置を中心に拡縮させるということは、カーソル位置の球体上の座標は変化しません。つまり、拡縮前後でカーソル位置の座標が変化しないようにズレを吸収する方向に球体を回転させます。
CameraController.cs
// ホイール操作による拡縮前後のカーソル位置からの衝突点(A、B)をそれぞれ求める
var fromPos = hit.point;
var toPos = hit2.point;
// 回転軸axisは、球面上の中心点(O)とABからなる平面の法線
var vecA = fromPos - this.TargetSphere.position;
var vecB = toPos - this.TargetSphere.position;
var axis = Vector3.Cross(vecA, vecB);
// 球面上の中心点(O)から軸axisに対する回転角度(θ)を求める
// 拡縮時のカーソルのズレを打ち消す方向
var dragAngle = Vector3.SignedAngle(toPos, fromPos, axis);
// 回転軸axisに対してθ回転させる
this.transform.RotateAround(this.TargetSphere.position, axis, dragAngle);
回転
マウスのドラッグ方向から計算した角度をもとに、球体を中心にカメラを回転させます。こうすることで、見かけ上球体が回転しているように見えます。回転の計算方法は、拡縮時とほぼ同じです。
CameraController.cs
// 平面上のドラッグから、球面上のドラッグ区間(AB)を求める
InputHepler.GetMouseDragFromTo(this.TargetSphere, this._TargetCamera, out var fromPos, out var toPos);
// 回転軸axisは、球面上の中心点(O)とABからなる平面の法線
var vecA = fromPos - this.TargetSphere.position;
var vecB = toPos - this.TargetSphere.position;
var axis = Vector3.Cross(vecA, vecB);
// 球面上の中心点(O)から軸axisに対する回転角度(θ)を求める
var dragAngle = Vector3.SignedAngle(fromPos, toPos, axis);
// 回転軸axisに対してθ回転させる
this.transform.RotateAround(this.TargetSphere.position, axis, -1 * dragAngle);