Unity简单的摄像机控制代码,第三人称,第一人称,场景漫游
- 摄像机控制器代码
摄像机控制器代码
是我最常用的代码,挂到摄像机上面直接可以用
相机功能除了可以第三人称第一人称自由漫游之外,我还做了空间限制功能
包括最大距离限制(黑的),多个(无限)内部限制(不可穿越的区域)
可以用下图的箭头调整限制区域的大小.
可以做出这样的相机限制
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;
}
}
}