# 实现的功能

(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.cnblogs.com/sailJs/p/16386361.html

你还没有登录,请先登录注册
  • 还没有人评论,欢迎说说您的想法!