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 }
效果图: