zoukankan      html  css  js  c++  java
  • 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 }

    效果图:

  • 相关阅读:
    c# 泛型集合Dictionary
    int+? int后带问题是什么意思,请看内容。
    在ASP.NET中实现Url ReWriting 示例
    #DataDirectory是什么意思呢?
    vs2008生成自定义dll,VS2008发布、生成网站时设置固定的dll文件名?
    平台安装注意事项
    快速开发平台程序运行环境
    快速开发平台介绍(动态)
    快速开发平台程序安装包20120612
    JavaFx 2.0总结
  • 原文地址:https://www.cnblogs.com/jietian331/p/12626530.html
Copyright © 2011-2022 走看看