栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 游戏开发 > 其他

Unity 第三人称自由视角

其他 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Unity 第三人称自由视角

环绕相机控制脚本

该脚本需要挂载在环绕相机上,而环绕相机不绑定在角色身上,而是作为一个单独的存在

此外,由于一般来说,角色的原点都在脚底,所以需要特别在角色的中心位置放置一个空对象作为视野中心,并在面板指定

相机被遮挡的判断与处理

具体分析见  Unity 相机被遮挡的判断与处理

相机的惯性旋转

具体分析见  Unity 自由视角的惯性旋转

效果

操作方式

按下鼠标左键并拖拽,可以让相机在上下和左右方向以角色为轴心旋转

鼠标滚轮可以调节视角的大小

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SurroundCamera : MonoBehaviour
{
    //视野中心
    public Transform focus;

    //相机相对角色的位置
    Vector3 RelativePosition;

    void Start()
    {
        RelativePosition = transform.position - focus.position;     //以人为原点A,相机为B,向量AB=B-A,B的坐标等于向量AB。
        unit = RelativePosition.magnitude;
    }

    void Update()
    {
        Follow();               //相机跟随

        if (cameraRotateBy == CameraRotateBy.MouseVelocity)              //调节视角
            DragToRotateView_Velocity();
        else
            DragToRotateView_Distance();

        OcclusionJudge();               //视野遮挡判断

        if (scaleViewBy == ScaleViewBy.Distance)              //调节视野
            ScrollToScaleDistance();
        else if (scaleViewBy == ScaleViewBy.FieldOfView)
            ScrollToScaleView();
        else if (scaleViewBy == ScaleViewBy.Level)
            ScrollToAdjustView();
    }

    

    void Follow()
    {
        transform.position = focus.position + RelativePosition;             //每一帧都跟随移动
    }

    

    //相机旋转方案
    public CameraRotateBy cameraRotateBy = CameraRotateBy.MouseVelocity;
    public enum CameraRotateBy
    {
        MouseVelocity,
        Distance,
    }

    //最小水平夹角
    public float MinimumDegree = 0;
    //最大水平夹角
    public float MaximumDegree = 60;
    //两点连线与水平方向的夹角
    float currentAngleY;

    

    float mouseVelocityX;
    float mouseVelocityY;
    Vector3? point1;
    //旋转每度,在一帧中需要的速度
    int DragVelocityPerAngle = 170;

    //脱手瞬间鼠标速度
    float lastMouseVelocityX;
    float lastMouseVelocityY;

    void DragToRotateView_Velocity()
    {
        if (Input.GetMouseButton(0))                //按下鼠标左键的每一帧都执行
        {
            var point2 = Input.mousePosition;
            if (point1 != null)
            {
                mouseVelocityX = -(point1.Value.x - point2.x) / Time.deltaTime;
                mouseVelocityY = -(point1.Value.y - point2.y) / Time.deltaTime;
            }

            point1 = point2;

            float anglex = mouseVelocityX / DragVelocityPerAngle;                   //将鼠标在屏幕上拖拽的速度转化为角度
            float angley = mouseVelocityY / DragVelocityPerAngle;

            currentAngleY = 90 - Vector3.Angle(-RelativePosition, Vector3.down);            //计算两点连线与水平方向的夹角

            if (currentAngleY - angley > MaximumDegree || currentAngleY - angley < MinimumDegree)
                angley = 0;

            transform.RotateAround(focus.position, Vector3.up, anglex);
            transform.RotateAround(focus.position, -transform.right, angley);

            transform.LookAt(focus);                    //如果没有这一句,摄像头转着转着就会歪

            RelativePosition = transform.position - focus.position;                 //更新相对位置
        }

        if(Input.GetMouseButtonUp(0))       //脱手瞬间
        {
            point1 = null;

            inertialRotation = true;
            lastMouseVelocityX = mouseVelocityX;
            lastMouseVelocityY = mouseVelocityY;
            if (lastMouseVelocityX > maxlastMouseVelocityX) lastMouseVelocityX = maxlastMouseVelocityX;
            else if (lastMouseVelocityX < -maxlastMouseVelocityX) lastMouseVelocityX = -maxlastMouseVelocityX;

            if (lastMouseVelocityX > 0) isCounterClockwise = true;
            else if (lastMouseVelocityX < 0) isCounterClockwise = false;
            //print(lastMouseVelocityX);
        }

        
        if(inertialRotation==true)
            StartCoroutine("InertialRotation");     //通过协程来实现视角的惯性旋转,调用协程只有写在Update里并且在每一帧都被调用时才会继续执行
    }

    bool inertialRotation = false;      //是否需要视角的惯性旋转
    float maxlastMouseVelocityX = 3000; 
    bool isCounterClockwise;            //旋转方向
    IEnumerator InertialRotation()      //在旋转末尾补上一个逐渐减缓的惯性旋转
    {
        yield return null;

        float anglex = lastMouseVelocityX / DragVelocityPerAngle;                   //将鼠标在屏幕上拖拽的速度转化为角度
        float angley = lastMouseVelocityY / DragVelocityPerAngle;

        currentAngleY = 90 - Vector3.Angle(-RelativePosition, Vector3.down);            //计算两点连线与水平方向的夹角

        if (currentAngleY - angley > MaximumDegree || currentAngleY - angley < MinimumDegree + 10)
            angley = 0;

        lastMouseVelocityX -= lastMouseVelocityX * 0.08f;
        lastMouseVelocityY -= lastMouseVelocityY * 0.08f;

        //print(lastMouseVelocityX);

        if ((isCounterClockwise && (anglex < 1))||!isCounterClockwise && (anglex > -1))
        {
            StopCoroutine("InertialRotation");
            inertialRotation = false;
        }    
        transform.RotateAround(focus.position, Vector3.up, anglex/3);
        transform.RotateAround(focus.position, -transform.right, Mathf.Abs(angley/25));
        transform.LookAt(focus);
        RelativePosition = transform.position - focus.position;
    }

    

    Vector3 Point1;
    Vector3 Point2;
    //旋转每度,在一帧中需要拖拽的距离
    int DragDistancePerAngle = 20;

    void DragToRotateView_Distance()
    {
        float v = Input.GetAxis("Vertical");
        float h = Input.GetAxis("Horizontal");
        if (!(h==0&&v==0))                  //不运动时的旋转灵敏度
        {
            DragDistancePerAngle = 17;      //松手前拖拽灵敏度
            sactor = 10;                    //松手后拖拽灵敏度
        }
        else                                //运动时的旋转灵敏度
        {
            DragDistancePerAngle = 8;
            sactor = 4;
        }

        if (Input.GetMouseButtonDown(0))                //按下鼠标左键的瞬间,记录起始位置
        {
            Point1 = Input.mousePosition;
            StartPoint = Point1;
        }

        if (Input.GetMouseButton(0))                //按下鼠标左键的每一帧都执行
        {
            Point2 = Input.mousePosition;
            float dx = Point2.x - Point1.x;
            float dy = Point2.y - Point1.y;

            float anglex = dx / DragDistancePerAngle;                   //将鼠标在屏幕上拖拽的距离转化为角度
            float angley = dy / DragDistancePerAngle;

            currentAngleY = 90 - Vector3.Angle(-RelativePosition, Vector3.down);                    //计算两点连线与水平方向的夹角

            if (currentAngleY - angley > MaximumDegree || currentAngleY - angley < MinimumDegree)
                angley = 0;

            transform.RotateAround(focus.position, Vector3.up, anglex);
            transform.RotateAround(focus.position, -transform.right, angley);

            transform.LookAt(focus);                    //如果没有这一句,摄像头转着转着就会歪

            RelativePosition = transform.position - focus.position;                 //更新相对位置

            Point1 = Point2;
            Point2 = Vector3.zero;
        }

        if(Input.GetMouseButtonUp(0))
        {
            EndPoint = Input.mousePosition;
            if (Point1!=EndPoint)                       //鼠标无速度则不进行惯性旋转
                inertialRotation = true;
            dragX = EndPoint.x - StartPoint.x;
            dragY = EndPoint.y - StartPoint.y;
            if (dragX > maxdragX) dragX = maxdragX;
            else if (dragX < -maxdragX) dragX = -maxdragX;

            if (dragX > 0) isCounterClockwise = true;
            else if (dragX < 0) isCounterClockwise = false;
            print(dragX);
        }

        if (inertialRotation == true)
            StartCoroutine("InertialRotation2");
    }

    Vector3 StartPoint;     //拖拽起点
    Vector3 EndPoint;       //拖拽终点
    float dragX;        //水平拖拽距离
    float dragY;        //垂直拖拽距离
    float maxdragX = 3000;
    float sactor = 10;  //惯性系数
    IEnumerator InertialRotation2()      //在旋转末尾补上一个逐渐减缓的惯性旋转
    {
        yield return null;

        float anglex = dragX / DragDistancePerAngle / sactor;                   //将鼠标在屏幕上拖拽的距离转化为角度
        float angley = dragY / DragDistancePerAngle / sactor;                   

        currentAngleY = 90 - Vector3.Angle(-RelativePosition, Vector3.down);            //计算两点连线与水平方向的夹角

        if (currentAngleY - angley > MaximumDegree || currentAngleY - angley < MinimumDegree + 10)
            angley = 0;


        dragX -= dragX * 0.05f;
        dragY -= dragY * 0.05f;

        print(dragX);

        if ((isCounterClockwise && (anglex < 1)) || !isCounterClockwise && (anglex > -1))
        {
            StopCoroutine("InertialRotation2");
            inertialRotation = false;
        }
        transform.RotateAround(focus.position, Vector3.up, anglex / 4);
        transform.RotateAround(focus.position, -transform.right, Mathf.Abs(angley/4));
        transform.LookAt(focus);
        RelativePosition = transform.position - focus.position;
    }

    

    //鼠标滚轮灵敏度
    float mouseWheelSensitivity = 30;

    //视野调整方案
    public enum ScaleViewBy
    {
        Distance,
        FieldOfView,
        Level,
    }

    //视野选择列表
    public ScaleViewBy scaleViewBy = ScaleViewBy.Level;

    

    float MinFieldOfView = 20f;
    float MaxFieldOfView = 100f;

    void ScrollToScaleView()
    {
        if (Input.GetAxis("Mouse ScrollWheel") == 0) return;

        GetComponent().fieldOfView = GetComponent().fieldOfView - Input.GetAxis("Mouse ScrollWheel") * mouseWheelSensitivity;
        GetComponent().fieldOfView = Mathf.Clamp(GetComponent().fieldOfView, MinFieldOfView, MaxFieldOfView);
    }

    

    float MinViewDistance = 1;
    float MaxViewDistance = 4;

    void ScrollToScaleDistance()
    {
        if (Input.GetAxis("Mouse ScrollWheel") > 0)
        {
            if (RelativePosition.magnitude <= MinViewDistance) return;

            transform.Translate(-RelativePosition / mouseWheelSensitivity *10, Space.World);
            RelativePosition = transform.position - focus.position;
        }
        else if (Input.GetAxis("Mouse ScrollWheel") < 0)
        {
            if (RelativePosition.magnitude >= MaxViewDistance) return;

            transform.Translate(RelativePosition / mouseWheelSensitivity *10, Space.World);
            RelativePosition = transform.position - focus.position;
        }
    }

    

    //单位长度
    float unit;

    void ScrollToAdjustView()
    {
        if (Input.GetAxis("Mouse ScrollWheel") > 0)
        {
            ViewPlus();
            preferdLevel = currentLevel;
        }
        else if (Input.GetAxis("Mouse ScrollWheel") < 0)
        {
            ViewMinus();
            preferdLevel = currentLevel;
        }
    }

    void ViewPlus()
    {
        if (RelativePosition.magnitude <= MinViewDistance) return;
        transform.Translate(-RelativePosition.normalized * unit, Space.World);
        RelativePosition = transform.position - focus.position;
        currentLevel--;
    }

    void ViewMinus()
    {
        if (RelativePosition.magnitude >= MaxViewDistance) return;
        transform.Translate(RelativePosition.normalized * unit, Space.World);
        RelativePosition = transform.position - focus.position;
        currentLevel++;
    }

    

    

    //当前视角级别
    int currentLevel = 1;
    //偏好视野级别
    int preferdLevel = 1;

    //是否需要恢复原机位
    bool resumable = false;

    void OcclusionJudge()
    {
        if (Physics.Raycast(transform.position, -RelativePosition.normalized, RelativePosition.magnitude - unit))               //如果机位被遮挡
        {
            resumable = true;
            while (Physics.Raycast(transform.position, -RelativePosition.normalized, RelativePosition.magnitude - unit))
            {
                ViewPlus();
            }
        }

        if (!resumable) return;             //如果不需要恢复

        Vector3 PositionToResume = focus.position + RelativePosition.normalized * unit * preferdLevel;              //计算偏好距离所在位置

        if (resumable && !Physics.Raycast(PositionToResume, -RelativePosition.normalized, (preferdLevel - 1) * unit))       //原机位没被遮挡,恢复原位
        {
            while (currentLevel != preferdLevel)
            {
                ViewMinus();
            }
            resumable = false;
        }
    }

    //todo
}

自由视角的角色控制脚本

不同于一般的固定视角的角色控制脚本,该脚本适用于自由视角的角色控制。

该脚本需要挂载在角色上,并指定环绕相机的Transform属性。

操作方式

W 朝着屏幕前方走        S 面向屏幕走        A 朝着屏幕左边走        D 朝着屏幕右边走      

空格 跳跃        LeftShift 行走

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//Move by Screen Reference System
public class PlayerControl : MonoBehaviour
{
    public Transform camera;
    private Rigidbody rigidbody;
    public float moveSpeed = 4;
    public float jumpForce = 200f;
    Animator anim;
    private int jumpLimit = 2;      //二段跳

    void Start()
    {
        rigidbody = GetComponent();
        anim = GetComponent();
    }

    void FixedUpdate()
    {
        Move();
        Jump();
    }

    void Move()
    {
        float v = Input.GetAxis("Vertical");
        float h = Input.GetAxis("Horizontal");

        if (Input.GetKey(KeyCode.LeftShift))
        {
            h *= 0.5f;
            v *= 0.5f;
        }

        anim.SetFloat("SpeedX", h);
        anim.SetFloat("SpeedY", v);

        Vector3 screenRight = camera.right;             //以屏幕为参考系移动
        Vector3 screenForward = camera.forward;
        screenForward.y = 0;                            //不能有竖直分量

        Vector3 sumVector = screenForward * v + screenRight * h;                //矢量之和

        if (!(h==0&&v==0))
        {
            transform.rotation = Quaternion.LookRotation(sumVector);
        }
        transform.Translate(sumVector * moveSpeed * Time.deltaTime, Space.World);       //Space.World绝对不能少
    }

    bool IsGrounded()                   // 通过射线检测角色是在地面或者物体(角色的零点需要设置在脚底处)
    {
        return Physics.Raycast(transform.position, -Vector3.up, 0.1f);
    }

    void Jump()
    {
        if (IsGrounded())                       //如果接触地面,则恢复可跳跃次数
        {
            jumpLimit = 2;
            anim.SetBool("Jump", false);
        }

        if (Input.GetKeyDown(KeyCode.Space))
        {
            if (jumpLimit > 0)
            {
                rigidbody.AddForce(Vector3.up * jumpForce);
                anim.SetBool("Jump", true);
                jumpLimit--;
            }
        }
    }
}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/905968.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号