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 {

        }
    }









}
相关推荐
Magnum Lehar6 小时前
vscode游戏引擎的build-clean配置
ide·vscode·游戏引擎
Magnum Lehar6 小时前
vscode游戏引擎的build-all全局配置
ide·vscode·游戏引擎
龚子亦7 小时前
【Shader学习】完整光照效果
unity·技术美术·shader
weixin_5316389411 小时前
Rokid AR交互开发工具对比
unity·游戏引擎·xr
Magnum Lehar21 小时前
wpf3d游戏引擎ControlTemplate.xaml.cs文件实现
游戏引擎·wpf
留待舞人归21 小时前
【Unity3D优化】优化多语言字体包大小
游戏·unity·游戏引擎·unity3d·优化
wsdchong之小马过河1 天前
2025虚幻引擎一般用什么模型格式
游戏引擎·虚幻
Magnum Lehar1 天前
wpf游戏引擎前端的Transform.cs实现
前端·游戏引擎·wpf
Magnum Lehar1 天前
wpf3d游戏引擎前端ControlTemplate实现
前端·游戏引擎·wpf
9765033351 天前
iOS 审核 cocos 4.3a【苹果机审的“分层阈值”设计】
flutter·游戏·unity·ios