Unity攝像機跟隨 Touch遮蔽UI 觸控控制攝像機旋轉,手勢控制放大縮小 穿牆拉近攝像機
阿新 • • 發佈:2019-02-02
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); } } }