# 實作的功能
(1) 滾輪拉近, 推遠相機(帶慣性)
(2) 滑鼠左鍵左右,上下轉動相機(帶慣性)
(3) 相機跟隨角色
# 待實作功能
(1) 轉動相機時,如果相機和跟隨角色間出現了障礙物,相機自動拉近
(2) 跟隨的角色向左或向右行走時,相機自動緩慢轉動

# ground為可行走地面(綠色),Sphere為不可行走區域(灰色),player為相機跟隨的角色(黃色)
# EventSystem這邊會用到它的拖動觸發閾值
# CameraControl掛在Main Camera上, PlayerMove掛在player上
# 相機控制代碼
public class CameraControl : MonoBehaviour { public Transform target; //相機跟隨目標 private Vector3 _lastTargetPos; public Vector3 offset = new Vector3(0, 1.35f, 0); //與target位置的偏移, 一般只設定y方向(高度) public float zDistanceMin = 2; public float zDistanceMax = 12; private float _zDistance = 9; //相機和跟隨目標當前的距離 private int _zDistanceInertia = 0; //慣性幀數 private float _zDistanceInertiaStep; //慣性步進 private int _rotateInertia = 0; //旋轉慣性幀數 private float _rotateDecelerateExp = 0.75f; //減速指數 private Vector3 _lastMousePosition; private Vector3 _deltaMousePosition; private bool _drag; private float _xCurRotate = 5; public bool xRotateLock = false; public float xRotateMin = 5; public float xRotateMax = 40; private float _yCurRotate; public bool yRotateLock = false; private Quaternion _camRotation; private Vector3 _zDistanceVec3; void OnEnable() { UpdateCameraRotateAndPos(); } void Update() { CheckZoomInertia(); CheckRotateInertia(); var mscroll = Input.GetAxis("Mouse ScrollWheel"); if (mscroll < 0) //zoom out, 推遠相機 { if (_zDistance < zDistanceMax) { _zDistance -= mscroll; _zDistance = Mathf.Min(_zDistance, zDistanceMax); UpdateCameraPos(); _zDistanceInertia = 15; _zDistanceInertiaStep = mscroll; } } else if (mscroll > 0) //Zoom in, 拉進相機 { if (_zDistance > zDistanceMin) { _zDistance -= mscroll; _zDistance = Mathf.Max(_zDistance, zDistanceMin); UpdateCameraPos(); _zDistanceInertia = 15; _zDistanceInertiaStep = mscroll; } } if (Input.GetMouseButtonDown(0)) { _drag = false; _lastMousePosition = Input.mousePosition; //按下的時候, 停止慣性 _zDistanceInertia = 0; _rotateInertia = 0; } else if (Input.GetMouseButton(0)) { _deltaMousePosition = Input.mousePosition - _lastMousePosition; _lastMousePosition = Input.mousePosition; if (_drag) { RotateCameraByDeltaPos(ref _deltaMousePosition); } else { if (_deltaMousePosition.sqrMagnitude >= Mathf.Sqrt(EventSystem.current.pixelDragThreshold)) //超過拖動閾值才進入drag模式 { _drag = true; } } } else if (Input.GetMouseButtonUp(0)) { if (_drag) { _rotateInertia = 15; } _drag = false; _lastMousePosition = Input.mousePosition; } } ///相機zoom慣性 private void CheckZoomInertia() { if (_zDistanceInertia > 0) { _zDistanceInertia--; if (_zDistanceInertiaStep < 0) //慣性推遠 { if (_zDistance < zDistanceMax) { _zDistance -= _zDistanceInertiaStep; _zDistance = Mathf.Min(_zDistance, zDistanceMax); UpdateCameraPos(); } else { _zDistanceInertia = 0; _zDistanceInertiaStep = 0; } } else if (_zDistanceInertiaStep > 0) //慣性拉進 { if (_zDistance > zDistanceMin) { _zDistance -= _zDistanceInertiaStep; _zDistance = Mathf.Max(_zDistance, zDistanceMin); UpdateCameraPos(); } else { _zDistanceInertia = 0; _zDistanceInertiaStep = 0; } } } } ///轉動相機慣性 private void CheckRotateInertia() { if (_rotateInertia > 0) { _rotateInertia--; if (_deltaMousePosition.sqrMagnitude >= 0.0001f) { RotateCameraByDeltaPos(ref _deltaMousePosition); _deltaMousePosition *= _rotateDecelerateExp; } else { _rotateInertia = 0; } } } private void RotateCameraByDeltaPos(ref Vector3 deltaPos) { const float Scale_Factor = 0.2f; //縮放系數, 調到看著舒服就行 if (!xRotateLock) { _xCurRotate += deltaPos.y * Scale_Factor; _xCurRotate = Mathf.Min(Mathf.Max(xRotateMin, _xCurRotate), xRotateMax); } if (!yRotateLock) { _yCurRotate += deltaPos.x * Scale_Factor; } UpdateCameraRotateAndPos(); } private void UpdateCameraRotateAndPos() { _camRotation = Quaternion.Euler(_xCurRotate, _yCurRotate, 0); transform.rotation = _camRotation; UpdateCameraPos(); //角度變化同時會引起位置變化 } private void UpdateCameraPos() { _zDistanceVec3.z = -_zDistance; var camPos = _camRotation * _zDistanceVec3 + target.position + offset; transform.position = camPos; } void LateUpdate() { //相機跟隨 var diff = target.position - _lastTargetPos; if (diff.sqrMagnitude >= 0.0001f) { _lastTargetPos = target.position; var camPos = _camRotation * _zDistanceVec3 + target.position + offset; transform.position = camPos; } } }
# 角色行走控制代碼,wasd為手動行走,滑鼠右鍵點擊地面為走到點擊的點
public class PlayerMove : MonoBehaviour { private NavMeshAgent _agent; ///跟隨角色的相機 public Camera followCamera; private Vector3[] _pathCorners = new Vector3[10]; void Start() { _agent = GetComponent<NavMeshAgent>(); _agent.isStopped = true; if (null == followCamera) followCamera = Camera.main; } void Update() { if (CheckNavToClickPoint() || CheckWASDMove()) return; if (_agent.enabled) { if (_agent.remainingDistance <= 0 || _agent.isStopped) { _agent.isStopped = true; _agent.enabled = false; } else { DrawNavMeshPath(); } } } ///尋路程序中, 繪制出路徑 private void DrawNavMeshPath() { if (_agent.hasPath) { var len = _agent.path.GetCornersNonAlloc(_pathCorners); for (var i = 1; i < len; ++i) { var p1 = _pathCorners[i - 1]; var p2 = _pathCorners[i]; Debug.DrawLine(p1, p2, Color.red); } } } ///尋路至點擊位置 private bool CheckNavToClickPoint() { if (Input.GetMouseButtonUp(1)) { const int maxDistance = 300; var ray = Camera.main.ScreenPointToRay(Input.mousePosition); //攝像機方向發射1條射線 Debug.DrawRay(ray.origin, ray.direction * maxDistance, Color.yellow); //畫出這條射線 if (Physics.Raycast(ray, out var hit, maxDistance)) { Debug.DrawLine(ray.origin, hit.point, Color.red); _agent.enabled = true; //_agent.Warp(transform.position); _agent.SetDestination(hit.point); return true; } } return false; } private bool CheckWASDMove() { var x = 0; if (Input.GetKey(KeyCode.W)) x = 1; else if (Input.GetKey(KeyCode.S)) x = -1; var y = 0; if (Input.GetKey(KeyCode.A)) y = -1; else if (Input.GetKey(KeyCode.D)) y = 1; if (0 != y || 0 != x) { if (_agent.enabled) //還在尋路中, 操作了wasd, 就停止尋路 { _agent.isStopped = true; _agent.ResetPath(); _agent.enabled = false; } var relativeAngle = Mathf.Atan2(y, x) * Mathf.Rad2Deg; //搖桿向上表示與相機forward為0度, 搖桿向右表示與相機forward為90度, 以此類推 var playerTransform = transform; var cameraForwardAngle = followCamera.transform.eulerAngles.y; playerTransform.rotation = Quaternion.Euler(0, cameraForwardAngle + relativeAngle, 0); //todo: 這里可以考慮改成Lerp做平滑處理 playerTransform.Translate(Vector3.forward * 3 * Time.deltaTime, Space.Self); //上面已經調整了轉向, 這邊只要往前走就好 return true; } return false; } }
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/498643.html
標籤:其他
