1. 程式人生 > >Unity攝像機跟隨 Touch遮蔽UI 觸控控制攝像機旋轉,手勢控制放大縮小 穿牆拉近攝像機

Unity攝像機跟隨 Touch遮蔽UI 觸控控制攝像機旋轉,手勢控制放大縮小 穿牆拉近攝像機

using UnityEngine;
using System.Collections;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using UnityEngine.PostProcessing;
using System.Collections.Generic;
using System;
using DTools;

public class PlayerCamera : MonoBehaviour
{
    public Transform mTarget;
    private GameObject m_goPivot;
    public Camera m_camPlayer;
    [Tooltip("x:最小;y:最大")]
    public Vector2 mDistance = new Vector2(3f, 10f);
   

    

    public float mf_HorizontalRotSpeed = 3.0f;
    public float mf_VerticalRotSpeed = 2.0f;
    /// <summary>
    /// 垂直方向旋轉限制
    /// </summary>
    [Tooltip("x:最小;y:最大")] 
    public Vector2 mVerticalRotLimit = new Vector2(-20f,80f);
    /// <summary>
    /// 移動延遲
    /// </summary>
    public float mMoveDelay =10.0f;

    public bool isReset = false;


    public static PlayerCamera m_instance;

   public static PlayerCamera Instance
    {
        get
        {
            if (m_instance == null)
            {
                m_instance = new GameObject("PlayerCamera").AddComponent<PlayerCamera>();
                m_instance.gameObject.AddComponent<DontDestroyOnLoadScript>();
                m_instance.InitCamera();
                m_instance.ResetPos();
            }
            return m_instance;
        }
    }
   
    //ProtectCameraFromWallClip protectCamera;

    public float clipMoveTime = 0.04f;              // time taken to move when avoiding cliping (low value = fast, which it should be)
    public float returnTime = 0.05f;                 // time taken to move back towards desired position, when not clipping (typically should be a higher value than clipMoveTime)
    public float sphereCastRadius = 0.1f;           // the radius of the sphere used to test for object between camera and target
    public bool visualiseInEditor;                  // toggle for visualising the algorithm through lines for the raycast in the editor
    public float closestDistance = 0.5f;            // the closest distance the camera can be from the target
    public bool protecting { get; private set; }    // used for determining if there is an object between the target and the camera
    public string dontClipTag = "Map";           // don't clip against objects with this tag (useful for not clipping against the targeted object)

    private Transform m_Cam;                  // the transform of the camera
    private Transform m_Pivot;                // the point at which the camera pivots around
    public float m_OriginalDist = 0.8f;             // the original distance to the camera before any modification are made
    private float m_MoveVelocity;             // the velocity at which the camera moved
    public float m_CurrentDist;              // the current distance from the camera to the target
    private Ray m_Ray = new Ray();                        // the ray used in the lateupdate for casting between the camera and the target
    private RaycastHit[] m_Hits;              // the hits between the camera and the target
    private RayHitComparer m_RayHitComparer;  // variable to compare raycast hit distances
                                           
    public float mRotMoveDelay = 4f;

    /// <summary>
    ///攝像機 到目標點距離
    /// </summary>
    [Range(0f, 1f)]
    public float mf_DistanceFromTarget = 0.8f;
    /// <summary>
    /// 攝像機距離限制
    /// </summary>
    public Vector2 mv2_Distance = new Vector2(1, 10);


    #region  MonoBehaviour
    public void InitCamera()
    {
        
        m_goPivot = new GameObject("CameraPivot");
        m_goPivot.transform.parent = transform ;
        m_goPivot.transform.localEulerAngles = new Vector3(0, 180, 0);

        if (Camera.main != null)
        {
            Destroy(Camera.main.gameObject);
        }
        m_camPlayer = new GameObject("Camera").AddComponent<Camera>();
        m_camPlayer.tag = "MainCamera";
        m_camPlayer.transform.parent =transform;
        m_camPlayer.transform.localEulerAngles = new Vector3(0,0,0);
        m_camPlayer.transform.localPosition = new Vector3(0,0, 5);
        m_camPlayer.farClipPlane = 3800;

        m_camPlayer.allowMSAA = false;
        m_camPlayer.renderingPath = RenderingPath.Forward;

        m_camPlayer.gameObject.AddComponent<FlareLayer>();
        PostProcessingBehaviour posproBehaviour= m_camPlayer.gameObject.AddComponent<PostProcessingBehaviour>();
        posproBehaviour.profile = (ResourceManager.Instance.LoadObject("CameraPrafeb/MainCameraPP", false) as PostProcessingProfile);



        //初始化穿牆檢測
        // find the camera in the object hierarchy
        m_Cam = m_camPlayer.transform;
        m_Pivot = transform.Find("CameraPivot");
        m_CurrentDist = m_OriginalDist;
        // create a new RayHitComparer
        m_RayHitComparer = new RayHitComparer();


        //protectCamera = gameObject.AddComponent<ProtectCameraFromWallClip>();
        DontDestroyOnLoad(gameObject);



    }
    public void ResetCameraTarget(Transform _target)
    {
        mTarget = _target;
    }


    Text mlogtext;
    private void Start()
    {
        if (RunMode.Instance.bLogWangsy)
        {
            HRotSpeed = mf_HorizontalRotSpeed.ToString();
            VRotSpeed = mf_VerticalRotSpeed.ToString();
            //VRotLimitMin = mVerticalRotLimit.x.ToString();
            //VRotLimitMax = mVerticalRotLimit.y.ToString();
            cameraSpeed = mMoveDelay.ToString();
            mlogtext = GameObject.Find("LogText").GetComponent<Text>();
            mlogtext.text = "sdasdada";
            //camDisLimitMin = protectCamera.mv2_Distance.x.ToString();
            //camDisLimitMax = protectCamera.mv2_Distance.y.ToString();
        }
    }
    string HRotSpeed;//水平旋轉速度
    string VRotSpeed;//垂直旋轉速度
    string VRotLimitMin;//垂直旋轉最小限制
    string VRotLimitMax;//垂直旋轉最大限制
    string cameraSpeed;//攝像機移動速度
    string camDisLimitMin;//攝像機最小距離限制
    string camDisLimitMax;//攝像機最大距離限制
    private void OnGUI()
    {


        if (RunMode.Instance.bLogWangsy)
        {
            GUI.Label(new Rect(300, 50, 80, 50), "水平旋轉速度");
            HRotSpeed = GUI.TextField(new Rect(380, 50, 80, 50), HRotSpeed);

            GUI.Label(new Rect(300, 110, 80, 50), "垂直旋轉速度");
            VRotSpeed = GUI.TextField(new Rect(380, 110, 80, 50), VRotSpeed);

            //    GUI.Label(new Rect(300, 170, 80, 50), "垂直旋轉最小限制");
            //    VRotLimitMin= GUI.TextField(new Rect(380, 170, 80, 50), mVerticalRotLimit.x.ToString());

            //    GUI.Label(new Rect(300, 240, 80, 50), "垂直旋轉最大限制");
            //    VRotLimitMax= GUI.TextField(new Rect(380, 240, 80, 50), VRotLimitMax);

            GUI.Label(new Rect(500, 50, 80, 50), "攝像機移動速度");
            cameraSpeed = GUI.TextField(new Rect(580, 50, 80, 50), cameraSpeed);


            GUI.Label(new Rect(500, 50, 80, 50), "攝像機拉近速度");
            clipMoveTime = float.Parse(GUI.TextField(new Rect(580, 170, 80, 50), clipMoveTime.ToString())) ;

            //    GUI.Label(new Rect(500, 170, 80, 50), "攝像機最小距離限制");
            //    camDisLimitMin= GUI.TextField(new Rect(580, 170, 80, 50), camDisLimitMin);

            //    GUI.Label(new Rect(500, 110, 80, 50), "攝像機最大距離限制");
            //    camDisLimitMax= GUI.TextField(new Rect(580, 110, 80, 50), camDisLimitMax);



            //    GUI.Label(new Rect(500, 240, 80, 50), "當前攝像機角度"+transform.eulerAngles);

            //    GUI.Label(new Rect(500, 320, 80, 50), "當前攝像機距離" + protectCamera.mf_DistanceFromTarget);

            if (GUI.Button(new Rect(100, 100, 80, 80), "更改所有引數"))
            {
                //if (!string.IsNullOrEmpty(camDisLimitMin))
                //    protectCamera.mv2_Distance.x = float.Parse(camDisLimitMin);
                //if (!string.IsNullOrEmpty(camDisLimitMax))
                //    protectCamera.mv2_Distance.y = float.Parse(camDisLimitMax);
                if (!string.IsNullOrEmpty(HRotSpeed))
                    mf_HorizontalRotSpeed = float.Parse(HRotSpeed);
                if (!string.IsNullOrEmpty(VRotSpeed))
                    mf_VerticalRotSpeed = float.Parse(VRotSpeed);
                //if (!string.IsNullOrEmpty(VRotLimitMin))
                //    mVerticalRotLimit.x = float.Parse(VRotLimitMin);
                //if (!string.IsNullOrEmpty(VRotLimitMax))
                //    mVerticalRotLimit.y = float.Parse(VRotLimitMax);
                if (!string.IsNullOrEmpty(cameraSpeed))
                    mMoveDelay = float.Parse(cameraSpeed);
            }
        }
    }
    float mouseX = 0.0f;
    float mouseY = 0.0f;
    Quaternion toRotation=Quaternion.identity;
 
    string msgStr="";

   

    /// <summary>
    /// 放大縮小  穿牆檢測用
    /// </summary>
    Vector3 oldPosition1;
    Vector3 oldPosition2;
    float mNowDistanceFromTarget;
    /// <summary>
    /// 快取手指位置
    /// </summary>
    List<Vector2> temppos;
    /// <summary>
    /// 快取手指按下的Touch
    /// </summary>
    Touch[] touches;

#if (UNITY_ANDROID || UNITY_IOS) && !UNITY_EDITOR
    int unUICount;
    class MyFinger
    {
        /// <summary>
        /// 定義一個手指類
        /// </summary>
        public int id = -1;
        public Touch touch;
    }
    static private List<MyFinger> fingers = new List<MyFinger>();
    /// <summary>  
    /// 手指容器  
    /// </summary>  
    static  List<MyFinger> Fingers
    {
        get
        {
            if (fingers.Count == 0)
            {
                for (int i = 0; i < 5; i++)
                {
                    MyFinger mf = new MyFinger();
                    mf.id = -1;
                    fingers.Add(mf);
                }
            }
            return fingers;
        }
    }
#endif

    void Update()
    {
    
#if UNITY_EDITOR || UNITY_STANDALONE || UNITY_WEBPLAYER


        if (Input.GetMouseButton(0))
        {
            //Debug.Log("選中的UI名字" + EventSystem.current.currentSelectedGameObject.tag);
            if (!EventSystem.current.IsPointerOverGameObject())
            {
                if (CtrlPlayer.bIsJoyCtrl)
                {
                    if (Input.mousePosition.x > Screen.width / 2)
                    {
                        mouseX += Input.GetAxis("Mouse X") * mf_HorizontalRotSpeed;
                        mouseY -= Input.GetAxis("Mouse Y") * mf_VerticalRotSpeed;
                    }
                }
                else
                {
                    mouseX += Input.GetAxis("Mouse X") * mf_HorizontalRotSpeed;
                    mouseY -= Input.GetAxis("Mouse Y") * mf_VerticalRotSpeed;
                }
            }
        }

        if (Input.GetAxis("Mouse ScrollWheel") != 0)
        {
            mf_DistanceFromTarget += Input.GetAxis("Mouse ScrollWheel") * 0.5f;
            mf_DistanceFromTarget = Mathf.Clamp(mf_DistanceFromTarget, 0.1f, 1);
        }

#elif  UNITY_ANDROID||UNITY_IOS

        touches = Input.touches;
        // 遍歷所有的已經記錄的手指  
        // --掦除已經不存在的手指  
        foreach (MyFinger mf in Fingers)
        {
            if (mf.id == -1)
            {
                continue;
            }
            bool stillExit = false;
            foreach (Touch t in touches)
            {
                if (mf.id == t.fingerId)
                {
                    stillExit = true;
                    break;
                }
            }
            // 掦除  
            if (stillExit == false)
            {
                mf.id = -1;
                unUICount--;
            }
        }

        // 遍歷當前的touches  
        // --並檢查它們在是否已經記錄在AllFinger中  
        // --是的話更新對應手指的狀態,不是的放放加進去  
        foreach (Touch t in touches)
        {
            bool still = false;
            // 存在--更新對應的手指  
            foreach (MyFinger mf in Fingers)
            {
                if (t.fingerId == mf.id)
                {
                    still = true;
                    mf.touch = t;
                    break;
                }
            }
            // 不存在--新增新記錄  
            if (!still &&t.phase== TouchPhase.Began && !EventSystem.current.IsPointerOverGameObject(t.fingerId))
            {
                foreach (MyFinger mf in Fingers)
                {
                    if (mf.id == -1)
                    {
                        mf.id = t.fingerId;
                        mf.touch = t;
                        unUICount++;
                        break;
                    }
                }
            }
        }
        if (unUICount == 1)
        {
            // 記錄完手指資訊後,就是響應相應和狀態記錄了  
            foreach (var mf in Fingers)
            {
                if (mf.id != -1)
                {
                    mouseX += mf.touch.deltaPosition.x * 0.05f * mf_HorizontalRotSpeed;
                    mouseY -= mf.touch.deltaPosition.y * 0.05f * mf_VerticalRotSpeed;
                }
            }
        }
        else if (unUICount > 1)
        {
             temppos = new List<Vector2>();
            bool canMove = false;
            foreach (var mf in Fingers)
            {
                if (mf.id != -1)
                {
                    temppos.Add(mf.touch.position);
                    if (mf.touch.phase == TouchPhase.Moved)
                    {
                        canMove = true;
                    }
                }
                if (canMove && temppos.Count > 1)
                {
                    var tempPosition1 = temppos[0];
                    var tempPosition2 = temppos[1];
                    if (isZoom(oldPosition1, oldPosition2, tempPosition1, tempPosition2))
                    {
                        if (mf_DistanceFromTarget > 0)
                            mf_DistanceFromTarget -= 0.05f;
                    }
                    else
                    {
                        if (mf_DistanceFromTarget < 1f)
                            mf_DistanceFromTarget += 0.05f;
                    }

                    oldPosition1 = tempPosition1;
                    oldPosition2 = tempPosition2;
                    break;
                }
            }
        }
#endif
        //攝像機旋轉
        if (mTarget != null)
        {
            m_goPivot.transform.position = mTarget.position;
            mouseY = ClampAngle(mouseY, mVerticalRotLimit.x, mVerticalRotLimit.y);
            toRotation = Quaternion.Euler(mouseY, mouseX, 0);
            transform.rotation = Quaternion.Slerp(transform.rotation, toRotation, Time.deltaTime * mMoveDelay);

            Vector3 toPosition = transform.rotation * new Vector3(0.0f, 0.0f, 0.0f) + mTarget.position;
            Vector3 nowpos = Vector3.Lerp(transform.position, toPosition, 2f);
            transform.position = nowpos;
        }
        else
        {
            GameLog.Wsy_Log("攝像機目標點不存在");
        }
        //攝像機撞牆檢測
        m_OriginalDist = GetCurrentDistance();
        // initially set the target distance
        float targetDist = m_OriginalDist;
        m_Ray.origin = m_Pivot.position + m_Pivot.forward * sphereCastRadius;
        m_Ray.direction = m_Pivot.forward;

        // initial check to see if start of spherecast intersects anything
        var cols = Physics.OverlapSphere(m_Ray.origin, sphereCastRadius);

        bool initialIntersect = false;
        bool hitSomething = false;

        // loop through all the collisions to check if something we care about
        for (int i = 0; i < cols.Length; i++)
        {
            if ((!cols[i].isTrigger) &&
               cols[i].gameObject.CompareTag(dontClipTag))
            {
                initialIntersect = true;
                break;
            }
        }
        // if there is a collision
        if (initialIntersect)
        {
            m_Ray.origin += m_Pivot.forward * sphereCastRadius;

            // do a raycast and gather all the intersections
            m_Hits = Physics.RaycastAll(m_Ray, m_OriginalDist - sphereCastRadius);
        }
        else
        {
            // if there was no collision do a sphere cast to see if there were any other collisions
            m_Hits = Physics.SphereCastAll(m_Ray, sphereCastRadius, m_OriginalDist + sphereCastRadius);
        }
        // sort the collisions by distance
        Array.Sort(m_Hits, m_RayHitComparer);

        // set the variable used for storing the closest to be as far as possible
        float nearest = Mathf.Infinity;

        // loop through all the collisions
        for (int i = 0; i < m_Hits.Length; i++)
        {
            // only deal with the collision if it was closer than the previous one, not a trigger, and not attached to a rigidbody tagged with the dontClipTag
            if (m_Hits[i].distance < nearest && (!m_Hits[i].collider.isTrigger) && m_Hits[i].collider.gameObject.CompareTag(dontClipTag))
            {
                // change the nearest collision to latest
                nearest = m_Hits[i].distance;
                targetDist = m_Pivot.InverseTransformPoint(m_Hits[i].point).z;
                hitSomething = true;
            }
        }
        // visualise the cam clip effect in the editor
        if (hitSomething)
        {
            Debug.DrawRay(m_Ray.origin, m_Pivot.forward * (targetDist + sphereCastRadius), Color.red);
            //m_CurrentDist = targetDist;
        }
        else
        {
            m_CurrentDist = m_OriginalDist;
        }
        // hit something so move the camera to a better position
        protecting = hitSomething;
        m_CurrentDist = Mathf.SmoothDamp(m_CurrentDist, targetDist, ref m_MoveVelocity, m_CurrentDist < targetDist ? clipMoveTime : returnTime);
        m_CurrentDist = Mathf.Clamp(m_CurrentDist, closestDistance, m_OriginalDist);
        m_Cam.localPosition = -Vector3.forward * (m_CurrentDist - 0.5f);
    }
    
#region  Tools
    /// <summary>
    /// 放大/縮小
    /// </summary>
    /// <param name="oP1"></param>
    /// <param name="oP2"></param>
    /// <param name="nP1"></param>
    /// <param name="nP2"></param>
    /// <returns></returns>
    bool isZoom(Vector2 oP1, Vector2 oP2, Vector2 nP1, Vector2 nP2)
    {
        float leng1 = Mathf.Sqrt((oP1.x - oP2.x) * (oP1.x - oP2.x) + (oP1.y - oP2.y) * (oP1.y - oP2.y));
        float leng2 = Mathf.Sqrt((nP1.x - nP2.x) * (nP1.x - nP2.x) + (nP1.y - nP2.y) * (nP1.y - nP2.y));
        if (leng1 < leng2)
            return true;
        else
            return false;
    }
    float GetCurrentDistance()
    {
        mNowDistanceFromTarget = Mathf.Lerp(mNowDistanceFromTarget, mf_DistanceFromTarget, Time.deltaTime * mRotMoveDelay);
        return mv2_Distance.x + (mv2_Distance.y - mv2_Distance.x) * mNowDistanceFromTarget;
    }
#endregion
   
#endregion

    public Vector3 GetForward()
    {
        return m_camPlayer.transform.forward;

    }
    Vector3 StartAngles = new Vector3(20,0,0);

    public void ResetPos()
    {
        float rotY = 0;
        if (MMgr.I.CtrlPlayer.m_PM != null)
        {
            rotY = MMgr.I.CtrlPlayer.m_PM.m_goPlayer.transform.localEulerAngles.y;
        }
        mouseX = StartAngles.y +rotY;
        mouseY = StartAngles.x;
        mf_DistanceFromTarget = 0.8f;
    }
    /// <summary>
    /// 限制旋轉最大/最小值
    /// </summary>
    /// <param name="angle">當前</param>
    /// <param name="min">最小</param>
    /// <param name="max">最大</param>
    /// <returns></returns>
    static float ClampAngle(float angle, float min, float max)
    {
        if (angle < -360)
            angle += 360;
        if (angle > 360)
            angle -= 360;
        return Mathf.Clamp(angle, min, max);
    }
    public class RayHitComparer : IComparer
    {
        public int Compare(object x, object y)
        {
            return ((RaycastHit)x).distance.CompareTo(((RaycastHit)y).distance);
        }
    }
}