Unity组件开发--相机跟随角色和旋转

1.相机跟随组件,节点:

2.相机跟随组件脚本:

cs 复制代码
using System;
using System.Collections;
using System.Collections.Generic;
using Unity.Burst.Intrinsics;
using UnityEngine;
using UnityEngine.UI;

public class CameraFollow : Singleton<CameraFollow> {
    public Transform firstAngleTarget; //第一人称跟随的目标
    public Transform threeAngleTarget; //第三人称跟随的目标
    public float radius;
    public float polarDeg;
    public float elevationDeg;
    private Transform target;
    public bool isLookAt;

    public float lerpSpeed = 0;

    //是否第一人称视角
    private bool isFirstAngle = false;

    LayerMask mask;
    /// <summary>
    /// 极坐标转换成笛卡尔坐标
    /// </summary>
    /// <param name="radius"></param>
    /// <param name="angle"></param>
    /// <returns></returns>
    /// 

    private bool isDriven;

    private void Awake() {
        EventManager.Instance.AddListener(EventName.PlayerDriving, (s, e) => {  //开车事件触发
            var arg = e as PlayerDrivingEventArgs;
            if (arg.driveObj && arg.driveObj.GetComponent<VehicleBase>() && arg.driveObj.GetComponent<VehicleBase>().driveCam) {
                gameObject.SetActive(false);
            }
            else {
                isDriven = true;
            }
        });

        EventManager.Instance.AddListener(EventName.PlayerDown, (s, e) => {  //开车事件触发
            var arg = e as PlayerDrivingEventArgs;                                              //isDriven = false;
            if (arg.driveObj && arg.driveObj.GetComponent<VehicleBase>() && arg.driveObj.GetComponent<VehicleBase>().driveCam) {
                gameObject.SetActive(true);
            }
            else {
                isDriven = false;
            }
        });
    }

    private void Start()
    {

        mask.value = (1 << LayerMask.NameToLayer("Ground")) | (1 << LayerMask.NameToLayer("Wall"));
        this.target = this.threeAngleTarget;
        EventManager.Instance.AddListener(EventName.ChangeAngle, changeAngle);

        var offset = SphericalToCartesian(radius, DegreeToRadian(polarDeg), DegreeToRadian(elevationDeg));
        //transform.position = Vector3.Lerp(transform.position, target.position + offset, Time.deltaTime * 10);
        transform.position = target.position + offset;
    }

    private void changeAngle(object sender, EventArgs e) {
        var data = e as AngleChangeEventArgs;
        if (data != null) {
            if (data.angleIndex == 1) {
                this.target = this.firstAngleTarget;
                this.radius = 0;
                isFirstAngle = true;
                transform.position = target.position;
                transform.forward = target.forward;
            } else if (data.angleIndex == 3) {
                this.target = this.threeAngleTarget;
                this.radius = 6;
                isFirstAngle = false;
            }
        }
        Debug.Log("摄像机视角改变" + e);
    }
    public Vector2 PolarToCartesian(float radius, float angle) {
        float x = radius * Mathf.Cos(angle);
        float y = radius * Mathf.Sin(angle);
        return new Vector2(x, y);
    }

    public static float DegreeToRadian(float degree) {
        return degree * Mathf.Deg2Rad;
    }

    public static Vector3 SphericalToCartesian(float radius, float polar, float elevation) {
        float a = radius * Mathf.Cos(elevation);
        float x = a * Mathf.Cos(polar);
        float y = radius * Mathf.Sin(elevation);
        float z = a * Mathf.Sin(polar);
        return new Vector3(x, y, z);
    }

    public static void CartesianToSpherical(Vector3 cartesian, out float radius, out float polar, out float elevation) {
        radius = Mathf.Sqrt(Mathf.Pow(cartesian.x, 2) + Mathf.Pow(cartesian.y, 2) + Mathf.Pow(cartesian.z, 2));
        polar = Mathf.Atan2(cartesian.z, cartesian.x);
        elevation = Mathf.Asin(cartesian.y / radius);
    }



    void LateUpdate() {

        if (isDriven) {
            var offset = SphericalToCartesian(6f, DegreeToRadian(270), DegreeToRadian(-15));
            transform.position = Vector3.Lerp(transform.position, target.TransformPoint(target.localPosition + offset), Time.deltaTime * 3);
            //transform.position = target.TransformPoint(target.localPosition + offset);
            transform.LookAt(target);
            return;
        }

        if (isFirstAngle) {
            //var offset = SphericalToCartesian(radius, DegreeToRadian(polarDeg), DegreeToRadian(elevationDeg));
            target.eulerAngles = new Vector3(elevationDeg, -polarDeg, 0);
            transform.forward = target.forward;
            transform.position = target.position;
            if (PlayerController.Instance.animator) {
                var euler = PlayerController.Instance.animator.transform.eulerAngles;
                PlayerController.Instance.animator.transform.eulerAngles = new Vector3(euler.x, target.eulerAngles.y, euler.z);
            }

        }
       else {
            Ctrl_Cam_Move();
            CtrThird();
        }

       
       

        
    }

    void CtrThird() {

        var offset = SphericalToCartesian(radius, DegreeToRadian(polarDeg), DegreeToRadian(elevationDeg));
        //更新相机位置
        //transform.position = target.position + offset;
        transform.position = target.position + offset;
        //TODO:做成CAMERA NEAR OBJ,进行隐藏
        if (PlayerController.Instance.animator != null) PlayerController.Instance.animator.gameObject.SetActive(true);
        //计算完位置之后处理让镜头不会穿墙
        Vector3 direction = transform.position - target.position;
        float distance = direction.magnitude;
        direction.Normalize();
        RaycastHit hit;
        if (Physics.Raycast(target.transform.position, direction, out hit, distance, mask.value)) {
            var dstPos = hit.point - distance * distance * direction * 0.01f;
            var offsetDis = target.position - dstPos;
            CartesianToSpherical(offsetDis, out var compareRadius, out _, out _);
            if (compareRadius < 1f)  {
                if (PlayerController.Instance.animator != null)  PlayerController.Instance.animator.gameObject.SetActive(false);
            }
            transform.position = dstPos;

        }
        transform.eulerAngles = target.eulerAngles;

        if (isLookAt) {
            

            transform.LookAt(target);
            
        }
    }

    public void Ctrl_Cam_Move()
    {
        if (EditorModel.Instance.CurrentUnlock != null) { //解锁其他物体的时候,镜头不动, 要前后移动的是物体
            return;
        }

        if (Input.GetAxis("Mouse ScrollWheel") > 0)
        {
            //transform.Translate(Vector3.forward * 1f);//速度可调  自行调整
            radius = Math.Clamp(radius - 1.0f, 2, 15);
        }
        if (Input.GetAxis("Mouse ScrollWheel") < 0)
        {
            //transform.Translate(Vector3.forward * -1f);//速度可调  自行调整
           
            radius = Math.Clamp(radius + 1.0f, 2, 15);
        }
    }
}

3.相机跟随角色视角旋转:

cs 复制代码
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;

public class CameraRotate : MonoBehaviour
{

    public float speedH;
    //public float speedVertical;
    public bool isMobile;
    //[Range(0f, 1f)]
    //public float damping;
    CameraFollow cameraFollow;
    private float currentHorizontal;
    private float currentVertical;
    private Vector3 lastMousePosition;
    private bool isDragging;

    float time;
    private float velocityY;
    private float velocityX;


    PointerEventData eventDataCurrentPosition;
    //private bool isDragging;
    public static CameraRotate instance;
    private void Awake() {
        instance = this;
        //UI交互要禁用玩家的控制
        
        cameraFollow = Camera.main.GetComponent<CameraFollow>();
        currentHorizontal = cameraFollow.polarDeg;
        time = Time.realtimeSinceStartup;
    }

    // Start is called before the first frame update
    void Start() {
        
    }

    public void SetPolarDeg(float degree) {
        currentHorizontal = degree;
        cameraFollow.polarDeg = degree;
    }

    private bool IsPointerOverUIObject() {//判断是否点击的是UI,有效应对安卓没有反应的情况,true为UI


        
        if (eventDataCurrentPosition == null) {
            eventDataCurrentPosition = new PointerEventData(EventSystem.current);
        }

        eventDataCurrentPosition.position = new Vector2(Input.mousePosition.x, Input.mousePosition.y);
        List<RaycastResult> results = new List<RaycastResult>();
        EventSystem.current.RaycastAll(eventDataCurrentPosition, results);
        

        

        return results.Count > 0;
    }

    // Update is called once per frame
    void LateUpdate() {

        //if (IsPointerOverUIObject()) {
        //    lastMousePosition = Input.mousePosition;
        //    return;//点击到UI不处理
        //}

        // 检查鼠标左键是否按下如果按下的那一下是在UI之上,则不让其旋转
        if (PlayerData.Instance.isRunningPC)
        {
            if (Input.GetMouseButtonDown(0) && IsPointerOverUIObject() == false)
            {
                // 记录鼠标点击位置
                lastMousePosition = Input.mousePosition;
                isDragging = true;
            }
            else if (Input.GetMouseButtonUp(0))
            {
                // 如果鼠标抬起,则需要重新按下鼠标来记录新的点击位置
                isDragging = false;
            }

        }
        else {
            if (Input.GetMouseButtonDown(0))
            {
                // 记录鼠标点击位置
                lastMousePosition = Input.mousePosition;
                isDragging = true;
            }
            else if (Input.GetMouseButtonUp(0))
            {
                // 如果鼠标抬起,则需要重新按下鼠标来记录新的点击位置
                isDragging = false;
            }

        }
        

      

        if (isDragging) {

            float speedHorizontal = speedH;
            float speedVertical = 0.1f;

            // 计算鼠标移动的增量
            Vector3 deltaMousePosition = Input.mousePosition - lastMousePosition;

            //Debug.Log("deltaMousePosition.x:" + deltaMousePosition.x + "deltaMousePosition.y:" + deltaMousePosition.y);
            // 计算水平旋转角度
            // 计算水平旋转角度
            float deltaHorizontal = speedHorizontal * deltaMousePosition.x;
            var newHorizontal = currentHorizontal - deltaHorizontal;
            //newHorizontal = Mathf.SmoothDamp(currentHorizontal, newHorizontal,ref velocityX, Time.unscaledDeltaTime);
            // 更新摄像机跟随脚本的旋转角度
            cameraFollow.polarDeg = newHorizontal;

            //Debug.Log("cameraFollow.polarDeg" + cameraFollow.polarDeg);

            currentHorizontal = newHorizontal;
            float deltaVertical = speedVertical * deltaMousePosition.y;
            var newVertical = currentVertical - deltaVertical;
            //newHorizontal = Mathf.SmoothDamp(currentVertical, newVertical, ref velocityY, Time.unscaledDeltaTime);
            // 更新摄像机跟随脚本的旋转角度
            cameraFollow.elevationDeg = Mathf.Clamp(newVertical, -90f, 89);
            currentVertical = newVertical;

            lastMousePosition = Input.mousePosition;
        }
        else {

        }
    }









}
相关推荐
天人合一peng7 小时前
unity 生成标记根据背景色标记变色
unity·游戏引擎
天人合一peng11 小时前
unity 生成标记根据背景色变色为明显的颜色
unity·游戏引擎
魔士于安12 小时前
Unity 超市总动员 超市收银台 超市货架 超市购物手推车 超市常见商品
游戏·unity·游戏引擎·贴图·模型
CandyU212 小时前
Unity —— 数据持久化
unity·游戏引擎
zh路西法12 小时前
【Unity实现Oneshot胶卷显形】游戏窗口化与Win32API的使用
游戏·unity·游戏引擎
迪捷软件13 小时前
显控系统虚拟仿真的工程化路径
游戏引擎·cocos2d
凡情17 小时前
android隐私合规检测
android·unity
小贺儿开发17 小时前
Unity3D 本地 Stable Diffusion 文生图效果演示
人工智能·unity·stable diffusion·文生图·ai绘画·本地化
Swift社区17 小时前
传统游戏引擎 vs 鸿蒙 System 架构
架构·游戏引擎·harmonyos
mxwin1 天前
Unity Shader 半透明物体为什么不能写入深度缓冲?
unity·游戏引擎·shader