Unity简单的摄像机控制代码,第三人称,第一人称,场景漫游

  • 摄像机控制器代码


摄像机控制器代码

是我最常用的代码,挂到摄像机上面直接可以用

unity urp 相机模式设置 unity相机控制_unity3d

相机功能除了可以第三人称第一人称自由漫游之外,我还做了空间限制功能

包括最大距离限制(黑的),多个(无限)内部限制(不可穿越的区域)

可以用下图的箭头调整限制区域的大小.

unity urp 相机模式设置 unity相机控制_开发工具_02


可以做出这样的相机限制

unity urp 相机模式设置 unity相机控制_开发工具_03

unity urp 相机模式设置 unity相机控制_unity3d_04

using System.Collections.Generic;
using UnityEngine;

public enum CameraControlMode
{
    第一人称,
    第三人称,
    自由漫游
}
[RequireComponent (typeof (Camera))]
public class CameraController : MonoBehaviour
{
    const string INPUT_MOUSE_SCROLLWHEEL = "Mouse ScrollWheel";
    const string INPUT_MOUSE_X = "Mouse X";
    const string INPUT_MOUSE_Y = "Mouse Y";
    [Header ("按键开启")]
    public KeyCode Key = KeyCode.Mouse1;

    [Header ("相机模式")]
    public CameraControlMode controlMode;

    [Header ("目标")]
    public Transform target;

    [Header ("旋转速度")]
    [Header ("================『相机控制设置』================")]
    [Range (0.1f, 5f)]
    public float orbitSpeed = 1f;
    [Header ("缩放速度")]
    [Range (0.1f, 2f)]
    public float zoomSpeed = 0.8f;
    [Header ("俯视角限制")]
    [Range (0.01f, 89.9f)]
    public float downEulerLimit = 85f;
    [Header ("仰视角限制")]
    [Range (0.01f, 89.9f)]
    public float upEulerLimit = 85f;
    float upLimit = 0, downLimit = 0;

    [Header ("最小缩放距离")]
    [Header ("================『第三人称设置』================")]
    public float minDistance = 2f;
    [Header ("最大缩放距离")]
    public float maxDistance = 10f;
    [Header ("当前缩放距离")]
    public float distance = 1f;

    [Header ("自由漫游旋转平滑")]
    [Header ("================『自由漫游设置』================")]
    [Range (0, 1)]
    public float rotationlerp = 0.5f;
    [Header ("攀升速度")]
    public float climbSpeed = 1;
    [Header ("正常移动速度")]
    public float normalMoveSpeed = 2;
    [Header ("下降速度")]
    public float slowMoveFactor = 1f;
    [Header ("加速度")]
    public float fastMoveFactor = 5;

    [Header ("空间最大限制")]
    [Header ("================『相机移动限制』================")]
    public SpaceLimitation MaximumLimit;

    [Header ("激活全部控制柄")]
    public bool ActivateAllInsideControl = true;
    [Header ("空间内部限制")]
    public List<SpaceLimitation> spaceInsideLimitations=new List<SpaceLimitation>();
    
    [System.Serializable]
    public class SpaceLimitation
    {
        public Vector2 vectorX = new Vector2 ();
        public Vector2 vectorY = new Vector2 ();
        public Vector2 vectorZ = new Vector2 ();
        [Header ("使用控制柄")]
        public bool IsControl = true;
        [Header ("区域颜色")]
        public Color AeraColor;
        [Header ("区域边框颜色")]
        public Color AeraLineColor;
    }

    void Start()
    {
        if (target == null)
        {
            GameObject Target = new GameObject ();
            Target.name = "Target";
            this.target = Target.transform;
        }
        distance = Vector3.Distance(target.transform.position,transform.position);
        updateCameraRotaiont ();
    }

    /// <summary>
    /// 重置相机
    /// </summary>
    public void ResetCameraLocation()
    {
        transform.eulerAngles = Vector3.zero;
        target.transform.position = new Vector3 (0, 0, 0);
        distance = maxDistance;
        updateCameraRotaiont ();
    }

    float rotationX = 0.0f;
    float rotationY = 0.0f;

    /// <summary>
    /// 漫游旋转更新
    /// </summary>
    void updateCameraRotaiont()
    {
        rotationX = transform.localEulerAngles.y;
        rotationY = -transform.localEulerAngles.x;
        if (transform.localEulerAngles.x > 90)
        {
            rotationY = transform.localEulerAngles.x - 360;
            rotationY *= -1;
        }
    }

    void LateUpdate()
    {
        if (Input.GetMouseButtonDown (0))
        {
            GameObject rayPointObject;
            rayPointObject = getCameraDetectionObj (100);
            if (rayPointObject)
                target.position = rayPointObject.transform.position;
        }
        // orbits

        switch (controlMode)
        {
            case CameraControlMode.第一人称:
                cameraRotationControl ();
                transform.position = target.position;
                updateCameraRotaiont ();
                break;
            case CameraControlMode.第三人称:
                cameraRotationControl ();
                Vector3 pos = transform.localRotation * ( Vector3.forward * -distance ) + target.position;
                transform.position = Vector3.Lerp (transform.position, pos, 0.05f);//缩放插值,看起来更平滑;
                updateCameraRotaiont ();
                break;
            case CameraControlMode.自由漫游:
                cameraRoam ();
                break;
            default:
                break;
        }
        limit ();
        foreach (var item in spaceInsideLimitations)
        {
            insideLimit (item);
        }
    }
    /// <summary>
    /// 检测相机到鼠标选中的物体
    /// </summary>
    /// <param name="MaxDistance"></param>
    /// <returns></returns>
    GameObject getCameraDetectionObj( float MaxDistance )
    {
        Ray camerRay;                       //声明一个射线
        Vector3 mousePos = new Vector3 ();   //记录将鼠标(因为屏幕坐标没有z,所以下面是将z设为0)
        RaycastHit cameraHit;               //用于记录射线碰撞到的物体
                                            //这里将屏幕坐标的鼠标位置存入一个vector3里面
        mousePos.x = Input.mousePosition.x;
        mousePos.y = Input.mousePosition.y;
        mousePos.z = 0;

        //Ray ray=Camera.main.ScreenPointToRay(Vector3 Pos):返回一条射线由摄像机近裁面发射经过Pos的射线。
        camerRay = Camera.main.ScreenPointToRay (mousePos);
        //物理检测射线,out一个RaycastHit类型的 hitInfo 信息,float distance是射线长度,int layerMask需要转换二进制,所以有如下操作
        if (Physics.Raycast (camerRay, out cameraHit, MaxDistance))
        {
            GameObject go = cameraHit.transform.gameObject; //这是检测到的物体
            if (go != null)
            {
                Debug.Log (go.name);
                return go;
            }
        }
        Debug.Log ("距离不够?没有点到物体?物体没有碰撞体?");
        return null;
    }
    /// <summary>
    /// 漫游
    /// </summary>
    void cameraRoam()
    {
        if (Input.anyKey)
        {
            if (Input.GetKey (Key))
            {
                rotationX += Input.GetAxis (INPUT_MOUSE_X) * orbitSpeed;
                rotationY += Input.GetAxis (INPUT_MOUSE_Y) * orbitSpeed;
                rotationY = Mathf.Clamp (rotationY, -90, 90);

                if (rotationY != 0 || rotationX != 0)
                {
                    Quaternion temp = Quaternion.AngleAxis (rotationX, Vector3.up);

                    temp *= Quaternion.AngleAxis (rotationY, Vector3.left);

                    transform.localRotation = Quaternion.Lerp (transform.localRotation, temp, rotationlerp);

                }
            }
            if (Input.GetKey (KeyCode.LeftShift) || Input.GetKey (KeyCode.RightShift))
            {
                transform.position += transform.forward * ( normalMoveSpeed * fastMoveFactor ) * Input.GetAxis ("Vertical") * Time.deltaTime;
                transform.position += transform.right * ( normalMoveSpeed * fastMoveFactor ) * Input.GetAxis ("Horizontal") * Time.deltaTime;

                if (Input.GetKey (KeyCode.Space)) { transform.position += Vector3.up * climbSpeed * fastMoveFactor * Time.deltaTime; }
                if (Input.GetKey (KeyCode.LeftControl)) { transform.position -= Vector3.up * climbSpeed * fastMoveFactor * Time.deltaTime; }
            }
            else if (Input.GetKey (KeyCode.LeftControl) || Input.GetKey (KeyCode.RightControl))
            {
                transform.position += transform.forward * ( normalMoveSpeed * slowMoveFactor ) * Input.GetAxis ("Vertical") * Time.deltaTime;
                transform.position += transform.right * ( normalMoveSpeed * slowMoveFactor ) * Input.GetAxis ("Horizontal") * Time.deltaTime;

                if (Input.GetKey (KeyCode.Space)) { transform.position += Vector3.up * climbSpeed * slowMoveFactor * Time.deltaTime; }
                if (Input.GetKey (KeyCode.LeftControl)) { transform.position -= Vector3.up * climbSpeed * slowMoveFactor * Time.deltaTime; }
            }
            else
            {
                transform.position += transform.forward * normalMoveSpeed * Input.GetAxis ("Vertical") * Time.deltaTime;
                transform.position += transform.right * normalMoveSpeed * Input.GetAxis ("Horizontal") * Time.deltaTime;

                if (Input.GetKey (KeyCode.Space)) { transform.position += Vector3.up * climbSpeed * Time.deltaTime; }
                if (Input.GetKey (KeyCode.LeftControl)) { transform.position -= Vector3.up * climbSpeed * Time.deltaTime; }
            }
            //Limit ();
        }
    }

    /// <summary>
    /// 控制旋转
    /// </summary>
    void cameraRotationControl()
    {
        if (Input.GetKey (Key))
        {
            upLimit = 90 - downEulerLimit;
            downLimit = 90 - upEulerLimit;
            float rot_x = Input.GetAxis (INPUT_MOUSE_X);
            float rot_y = -Input.GetAxis (INPUT_MOUSE_Y);

            Vector3 eulerRotation = transform.localRotation.eulerAngles;
            eulerRotation.x += rot_y * orbitSpeed;
            eulerRotation.y += rot_x * orbitSpeed;
            eulerRotation.z = 0f;
            #region Limit
            if (eulerRotation.x > 90 - upLimit && eulerRotation.x < 200)
            {
                eulerRotation.x = 90 - upLimit;
            }
            //270--360
            if (eulerRotation.x > 200 && eulerRotation.x < 270 + downLimit)
            {
                eulerRotation.x = 270 + downLimit;
            }
            #endregion
            transform.localRotation = Quaternion.Euler (eulerRotation);
            switch (controlMode)
            {
                case CameraControlMode.第一人称:
                    transform.position = target.position;
                    break;
                case CameraControlMode.第三人称:
                    transform.position = transform.localRotation * ( Vector3.forward * -distance ) + target.position;
                    break;
                default:
                    break;
            }
        }
        if (Input.GetAxis (INPUT_MOUSE_SCROLLWHEEL) != 0f)
        {
            float delta = Input.GetAxis (INPUT_MOUSE_SCROLLWHEEL);

            distance -= delta * ( distance / maxDistance ) * ( zoomSpeed * 1000 ) * Time.deltaTime;
            distance = Mathf.Clamp (distance, minDistance, maxDistance);
        }
    }

    /// <summary>
    /// 相机限制
    /// </summary>
    void limit()
    {
        if (transform.position.x < MaximumLimit.vectorX.x || transform.position.x > MaximumLimit.vectorX.y)
        {
            if (transform.position.x < MaximumLimit.vectorX.x)
            {
                transform.position = new Vector3 (MaximumLimit.vectorX.x, transform.position.y, transform.position.z);
            }
            if (transform.position.x > MaximumLimit.vectorX.y)
            {
                transform.position = new Vector3 (MaximumLimit.vectorX.y, transform.position.y, transform.position.z);
            }
        }
        if (transform.position.y < MaximumLimit.vectorY.x || transform.position.y > MaximumLimit.vectorY.y)
        {
            if (transform.position.y < MaximumLimit.vectorY.x)
            {
                transform.position = new Vector3 (transform.position.x, MaximumLimit.vectorY.x, transform.position.z);
            }
            if (transform.position.y > MaximumLimit.vectorY.y)
            {
                transform.position = new Vector3 (transform.position.x, MaximumLimit.vectorY.y, transform.position.z);
            }
        }
        if (transform.position.z < MaximumLimit.vectorZ.x || transform.position.z > MaximumLimit.vectorZ.y)
        {
            if (transform.position.z < MaximumLimit.vectorZ.x)
            {
                transform.position = new Vector3 (transform.position.x, transform.position.y, MaximumLimit.vectorZ.x);
            }
            if (transform.position.z > MaximumLimit.vectorZ.y)
            {
                transform.position = new Vector3 (transform.position.x, transform.position.y, MaximumLimit.vectorZ.y);
            }
        }
    }

    /// <summary>
    /// 内部限制
    /// </summary>
    /// <param name="spaceLimitation"></param>
    void insideLimit( SpaceLimitation spaceLimitation)
    {
        Vector3 vector3= transform.position;
        if (transform.position.x < spaceLimitation.vectorX.y &&
            transform.position.y < spaceLimitation.vectorY.y && 
            transform.position.z < spaceLimitation.vectorZ.y &&
            transform.position.x > spaceLimitation.vectorX.x &&
            transform.position.y > spaceLimitation.vectorY.x && 
            transform.position.z > spaceLimitation.vectorZ.x)
        {
            float vectorXxAbs = Mathf.Abs (transform.position.x - spaceLimitation.vectorX.x);
            float vectorXyAbs = Mathf.Abs (transform.position.x - spaceLimitation.vectorX.y);
            float vectorYxAbs = Mathf.Abs (transform.position.y - spaceLimitation.vectorY.x);
            float vectorYyAbs = Mathf.Abs (transform.position.y - spaceLimitation.vectorY.y);
            float vectorZxAbs = Mathf.Abs (transform.position.z - spaceLimitation.vectorZ.x);
            float vectorZyAbs = Mathf.Abs (transform.position.z - spaceLimitation.vectorZ.y);
            float[] nums = {vectorXxAbs ,vectorXyAbs ,vectorYxAbs ,vectorYyAbs ,vectorZxAbs ,vectorZyAbs};
            for (int i = 0; i < nums.Length - 1; i++)
                for (int j = 0; j < nums.Length - 1 - i; j++)
                    if (nums[j] > nums[j + 1])
                    {
                        float temp = nums[j];
                        nums[j] = nums[j + 1];
                        nums[j + 1] = temp;
                    }
            if (vectorXxAbs == nums[0])
                transform.position = new Vector3 (spaceLimitation.vectorX.x, transform.position.y, transform.position.z);
            else if (vectorXyAbs == nums[0])
                transform.position = new Vector3 (spaceLimitation.vectorX.y, transform.position.y, transform.position.z);
            else if (vectorYyAbs == nums[0])
                transform.position = new Vector3 (transform.position.x, spaceLimitation.vectorY.y, transform.position.z);
            else if (vectorYxAbs == nums[0])
                transform.position = new Vector3 (transform.position.x, spaceLimitation.vectorY.x, transform.position.z);
            else if (vectorZyAbs == nums[0])
                transform.position = new Vector3 (transform.position.x, transform.position.y, spaceLimitation.vectorZ.y);
            else if (vectorZxAbs == nums[0])
                transform.position = new Vector3 (transform.position.x, transform.position.y, spaceLimitation.vectorZ.x);
            distance = Vector3.Distance (transform.position, target.position);
        }
    }
    
    Vector3 gizmosPosition, gizmosWidthHeight;
    
    void OnDrawGizmosSelected()
    {
        limitArea (out MaximumLimit.vectorX, MaximumLimit.vectorX, out gizmosPosition.x, out gizmosWidthHeight.x);
        limitArea (out MaximumLimit.vectorY, MaximumLimit.vectorY, out gizmosPosition.y, out gizmosWidthHeight.y);
        limitArea (out MaximumLimit.vectorZ, MaximumLimit.vectorZ, out gizmosPosition.z, out gizmosWidthHeight.z);
        Gizmos.color = MaximumLimit.AeraLineColor;
        Gizmos.DrawWireCube (gizmosPosition, gizmosWidthHeight);
        Gizmos.color = MaximumLimit.AeraColor;
        Gizmos.DrawCube (gizmosPosition, gizmosWidthHeight);
        foreach (var spaceInsideLimitation in spaceInsideLimitations)
        {
            limitArea (out spaceInsideLimitation.vectorX, spaceInsideLimitation.vectorX, out gizmosPosition.x, out gizmosWidthHeight.x);
            limitArea (out spaceInsideLimitation.vectorY, spaceInsideLimitation.vectorY, out gizmosPosition.y, out gizmosWidthHeight.y);
            limitArea (out spaceInsideLimitation.vectorZ, spaceInsideLimitation.vectorZ, out gizmosPosition.z, out gizmosWidthHeight.z);
            Gizmos.color = spaceInsideLimitation.AeraLineColor;
            Gizmos.DrawWireCube (gizmosPosition, gizmosWidthHeight);
            Gizmos.color = spaceInsideLimitation.AeraColor;
            Gizmos.DrawCube (gizmosPosition, gizmosWidthHeight);
        }
    }
   /// <summary>
   /// 得到绘画位置与比例
   /// </summary>
   /// <param name="vector"></param>
   /// <param name="vectorXYZ"></param>
   /// <param name="mGizmosPosition"></param>
   /// <param name="mGizmosWidthHeight"></param>
    void limitArea( out Vector2 vector, Vector2 vectorXYZ, out float mGizmosPosition, out float mGizmosWidthHeight )
    {
        vector = vectorXYZ;
        if (vector.x > 0 || vector.y < 0)
        {
            if (vector.y < vector.x && vector.y < 0)
                vector.y = vector.x;
            if (vector.x > vector.y && vector.x > 0)
                vector.x = vector.y;

            mGizmosPosition = ( ( vector.x ) / 2 + ( vector.y ) / 2 );
            mGizmosWidthHeight = Mathf.Abs (vector.y) - Mathf.Abs (vector.x);
        }
        else
        {
            mGizmosPosition = ( vector.x + vector.y ) / 2;
            mGizmosWidthHeight = Mathf.Abs (vector.x) + Mathf.Abs (vector.y);
        }
    }
}

编辑器扩展代码(需要放在Editor文件夹)

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using static CameraController;

[CustomEditor (typeof (CameraController))]
public class CameraControllerEditor : Editor
{
    CameraController cameraController;
    bool mActivateAllInsideControl = true;
    void OnSceneGUI()
    {
        Vector3 vector;
        cameraController = (CameraController)target;

        if (cameraController.ActivateAllInsideControl!= mActivateAllInsideControl)
        {
            mActivateAllInsideControl = cameraController.ActivateAllInsideControl;
            if (mActivateAllInsideControl)
            {
                foreach (var item in cameraController.spaceInsideLimitations)
                {
                    item.IsControl = true;
                }
            }
            else
            {
                foreach (var item in cameraController.spaceInsideLimitations)
                {
                    item.IsControl = false;
                }
            }
        }
        if (cameraController.MaximumLimit.IsControl)
        {
            Handles.color = cameraController.MaximumLimit.AeraLineColor;
            vector = GetGizmosWidthHeightLocation (cameraController.MaximumLimit.vectorX, cameraController.MaximumLimit.vectorY, cameraController.MaximumLimit.vectorZ, "Xx");
            //滑动方向被限制为  位置1 arraw.vectorPoint, 和位置 2  arraw.transform.position两个点所在的线上,
            cameraController.MaximumLimit.vectorX.x = Handles.Slider (vector, Vector3.left).x;
            vector = GetGizmosWidthHeightLocation (cameraController.MaximumLimit.vectorX, cameraController.MaximumLimit.vectorY, cameraController.MaximumLimit.vectorZ, "Xy");
            cameraController.MaximumLimit.vectorX.y = Handles.Slider (vector, Vector3.right).x;
            vector = GetGizmosWidthHeightLocation (cameraController.MaximumLimit.vectorX, cameraController.MaximumLimit.vectorY, cameraController.MaximumLimit.vectorZ, "Yx");
            cameraController.MaximumLimit.vectorY.x = Handles.Slider (vector, Vector3.down).y;
            vector = GetGizmosWidthHeightLocation (cameraController.MaximumLimit.vectorX, cameraController.MaximumLimit.vectorY, cameraController.MaximumLimit.vectorZ, "Yy");
            cameraController.MaximumLimit.vectorY.y = Handles.Slider (vector, Vector3.up).y;
            vector = GetGizmosWidthHeightLocation (cameraController.MaximumLimit.vectorX, cameraController.MaximumLimit.vectorY, cameraController.MaximumLimit.vectorZ, "Zx");
            cameraController.MaximumLimit.vectorZ.x = Handles.Slider (vector, Vector3.back).z;
            vector = GetGizmosWidthHeightLocation (cameraController.MaximumLimit.vectorX, cameraController.MaximumLimit.vectorY, cameraController.MaximumLimit.vectorZ, "Zy");
            cameraController.MaximumLimit.vectorZ.y = Handles.Slider (vector, Vector3.forward).z;
        }
        foreach (var spaceInsideLimitation in cameraController.spaceInsideLimitations)
        {
            if (!spaceInsideLimitation.IsControl)
                continue;
            
            Handles.color = spaceInsideLimitation.AeraLineColor;
            vector = GetGizmosWidthHeightLocation (spaceInsideLimitation.vectorX, spaceInsideLimitation.vectorY, spaceInsideLimitation.vectorZ, "Xx");
            //滑动方向被限制为  位置1 arraw.vectorPoint, 和位置 2  arraw.transform.position两个点所在的线上,
            spaceInsideLimitation.vectorX.x = Handles.Slider (vector, Vector3.left).x;
            vector = GetGizmosWidthHeightLocation (spaceInsideLimitation.vectorX, spaceInsideLimitation.vectorY, spaceInsideLimitation.vectorZ, "Xy");
            spaceInsideLimitation.vectorX.y = Handles.Slider (vector, Vector3.right).x;
            vector = GetGizmosWidthHeightLocation (spaceInsideLimitation.vectorX, spaceInsideLimitation.vectorY, spaceInsideLimitation.vectorZ, "Yx");
            spaceInsideLimitation.vectorY.x = Handles.Slider (vector, Vector3.down).y;
            vector = GetGizmosWidthHeightLocation (spaceInsideLimitation.vectorX, spaceInsideLimitation.vectorY, spaceInsideLimitation.vectorZ, "Yy");
            spaceInsideLimitation.vectorY.y = Handles.Slider (vector, Vector3.up).y;
            vector = GetGizmosWidthHeightLocation (spaceInsideLimitation.vectorX, spaceInsideLimitation.vectorY, spaceInsideLimitation.vectorZ, "Zx");
            spaceInsideLimitation.vectorZ.x = Handles.Slider (vector, Vector3.back).z;
            vector = GetGizmosWidthHeightLocation (spaceInsideLimitation.vectorX, spaceInsideLimitation.vectorY, spaceInsideLimitation.vectorZ, "Zy");
            spaceInsideLimitation.vectorZ.y = Handles.Slider (vector, Vector3.forward).z;
        }
        if (GUI.changed)
            EditorUtility.SetDirty (target);
    }

    Vector3 GetGizmosWidthHeightLocation( Vector3 vectorX, Vector3 vectorY, Vector3 vectorZ, string value )
    {
        Vector3 vector = new Vector3 (
            ( vectorX.x + vectorX.y ) / 2,
            ( vectorY.x + vectorY.y ) / 2,
            ( vectorZ.x + vectorZ.y ) / 2);
        switch (value)
        {
            case "Xx":
                vector.x += ( ( vectorX.x ) - ( vectorX.y ) ) / 2;
                return vector;
            case "Xy":
                vector.x -= ( ( vectorX.x ) - ( vectorX.y ) ) / 2;
                return vector;
            case "Yx":
                vector.y += ( ( vectorY.x ) - ( vectorY.y ) ) / 2;
                return vector;
            case "Yy":
                vector.y -= ( ( vectorY.x ) - ( vectorY.y ) ) / 2;
                return vector;
            case "Zx":
                vector.z += ( ( vectorZ.x ) - ( vectorZ.y ) ) / 2;
                return vector;
            case "Zy":
                vector.z -= ( ( vectorZ.x ) - ( vectorZ.y ) ) / 2;
                return vector;
            default:
                return Vector3.zero;
        }
    }
}