Unity 3D 观察物体 旋转查看物体 世界坐标与局部坐标旋转转换 旋转物体问题

关于Unity 3D 旋转物体问题

之前得到一个需求,需要查看物体的破损状况与标牌的过期情况.
也就是需要360°查看物体,还需要有一个聚焦功能查看标牌.
请添加图片描述

移动好限制,可以控制具体的位置信息.
详情可查看我之前写的位置限制功能 => Unity简单的摄像机控制代码,第三人称,第一人称,场景漫游
旋转可不像位置一样有具体的位置信息,旋转过了360就会重0继续增加,还有万向锁的问题,关于万向锁我这里不过多解释,网上有很多解析
所以我自己做了一个控制物体旋转的小工具(包含聚焦功能).

Demo效果

请添加图片描述

控制面板

在这里插入图片描述
为了避免穿模,聚焦与查看整个物体模式有不同的限制.可以根据自身情况自行调整
在这里插入图片描述

上代码!

using System;
//using TFramework;
using UnityEngine;

public class ObjectViewer : MonoBehaviour
{
    const string INPUT_MOUSE_SCROLLWHEEL = "Mouse ScrollWheel";
    const string INPUT_MOUSE_X = "Mouse X";
    const string INPUT_MOUSE_Y = "Mouse Y";
    private float axisX = 1;      //鼠标沿水平方向移动的增量   
    private float axisY = 1;      //鼠标沿竖直方向移动的增量   

    [Header("是否激活")]
    public bool Activate = true;
    public Transform cameraLocation;
    Transform InitObj = null;
    Transform TempObjUp;
    Transform TempObjLeft;
    [Header("仰视角限制")]
    [Range(0.01f, 89.9f)]
    public float downEulerLimit = 85f;
    [Header("俯视角限制")]
    [Range(-0.01f, -89.9f)]
    public float upEulerLimit = -85f;

    [Header("右限制")]
    [Range(0.01f, 179.9f)]
    public float mRightEulerLimit = 85f;
    [Header("左限制")]
    [Range(-0.01f, -179.9f)]
    public float mLeftEulerLimit = -85f;

    [Header("旋转速度")]
    public float speed = 2f;      //旋转速度  

    [Header("最远查看距离限制")]
    [Range(0, 1)]
    public float farDistance = 0f;
    [Header("最近查看距离限制")]
    [Range(0, 1)]
    public float nearDistance = 0.8f;
    [Header("当前缩放距离")]
    public float distance = 0.5f;
    [Header("缩放速度")]
    public float zoomSpeed = 0.8f;

    float LimitY = 0, LimitX = 0;
    Vector3 MaxDistance = Vector3.zero;
    Vector3 NowPoint = Vector3.zero;
    void UpdateObjRotateState()
    {
        axisX = -Input.GetAxis(INPUT_MOUSE_X);
        axisX *= Time.deltaTime * 600 * speed;
        //获得鼠标增量 
        axisY = Input.GetAxis(INPUT_MOUSE_Y);
        axisY *= Time.deltaTime * 600 * speed;
        #region 限制

        LimitY += axisY;
        LimitX += axisX;
        if (LimitY < downEulerLimit && LimitY > upEulerLimit)
            TempObjUp.Rotate(new Vector3(-axisY, 0, 0), Space.Self);
        else
            LimitY -= axisY;

        if (LimitX < mRightEulerLimit && LimitX > mLeftEulerLimit)
            TempObjLeft.Rotate(new Vector3(0, axisX, 0), Space.Self);
        else
            LimitX -= axisX;

        #endregion
        //TempObjLeft.Rotate(new Vector3(0, axisX, 0), Space.Self);
    }
    bool isReset = false;
    void UpdateObjDistanceState()
    {
        if (Input.GetAxis(INPUT_MOUSE_SCROLLWHEEL) != 0f)
        {
            float delta = Input.GetAxis(INPUT_MOUSE_SCROLLWHEEL);
            float temp = delta * 100 * Time.deltaTime * -zoomSpeed;
            distance -= temp;
            distance = Mathf.Clamp(distance, farDistance, nearDistance);
            if (distance < farDistance + (nearDistance - farDistance) / 2 && isReset)
            {
                Init(InitObj);
                ViewObj = null;
                isReset = false;
                UnLimitLeftRotate();
            }
        }
        NowPoint = Vector3.Lerp(MaxDistance, cameraLocation.position, distance);
        if (Vector3.Distance(TempObjUp.position, NowPoint) > 0.01f)
            TempObjUp.position = Vector3.Lerp(TempObjUp.position, NowPoint, Time.deltaTime * 5f);
        else
            TempObjUp.position = NowPoint;
        if (IsReset)
        {
            TempObjUp.LookAt(cameraLocation);
            ResetTo(TempObjLeft, TempObjUp);
            LimitY = 0;
            LimitX = 0;
        }
        //TempObjUp.position = NowPoint;
    }
    private void Start()
    {
        if (TempObjUp == null)
            TempObjUp = new GameObject("TempUp").transform;
        if (TempObjLeft == null)
            TempObjLeft = new GameObject("TempLeft").transform;

        UnLimitLeftRotate();
    }
    private void LateUpdate()
    {
        if (!Activate)
            return;
        UpdateObjDistanceState();

        if (Input.GetMouseButton(0))
        {
            MouseButtonL(null);
        }
        if (Input.GetMouseButtonDown(0))
        {
            MouseButtonDownL(null);
        }
        if (Input.GetMouseButtonDown(1))
        {
            MouseButtonDownR(null);
        }
    }
    Vector3 TempPosition;
    Quaternion TempRotation;
    Transform InitObjRoot;
    private void MouseButtonDownR(object obj)
    {
        if (OffViewObj())
        {
            return;
        }
        MaxDistance = GetGameObjectForwardPoint(cameraLocation, 10);
        GameObject mRayObj = GetCameraDetectionObj(10);

        if (mRayObj)
        {
            TempPosition = mRayObj.transform.position;
            TempRotation = mRayObj.transform.rotation;
            InitObjRoot = mRayObj.transform.parent;
            InitObj = mRayObj.transform;
            distance = (nearDistance - farDistance) / 2;
            //Activate = true;
            Init(InitObj);
        }
    }
    Vector3 GetGameObjectForwardPoint(Transform transform, float MaxDistance)
    {

        Vector3 Ray;                       //声明一个射线
        RaycastHit Hit;               //用于记录射线碰撞到的物体
        Ray = transform.forward * MaxDistance;
        //Debug.DrawRay(transform.position, Ray,Color.black);
        //物理检测射线,out一个RaycastHit类型的 hitInfo 信息,float distance是射线长度,int layerMask需要转换二进制,所以有如下操作
        if (Physics.Raycast(transform.position, Ray, out Hit, MaxDistance))
        {
            return Hit.point;
        }
        //Debug.Log("距离不够?物体没有碰撞体?");
        return Vector3.zero;
    }
    public bool OffViewObj()
    {
        if (InitObj)
        {
            //Activate = false;
            InitObj.position = TempPosition;
            InitObj.rotation = TempRotation;
            InitObj.parent = InitObjRoot;
            //InitObj.GetComponent<ObjectTransformation>().changeObjState(0);
            InitObj = null;
            ViewObj = null;
            //Player.Instance.HideSetting();
            return true;
        }
        return false;
    }
    GameObject ViewObj = null;
    private void MouseButtonDownL(object obj)
    {
        if (!Activate || InitObj == null)
            return;
        axisX = 0f; axisY = 0f;
        GameObject mRayObj = GetCameraDetectionObj(10);
        if (mRayObj && ViewObj == null)
        {
            if (mRayObj.tag == "物品查看点")
            {
                ViewObj = mRayObj;
                Init(mRayObj.transform);
                LimitLeftRotate();
                //if (distance < farDistance + (nearDistance - farDistance) / 2)
                distance = farDistance + (nearDistance - farDistance) / 1.1f;
                isReset = true;
                return;
            }
        }
    }
    GameObject GetCameraDetectionObj(float MaxDistance)
    {
        Ray camerRay;                       //声明一个射线
        Vector3 mousePos = new Vector3();   //记录将鼠标(因为屏幕坐标没有z,所以下面是将z设为0)
        RaycastHit cameraHit;               //用于记录射线碰撞到的物体
                                            //这里将屏幕坐标的鼠标位置存入一个vector3里面
        mousePos.x = Input.mousePosition.x;
        mousePos.y = Input.mousePosition.y;
        mousePos.z = 0;

        //Ray ray=Camera.main.ScreenPointToRay(Vector3 Pos):返回一条射线由摄像机近裁面发射经过Pos的射线。
        camerRay = Camera.main.ScreenPointToRay(mousePos);
        //物理检测射线,out一个RaycastHit类型的 hitInfo 信息,float distance是射线长度,int layerMask需要转换二进制,所以有如下操作
        if (Physics.Raycast(camerRay, out cameraHit, MaxDistance))
        {
            GameObject go = cameraHit.transform.gameObject; //这是检测到的物体
            if (go != null)
            {
                return go;
            }
        }
        //Debug.Log("距离不够?没有点到物体?物体没有碰撞体?");
        return null;
    }
    void LimitLeftRotate()
    {
        mRightEulerLimit = 60f;
        mLeftEulerLimit = -60f;
        nearDistance = 0.95f;
    }
    void UnLimitLeftRotate()
    {
        mRightEulerLimit = 179.9f;
        mLeftEulerLimit = -179.9f;
        nearDistance = 0.85f;
    }

    private void MouseButtonL(object obj)
    {
        if (!Activate || InitObj == null)
            return;
        UpdateObjRotateState();
    }
    private void Init(Transform mObj)
    {
        if (!Activate || InitObj == null)
            return;

        if (TempObjUp.childCount == 0)
            TempObjLeft.parent = TempObjUp;
        if (TempObjLeft.childCount != 0)
            TempObjLeft.GetChild(0).parent = InitObjRoot;
        
        ResetTo(TempObjLeft, TempObjUp);
        TempObjLeft.localScale = Vector3.one;
        Debug.Log(mObj.name);
        ResetTo(TempObjUp, mObj);
        TempObjUp.localScale = Vector3.one;
        TempObjUp.parent = InitObj.parent;
        InitObj.parent = TempObjLeft;
        IsReset = true;
        Invoke("OnInvok", 1f);
    }
    void OnInvok()
    {
        IsReset = false;
    }
    void ResetTo(Transform transform, Transform Target)
    {
        transform.position = Target.position;
        transform.rotation = Target.rotation;
        transform.localScale = Target.localScale;
    }
    bool IsReset = false;
}

Demo下载链接: Unity 3D 观察物体

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

唐沢

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值