为球体创建类似 Google Earth 的导航 [英] Creating a Google Earth like navigation for a sphere
问题描述
这个问题是关于 Unity3D 的.我想创建一个类似于
private void RotateCamera(Vector3 dragStart, Vector3 dragEnd){//计算拖动的旋转浮动角度 = Vector3.Angle(dragStart, dragEnd);//围绕球体旋转相机Camera.main.transform.RotateAround(sphere), Vector3.up, angle);}
我想到了使用 Unitys RotateAround 方法以计算出的角度旋转相机.不幸的是我没有旋转向量(在示例中使用 Vector3.up 显然是错误的).有人知道我如何计算这个向量以将其应用于该方法吗?实施 Google 地球导航的方向是否正确?
谢谢!
更新我非常接近一个新的解决方案.我将阻力向量投影到向下和向右的平面以获得角度.之后我向上和向左旋转相机.这很有效,直到我到达球体的两极.如果我到达一根杆子,相机会围绕自身旋转很多.
private void RotateCamera(Vector3 dragStart, Vector3 dragEnd){Vector3 平面 = Vector3.down;var a = Vector3.ProjectOnPlane(dragStart, plane);var b = Vector3.ProjectOnPlane(dragEnd, plane);float up = Vector3.SignedAngle(a, b, plane);平面 = Vector3.right;a = Vector3.ProjectOnPlane(dragStart, plane);b = Vector3.ProjectOnPlane(dragEnd, plane);float left = Vector3.SignedAngle(a, b, plane);Camera.main.transform.RotateAround(_sphere, Vector3.up, up);Camera.main.transform.RotateAround(_sphere, Vector3.left, left);}
事实证明这比我预期的要容易.我想过计算旋转轴,得出的结论是必须是起点和终点矢量的叉积.看看解决方案.RotateCamera 方法是数学魔法 发生的地方:)
公共类 GoogleEarthControls : MonoBehaviour{私有常量 int SpehreRadius = 1;私人Vector3?_mouseStartPos;私人Vector3?_currentMousePos;无效开始(){//初始化相机看这个物体Vector3 cameraPos = new Vector3(变换位置.x,变换位置y,变换.位置.z - 2);Camera.main.transform.position = cameraPos;Camera.main.transform.LookAt(transform.position);}私人无效更新(){if (Input.GetMouseButtonDown(0)) _mouseStartPos = GetMouseHit();if (_mouseStartPos != null) HandleDrag();if (Input.GetMouseButtonUp(0)) HandleDrop();}私有无效 HandleDrag(){_currentMousePos = GetMouseHit();RotateCamera((Vector3) _mouseStartPos, (Vector3)_currentMousePos);}私有无效 HandleDrop(){_mouseStartPos = null;_currentMousePos = null;}private void RotateCamera(Vector3 dragStartPosition, Vector3 dragEndPosition){//如果球体模型不是完美的球体..dragEndPosition = dragEndPosition.normalized * SpehreRadius;dragStartPosition = dragStartPosition.normalized * SpehreRadius;//计算一个垂直向量以围绕..var cross = Vector3.Cross(dragEndPosition, dragStartPosition);//计算旋转的角度..var angle = Vector3.SignedAngle(dragEndPosition, dragStartPosition, cross);//围绕向量旋转..Camera.main.transform.RotateAround(transform.position, cross, angle);}/*** 将鼠标位置投影到球体并返回交点.*/私有静态 Vector3?获取鼠标命中(){//确保有一个 shepre 网格,带有一个以该游戏对象为中心的 colider//半径为 SpehreRadiusRaycastHit 命中;if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit)){返回命中点;}返回空;}}
This question is about Unity3D. I want to create a navigation similar to Google Earth where you click and drag on a sphere and let the camera orbit accordingly. It is important that the point that was grabbed is always under the mouse position while dragging. The navigation should also work if I zoom close to the sphere. I do not want to rotate the sphere itself. Just exactly like Google Earth does it.
My attempt is to project the mouse position to the sphere if I start to drag. On the next frame I do the same and calculate the angle between the start drag and end drag position.
private void RotateCamera(Vector3 dragStart, Vector3 dragEnd)
{
// calc the rotation of the drag
float angle = Vector3.Angle(dragStart, dragEnd);
// rotate the camera around the sphere
Camera.main.transform.RotateAround(sphere), Vector3.up, angle);
}
I thought of using Unitys RotateAround method to rotate the camera with the calculated angle. Unfortunately I do not have the rotation vector (using Vector3.up in the example is obviously wrong). Does somebody know how I can calculate this vector to apply it for the method? Am I on the right direction to implement the Google Earth navigation?
Thank You!
UPDATE I am very close with a new solution. I project the drag vectors to a down and a right plane to get the angles. Afterwards I rotate the camera around up and left. This works well until I reach the poles of the sphere. The camera rotates a lot around itself if I reach a pole.
private void RotateCamera(Vector3 dragStart, Vector3 dragEnd)
{
Vector3 plane = Vector3.down;
var a = Vector3.ProjectOnPlane(dragStart, plane);
var b = Vector3.ProjectOnPlane(dragEnd, plane);
float up = Vector3.SignedAngle(a, b, plane);
plane = Vector3.right;
a = Vector3.ProjectOnPlane(dragStart, plane);
b = Vector3.ProjectOnPlane(dragEnd, plane);
float left = Vector3.SignedAngle(a, b, plane);
Camera.main.transform.RotateAround(_sphere, Vector3.up, up);
Camera.main.transform.RotateAround(_sphere, Vector3.left, left);
}
Turns out that is was easier than I expected. I thought about calculating the rotation axis and came to the conclusion that is must be the cross product of the start and end vector. Take a look at the solution. The RotateCamera method is where the math magic happens :)
public class GoogleEarthControls : MonoBehaviour
{
private const int SpehreRadius = 1;
private Vector3? _mouseStartPos;
private Vector3? _currentMousePos;
void Start () {
// init the camera to look at this object
Vector3 cameraPos = new Vector3(
transform.position.x,
transform.position.y,
transform.position.z - 2);
Camera.main.transform.position = cameraPos;
Camera.main.transform.LookAt(transform.position);
}
private void Update()
{
if (Input.GetMouseButtonDown(0)) _mouseStartPos = GetMouseHit();
if (_mouseStartPos != null) HandleDrag();
if (Input.GetMouseButtonUp(0)) HandleDrop();
}
private void HandleDrag()
{
_currentMousePos = GetMouseHit();
RotateCamera((Vector3) _mouseStartPos, (Vector3)_currentMousePos);
}
private void HandleDrop()
{
_mouseStartPos = null;
_currentMousePos = null;
}
private void RotateCamera(Vector3 dragStartPosition, Vector3 dragEndPosition)
{
// in case the spehre model is not a perfect sphere..
dragEndPosition = dragEndPosition.normalized * SpehreRadius;
dragStartPosition = dragStartPosition.normalized * SpehreRadius;
// calc a vertical vector to rotate around..
var cross = Vector3.Cross(dragEndPosition, dragStartPosition);
// calc the angle for the rotation..
var angle = Vector3.SignedAngle(dragEndPosition, dragStartPosition, cross);
// roatate around the vector..
Camera.main.transform.RotateAround(transform.position, cross, angle);
}
/**
* Projects the mouse position to the sphere and returns the intersection point.
*/
private static Vector3? GetMouseHit()
{
// make sure there is a shepre mesh with a colider centered at this game object
// with a radius of SpehreRadius
RaycastHit hit;
if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit))
{
return hit.point;
}
return null;
}
}
这篇关于为球体创建类似 Google Earth 的导航的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!