Unity人物摄像机跟随简单实现

使用了 Mathf.SmoothDamp() 函数,来实现摄像机的平滑过度。

转载请注明出处:https://www.cnblogs.com/jietian331/p/12626530.html

代码如下。

版本一:

  1 using System;
  2 using UnityEngine;
  3 using UnityEngine.EventSystems;
  4 
  5 class PlayerCamera : MonoBehaviour
  6 {
  7     const float
  8         MouseSmoothingFactor = 0.1f,
  9         MaxDistance = 16,
 10         MinDistance = 1f,
 11         MinAngleY = -30f,
 12         MaxAngleY = 60f;
 13 
 14     [SerializeField]
 15     Transform m_target;
 16     [SerializeField]
 17     float m_distance;
 18     [SerializeField]
 19     float m_offsetY;
 20 
 21     Camera m_camera;
 22     float m_mouseX;
 23     float m_mouseY = 30;
 24     float m_mouseXSmooth;
 25     float m_mouseYSmooth;
 26 
 27 
 28     #region singleton
 29 
 30     static PlayerCamera s_instance;
 31     public static PlayerCamera Singleton
 32     {
 33         get
 34         {
 35             if (s_instance == null)
 36             {
 37                 var asset = Resources.Load<PlayerCamera>("Prefabs/Game/PlayerCamera");
 38                 s_instance = GameObject.Instantiate(asset);
 39             }
 40             return s_instance;
 41         }
 42     }
 43 
 44     #endregion
 45 
 46 
 47     #region Property
 48 
 49     float Distance
 50     {
 51         get { return m_distance; }
 52         set { m_distance = Mathf.Clamp(value, MinDistance, MaxDistance); }
 53     }
 54 
 55     public Camera Camera
 56     {
 57         get { return m_camera; }
 58     }
 59 
 60     public static bool ClickedUI
 61     {
 62         get { return GUIUtility.hotControl != 0 || (EventSystem.current != null && EventSystem.current.IsPointerOverGameObject()); }
 63     }
 64 
 65     public bool Active
 66     {
 67         set { gameObject.SetActive(value); }
 68     }
 69 
 70     #endregion
 71 
 72 
 73     static float ClampAngle(float angle, float min, float max)
 74     {
 75         while (angle < -360 || angle > 360)
 76         {
 77             if (angle < -360)
 78                 angle += 360;
 79             if (angle > 360)
 80                 angle -= 360;
 81         }
 82 
 83         return Mathf.Clamp(angle, min, max);
 84     }
 85 
 86 
 87     void Start()
 88     {
 89         DontDestroyOnLoad(gameObject);
 90 
 91         // camera
 92         m_camera = GetComponent<Camera>();
 93 
 94         // setting
 95         Distance = GameConfig.Instance.CameraDefaltDistance;
 96     }
 97 
 98 
 99     public void SetTarget(Transform target, float height)
100     {
101         if (target == null)
102             throw new ArgumentNullException("target");
103 
104         m_target = target;
105         m_offsetY = height * 0.8f;
106     }
107 
108     Vector3 GetSmoothDir()
109     {
110         if (Input.GetKey(KeyCode.Mouse1))
111         {
112             float mouseXAxis = Input.GetAxis("Mouse X");
113             float mouseYAxis = Input.GetAxis("Mouse Y");
114             m_mouseX += mouseXAxis * 6;
115             m_mouseY -= mouseYAxis * 4;
116         }
117         else
118         {
119             bool left = Input.GetKey(KeyCode.Q);
120             bool right = Input.GetKey(KeyCode.E);
121             bool downward = Input.GetKey(KeyCode.Z);
122             bool upward = Input.GetKey(KeyCode.C);
123             bool reset = Input.GetKey(KeyCode.X);
124 
125             if (left || right)
126             {
127                 float eulerY = left ? -2 : 2;
128                 m_mouseX += eulerY;
129             }
130             else if (downward || upward)
131             {
132                 float offsetY = downward ? 2f : -2f;
133                 m_mouseY -= offsetY;
134             }
135             else if (reset)
136             {
137                 m_mouseX = 0;
138                 m_mouseY = 30;
139             }
140         }
141 
142         float currentVelX = 0, currentVelY = 0;
143         m_mouseXSmooth = Mathf.SmoothDamp(m_mouseXSmooth, m_mouseX, ref currentVelX, MouseSmoothingFactor);
144         m_mouseY = ClampAngle(m_mouseY, MinAngleY, MaxAngleY);
145         m_mouseYSmooth = Mathf.SmoothDamp(m_mouseYSmooth, m_mouseY, ref currentVelY, MouseSmoothingFactor);
146         m_mouseYSmooth = ClampAngle(m_mouseYSmooth, MinAngleY, MaxAngleY);
147 
148         // Distance
149         float scrollWheel = Input.GetAxis("Mouse ScrollWheel") * 3;
150         if (scrollWheel != 0)
151             Distance -= scrollWheel;
152 
153         Vector3 offset = new Vector3(0, 0, Distance);
154         Quaternion rotation = Quaternion.Euler(m_mouseYSmooth, m_mouseXSmooth, 0);
155         return rotation * offset;
156     }
157 
158     //  A follow camera should always be implemented in LateUpdate because it tracks objects that might have moved inside Update
159     void LateUpdate()
160     {
161         if (!m_target)
162             return;
163 
164         Vector3 dir = GetSmoothDir();
165         ResetCamera(dir);
166     }
167 
168     void ResetCamera(Vector3 forward)
169     {
170         Vector3 center = m_target.position + new Vector3(0, m_offsetY);
171 
172         // 摄像机不会被物体遮住,用射线检测
173         RaycastHit hit;
174         Ray ray = new Ray(center, -forward);
175         int groundMask = StaticLayers.Ground.GetLayerMask();
176         bool raycast = Physics.Raycast(ray, out hit, Distance, groundMask);
177         Vector3 pos;
178         if (raycast)
179             pos = hit.point + forward * 0.05f;
180         else
181             pos = center - forward;
182 
183         transform.position = pos;
184         transform.LookAt(center);
185     }
186 }

 版本二(支持手机上的操作):

  1 using ClientData;
  2 using Common;
  3 using Common.Messenger;
  4 using Modules.UI;
  5 using UnityEngine;
  6 using UnityEngine.AI;
  7 
  8 public class CameraOperationController : MonoBehaviour
  9 {
 10     public static float
 11     MouseSmoothingFactor = 0.1f,
 12     MaxDistance = 16,
 13     MinDistance = 1f,
 14     DefaultDistance = 4f,
 15     MinAngleY = -30f,
 16     MaxAngleY = 60f,
 17     ScaleSpeed = 10f,
 18     DragSpeed = 0.15f;
 19 
 20     static Camera s_camera;
 21     public static bool CanUsed = true;
 22 
 23 
 24     [SerializeField]
 25     GameObject m_targetGo;
 26     [SerializeField]
 27     float m_distance;
 28     [SerializeField]
 29     float m_offsetY;
 30     [SerializeField]
 31     float m_mouseX;
 32     [SerializeField]
 33     float m_mouseY;
 34 
 35     float m_mouseXSmooth;
 36     float m_mouseYSmooth;
 37 
 38     //摄像机的初始状态
 39     Vector3 m_originalPos;
 40     Vector3 m_originalAngle;
 41     float m_originalView;
 42     GameObject m_originTarget;
 43 
 44     //手机相关
 45     Vector2? m_oldTouch0;
 46     Vector2? m_oldTouch1;
 47 
 48     bool m_isDiplayUI = true;
 49     bool m_toAdjustDistance = false;
 50 
 51 
 52     public static Camera MainCamera
 53     {
 54         get
 55         {
 56             if (!s_camera)
 57             {
 58                 GameObject obj = GameObject.Find("DanceRoot/Main Camera");
 59                 if (obj)
 60                 {
 61                     s_camera = obj.GetComponent<Camera>();
 62                 }
 63             }
 64             return s_camera;
 65         }
 66     }
 67 
 68     float Distance
 69     {
 70         get { return m_distance; }
 71         set { m_distance = Mathf.Clamp(value, MinDistance, MaxDistance); }
 72     }
 73 
 74     float MouseX
 75     {
 76         get { return m_mouseX; }
 77         set { m_mouseX = value; }
 78     }
 79 
 80     float MouseY
 81     {
 82         get { return m_mouseY; }
 83         set
 84         {
 85             m_mouseY = ClampAngle(value, MinAngleY, MaxAngleY);
 86         }
 87     }
 88 
 89 
 90     static float ClampAngle(float angle, float min, float max)
 91     {
 92         while (angle < -360 || angle > 360)
 93         {
 94             if (angle < -360)
 95                 angle += 360;
 96             if (angle > 360)
 97                 angle -= 360;
 98         }
 99 
100         return Mathf.Clamp(angle, min, max);
101     }
102 
103 
104     //  A follow camera should always be implemented in LateUpdate because it tracks objects that might have moved inside Update
105     void LateUpdate()
106     {
107         if (CanUsed && m_targetGo)
108         {
109             ResetCamera();
110         }
111     }
112 
113     void ResetCamera()
114     {
115         // get smooth dir
116         float currentVelX = 0, currentVelY = 0;
117         m_mouseXSmooth = Mathf.SmoothDamp(m_mouseXSmooth, MouseX, ref currentVelX, MouseSmoothingFactor);
118         m_mouseYSmooth = Mathf.SmoothDamp(m_mouseYSmooth, MouseY, ref currentVelY, MouseSmoothingFactor);
119         Quaternion rotation = Quaternion.Euler(m_mouseYSmooth, m_mouseXSmooth, 0);
120         Vector3 dir = new Vector3(0, 0, -Distance);
121         dir = rotation * dir;
122 
123         Vector3 center = m_targetGo.transform.position + new Vector3(0, m_offsetY);
124 
125         // 摄像机不会被物体遮住,用射线检测
126         RaycastHit hit;
127         Ray ray = new Ray(center, dir);
128         int mask = 1 << (int)CameraLayer.Default | 1 << (int)CameraLayer.Obstacles | 1 << (int)CameraLayer.Ground;
129         bool raycast = Physics.Raycast(ray, out hit, Distance, mask);
130         Vector3 pos;
131         if (raycast)
132         {
133             pos = hit.point - dir * 0.05f;
134         }
135         else
136         {
137             pos = center + dir;
138         }
139 
140         MainCamera.transform.position = pos;
141         MainCamera.transform.LookAt(center);
142 
143 #if UNITY_EDITOR
144         Debug.DrawLine(m_targetGo.transform.position, center, Color.green);
145         Debug.DrawLine(center, pos, Color.green);
146 #endif
147     }
148 
149     void OnDrag(Vector2 delta)
150     {
151         if (!m_toAdjustDistance)
152         {
153             MouseX += delta.x * DragSpeed;
154             MouseY -= delta.y * DragSpeed * 2f / 3f;
155         }
156     }
157 
158     void Update()
159     {
160         if (!CanUsed)
161         {
162             return;
163         }
164 
165 #if UNITY_EDITOR
166         if (Application.platform == RuntimePlatform.WindowsEditor || Application.platform == RuntimePlatform.WindowsPlayer)
167         {
168             float scrollWheel = Input.GetAxis("Mouse ScrollWheel");
169             if (scrollWheel != 0)
170             {
171                 Distance -= scrollWheel * 3;
172             }
173         }
174 #endif
175 
176         if (Application.platform == RuntimePlatform.Android || Application.platform == RuntimePlatform.IPhonePlayer)
177         {
178             m_toAdjustDistance = Input.touchCount > 1 && !JoyStick.isUsingJoyStick;
179             if (m_toAdjustDistance)
180             {
181                 var touch0 = Input.GetTouch(0);
182                 var touch1 = Input.GetTouch(1);
183                 if (touch0.phase == TouchPhase.Moved || touch1.phase == TouchPhase.Moved)
184                 {
185                     Vector3 pos0 = touch0.position;
186                     Vector3 pos1 = touch1.position;
187                     float speed = ScaleSpeed * Time.deltaTime;
188 
189                     if (m_oldTouch0.HasValue && m_oldTouch1.HasValue)
190                     {
191                         if (IsEnlarge(m_oldTouch0.Value, m_oldTouch1.Value, pos0, pos1))
192                         {
193                             Distance -= speed;
194                         }
195                         else
196                         {
197                             Distance += speed;
198                         }
199                     }
200 
201                     m_oldTouch0 = pos0;
202                     m_oldTouch1 = pos1;
203                 }
204             }
205             else
206             {
207                 m_oldTouch0 = null;
208                 m_oldTouch1 = null;
209             }
210         }
211 
212     }
213 
214     // 是否是放大
215     bool IsEnlarge(Vector3 oldP1, Vector3 oldP2, Vector3 newP1, Vector3 newP2)
216     {
217         float oldXDis = oldP1.x - oldP2.x;
218         float oldYDis = oldP1.y - oldP2.y;
219         float newXDis = newP1.x - newP2.x;
220         float newYDis = newP1.y - newP2.y;
221         float oldV = oldXDis * oldXDis + oldYDis * oldYDis;
222         float newV = newXDis * newXDis + newYDis * newYDis;
223         return oldV < newV;
224     }
225 
226     void OnChangeCameraState(bool isOpen, GameObject target, float showDistance)
227     {
228         if (isOpen && target)
229         {
230             m_targetGo = target;
231             Distance = showDistance;
232         }
233         else
234         {
235             m_targetGo = m_originTarget;
236             DefaultCamera();
237         }
238     }
239 
240 
241     void DefaultCamera()
242     {
243         Distance = DefaultDistance;
244         MouseX = 0;
245         MouseY = 0;
246 
247         var agent = m_targetGo.GetComponent<NavMeshAgent>();
248         if (agent)
249         {
250             m_offsetY = agent.height * 0.9f;
251         }
252         else
253         {
254             m_offsetY = 1.6f;
255         }
256     }
257 
258     void Awake()
259     {
260         Messenger.AddListener(MessengerEventDef.RecardCamState, RecardCamState);
261         Messenger.AddListener(MessengerEventDef.ResetCamState, ResetCamState);
262         Messenger<bool>.AddListener(MessengerEventDef.ChangeFollowTargetBone, FollowTargetBone);
263         Messenger<bool>.AddListener(MessengerEventDef.OnChangeUIDisplayState, OnChangeUIDisplayState);
264         Messenger<bool, GameObject, float>.AddListener(MessengerEventDef.ChangeRoomCameraState, OnChangeCameraState);
265 
266         m_originalPos = MainCamera.transform.position;
267         m_originalAngle = MainCamera.transform.eulerAngles;
268         m_originalView = MainCamera.fieldOfView;
269         MainCamera.fieldOfView = 30;
270         m_targetGo = AppInterface.RoomModule.RoomPlayer.player.gameObject;
271         m_originTarget = m_targetGo;
272 
273         /*
274         RoomBase currRoom = AppInterface.RoomModule.GetRoom();
275         m_x = currRoom.EModel == eRoomModel.Waite || currRoom.EModel == eRoomModel.SpiritCity ? 540 : 0;
276         m_y = currRoom.EModel == eRoomModel.Waite || currRoom.EModel == eRoomModel.SpiritCity ? -3 : 0;  //-22.5
277 
278         if (currRoom.EModel == eRoomModel.DanceGroup)
279         {
280             m_x = -136;
281             m_y = -3.0f;
282             m_z = -7;
283         }
284         else if (currRoom.EModel == eRoomModel.Waite || currRoom.EModel == eRoomModel.MarryRoom || currRoom.EModel == eRoomModel.IdolNoticeRoom || currRoom.EModel == eRoomModel.IdolPlayoffRoom)
285         {
286             m_x = ClientRoom.cam_X;
287             m_y = ClientRoom.cam_Y;
288             m_z = ClientRoom.cam_Z;
289         }
290         else //if (currRoom.EModel == eRoomModel.SpiritCity || currRoom.EModel == eRoomModel.Guide_SpiritCity)
291         {
292             m_x = 540;
293             m_y = -3.0f;
294             m_z = -7;
295         }*/
296 
297         DefaultCamera();
298     }
299 
300     void FollowTargetBone(bool isFollow)
301     {
302         var player = AppInterface.RoomModule.RoomPlayer.player;
303         m_targetGo = isFollow ? player.Bip001.gameObject : player.gameObject;
304     }
305 
306     void OnClick()
307     {
308         if (!m_isDiplayUI)
309         {
310             Messenger<bool>.Broadcast(MessengerEventDef.OnChangeUIDisplayState, true);
311         }
312         else
313         {
314             Vector3 mousePos = Input.mousePosition;
315             Ray ray = MainCamera.ScreenPointToRay(mousePos);
316             Messenger<Ray>.Broadcast(MessengerEventDef.RoomOnClickRay, ray);
317         }
318     }
319 
320     void RecardCamState()
321     {
322         //ClientRoom.cam_X = m_x;
323         //ClientRoom.cam_Y = m_y;
324         //ClientRoom.cam_Z = m_z;
325     }
326 
327     void ResetCamState()
328     {
329         ClientRoom.cam_X = 540.0f;
330         ClientRoom.cam_Y = -3.0f;
331         ClientRoom.cam_Z = -7.0f;
332     }
333 
334     void OnDestroy()
335     {
336         Messenger.RemoveListener(MessengerEventDef.RecardCamState, RecardCamState);
337         Messenger.RemoveListener(MessengerEventDef.ResetCamState, ResetCamState);
338         Messenger<bool>.RemoveListener(MessengerEventDef.ChangeFollowTargetBone, FollowTargetBone);
339         Messenger<bool>.RemoveListener(MessengerEventDef.OnChangeUIDisplayState, OnChangeUIDisplayState);
340         Messenger<bool, GameObject, float>.RemoveListener(MessengerEventDef.ChangeRoomCameraState, OnChangeCameraState);
341 
342         if (MainCamera)
343         {
344             MainCamera.transform.position = m_originalPos;
345             MainCamera.transform.eulerAngles = m_originalAngle;
346             MainCamera.fieldOfView = m_originalView;
347         }
348 
349         if (!m_isDiplayUI)
350         {
351             Messenger<bool>.Broadcast(MessengerEventDef.OnChangeUIDisplayState, true);
352         }
353     }
354 
355     void OnChangeUIDisplayState(bool isDisplay)
356     {
357         m_isDiplayUI = isDisplay;
358     }
359 }

效果图:

 

posted @ 2020-04-03 14:57  孤独の巡礼  阅读(1295)  评论(0编辑  收藏  举报