Unity中基于点击/触摸实现相机绕椭圆轨道旋转的问题
解决相机沿椭圆轨道绕鸡蛋旋转的问题
我懂你现在的困扰——线性计算相机距离的方式没法贴合鸡蛋的椭球形曲面,导致穿模对吧?咱们直接把逻辑改成基于椭球径向距离的计算,就能让相机完美沿着椭圆轨道绕转了。
问题根源
原来的MaintainDistanceAt用线性比例计算Z轴距离,相当于假设鸡蛋是规则的球体或者圆柱,但鸡蛋是椭球形,不同旋转角度下的表面距离中心的半径是不一样的,所以线性缩放必然会穿模。
解决方案:用椭球径向公式计算相机距离
我们需要根据椭球的方程,计算每个旋转角度下相机到Pivot的正确距离,这样相机就会刚好贴合鸡蛋表面。
步骤1:添加椭球参数
首先在脚本里添加两个参数,用来匹配你鸡蛋模型的实际尺寸:
public float eggVerticalRadius = 1.5f; // 鸡蛋上下(Y轴)方向的半径 public float eggHorizontalRadius = 2.0f; // 鸡蛋前后(Z轴)方向的半径
你可以根据模型的实际大小调整这两个值,比如鸡蛋更“扁”的话就调小垂直半径,更“长”就调大。
步骤2:重写MaintainDistanceAt方法
替换原来的线性计算逻辑,用椭球的径向距离公式来计算目标距离:
void MaintainDistanceAt(float f) { // 将角度转换为弧度(Unity的三角函数用弧度计算) float angleRad = f * Mathf.Deg2Rad; // 计算当前旋转方向对应的椭球径向距离 float sinTheta = Mathf.Sin(angleRad); float cosTheta = Mathf.Cos(angleRad); float radiusTerm = Mathf.Pow(sinTheta / eggVerticalRadius, 2) + Mathf.Pow(cosTheta / eggHorizontalRadius, 2); float targetDistance = 1 / Mathf.Sqrt(radiusTerm); // 确保相机不会太近(防止穿模模型内部) targetDistance = Mathf.Max(targetDistance, minDist); // 设置相机在Pivot本地Z轴的位置(负方向是朝向鸡蛋) theCamera.localPosition = new Vector3(0f, 0f, -targetDistance); }
步骤3:清理冗余代码
原来的divRatio变量和Start里的divRatio = maxX / maxDist;已经没用了,可以直接删掉。
修改后的完整代码
using System.Collections; using System.Collections.Generic; using UnityEngine; public class CameraMotionScript : MonoBehaviour { public Transform thePivot, theCamera; public float rotSpeed = 5f, lerpSpeed, minX = -90f, maxX = 90f, minY = -90f, maxY = 90f, maxDist; public float eggVerticalRadius = 1.5f; // 鸡蛋Y轴半径 public float eggHorizontalRadius = 2.0f; // 鸡蛋Z轴半径 private float rotX = 0f, rotY = 0f, dX = 0f, dY = 0f, rotateDirection = -1f, cachedRotSpeed = 0f, minDist = 0f; private bool rotationActive = false; void Start () { cachedRotSpeed = rotSpeed; minDist = Mathf.Abs (theCamera.localPosition.z); } void Update () { if (Input.GetMouseButton (0)) { rotSpeed = cachedRotSpeed; dX = Input.GetAxis ("Mouse X"); dY = Input.GetAxis ("Mouse Y"); rotationActive = true; } if (Input.GetMouseButtonUp (0)) { rotationActive = false; } rotX += dY * rotSpeed * rotateDirection * Time.deltaTime; rotY += dX * rotSpeed * -rotateDirection * Time.deltaTime; // 限制旋转范围在-90到90度之间 rotX = Mathf.Clamp (rotX, minX, maxX); rotY = Mathf.Clamp (rotY, minY, maxY); if (thePivot != null) thePivot.eulerAngles = new Vector3 (rotX, rotY, 0f); if (!rotationActive) { rotSpeed = Mathf.Lerp (rotSpeed, 0, Time.deltaTime * lerpSpeed); if (rotSpeed < 1) rotSpeed = 0; } MaintainDistanceAt (Mathf.Abs (rotX)); } void MaintainDistanceAt (float f) { float angleRad = f * Mathf.Deg2Rad; float sinTheta = Mathf.Sin(angleRad); float cosTheta = Mathf.Cos(angleRad); float radiusTerm = Mathf.Pow(sinTheta / eggVerticalRadius, 2) + Mathf.Pow(cosTheta / eggHorizontalRadius, 2); float targetDistance = 1 / Mathf.Sqrt(radiusTerm); targetDistance = Mathf.Max(targetDistance, minDist); theCamera.localPosition = new Vector3(0f, 0f, -targetDistance); } }
额外优化建议
- 如果你的鸡蛋模型是不对称的(比如一端尖一端圆),可以给
rotX的正负值分别设置不同的垂直半径,比如eggTopRadius和eggBottomRadius,在MaintainDistanceAt里判断rotX的正负来选择对应的半径。 - 可以添加射线检测:从Pivot向相机方向发射射线,检测是否和鸡蛋模型碰撞,如果碰撞就把相机拉近到碰撞点,这样即使模型有不规则的细节也不会穿模。
内容的提问来源于stack exchange,提问作者Rishi
相关产品推荐
相关产品推荐

