You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:30:03