第三人称摄像机代码:
Help
using UnityEngine;
public class Help_Qing {
//剪切平面点
public struct ClipPlanePoints
{
public Vector3 UpperLeft;
public Vector3 UpperRight;
public Vector3 LowerLeft;
public Vector3 LowerRight;
}
//夹角
public static float ClampAngle(float angle,float min,float max)
{
do
{
if (angle < -360)
angle += 360;
if (angle > 360)
angle -= 360;
} while (angle < -360 || angle > 360);
return Mathf.Clamp(angle, min, max);
}
//近夹平面
public static ClipPlanePoints ClipPlaneAtNear(Vector3 pos)
{
var clipPlanePoints = new ClipPlanePoints();
if (Camera.main == null)
return clipPlanePoints;
//位置
var transform = Camera.main.transform;
//半视野
var halfFOV = (Camera.main.fieldOfView / 2) * Mathf.Deg2Rad;
//Mathf.Deg2Rad 角度转弧度
var aspect = Camera.main.aspect;
//aspect 设置摄像机视口比例
var distance = Camera.main.nearClipPlane;
//.nearClipPlane 近裁前面的距离
var height = distance * Mathf.Tan(halfFOV);
//Mathf 正切值
var width = height * aspect;
clipPlanePoints.LowerRight = pos + transform.right * width;
//剪切平面的右下角 = pos + x轴 * 宽度
clipPlanePoints.LowerRight -= transform.up * height;
//剪切平面的右下角 -= y轴 * 高
clipPlanePoints.LowerRight += transform.forward * distance;
//剪切平面的右下角 += z轴 * 距离
clipPlanePoints.LowerLeft = pos - transform.right * width;
clipPlanePoints.LowerLeft -= transform.up * height;
clipPlanePoints.LowerLeft += transform.forward * distance;
clipPlanePoints.UpperRight = pos + transform.right * width;
clipPlanePoints.UpperRight += transform.up * height;
clipPlanePoints.UpperRight += transform.forward * distance;
clipPlanePoints.UpperLeft = pos - transform.right * width;
clipPlanePoints.UpperLeft += transform.up * height;
clipPlanePoints.UpperLeft += transform.forward * distance;
return clipPlanePoints;
}
}
ScriptableObjectUtility(脚本表对象实用程序)
using UnityEngine;
using UnityEditor;
public class ScriptableObjectUtility_Qing
{
public static void CreateAsset<T>() where T :ScriptableObject
{
var asset = ScriptableObject.CreateInstance<T>();
//创建.asset文件
ProjectWindowUtil.CreateAsset(asset, "New" + typeof(T).Name + ".asset");
//public static ScriptableObject CreateInstance(string className)
//创建脚本化对象类名为className的一个实例。
//public static ScriptableObject CreateInstance(Type type)
//创建脚本化对象类型为type的一个实例。
}
}
ThirdCameraState(第三人称状态)
using UnityEngine;
[System.Serializable]
public class ThirdCameraState{
public string Name;
public float forward;
public float right;
public float maxDistance;
public float minDistance;
public float Height;
public ThirdCameraState(string name)
{
this.Name = name;
this.forward = -1f;
this.right = 0.35f;
this.maxDistance = 1.5f;
this.minDistance = 0.5f;
this.Height = 1.5f;
}
}
ThirdameraListData (第三人称相机列表数据)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
//序列化:再次读取Unity序列化的变量是有值,不需要你再次去赋值,因为它已经保存下来
public class ThirdameraListData : ScriptableObject {
[SerializeField] public string Name;
[SerializeField] public List<ThirdCameraState> thirdCameraStates;
//SerializeField:在Inspector面版中显示非public属性,并且序列化
//ThirdameraListDat()方法
//新建一ThirdCameraState("name")表
public ThirdameraListData()
{
thirdCameraStates = new List<ThirdCameraState>();
thirdCameraStates.Add(new ThirdCameraState("Default"));
}
}
ThirdCamera (第三人称摄像机)
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityStandardAssets.CrossPlatformInput;
public class ThirdCamera : MonoBehaviour {
private static ThirdCamera _instance;
public static ThirdCamera instance
{
get
{
if(_instance == null)
{
_instance =GameObject.FindObjectOfType<ThirdCamera>();
}
return _instance;
}
}
#region 检查面板特性
//玩家的位置
public Transform Player;
//锁定目标
public Transform lockTarget;
//X轴的鼠标的灵敏度
public float X_MouseSensitivity = 3f;
//Y轴的鼠标的灵敏度
public float Y_MouseSensitivity = 3f;
//两数之间平滑状态
public float SmoothBetweenState = 0.05f;
//摄像机旋转平滑
public float SmoothCameraRotation = 12f;
//平滑跟随
public float SmoothFollow = 10f;
//Y轴的最小限制
public float Y_MinLimit = -40f;
//Y轴的最大限制
public float Y_MaxLimit = 80f;
//X轴的最小限制
public float X_MinLimit = -360f;
//X轴的最大限制
public float X_MaxLimit = 360f;
//剔除高度
public float cullingHeight = 1f;
//剔除层
public LayerMask cullingLayer = 1 << 0;
//是否锁定摄像机
public bool lockCamera;
//当前状态名
public string currentStateName;
#endregion
#region 隐藏属性
//目标
[HideInInspector] public Transform target;
//目标位置
[HideInInspector] public Vector3 targetPos;
//摄像机状态表
[HideInInspector] public ThirdameraListData CameraStateList;
//指数/标志
[HideInInspector] public int index;
//目标看向
[HideInInspector] public Transform TargetLookAt;
//距离
[HideInInspector] public float Distance = 5f;
//鼠标Y轴
[HideInInspector] public float mouseY = 0f;
//鼠标X轴
[HideInInspector] public float mouseX = 0f;
//当前摄像机状态
[HideInInspector] private ThirdCameraState currentState;
//平滑摄像机状态
[HideInInspector] public ThirdCameraState lerpState;
//看向的点
[HideInInspector] public Vector3 LookPoint;
//目标高度
private float targetHeight;
#endregion
void Start()
{
Init();
}
void Init()
{
//隐藏鼠标
Cursor.visible = false;
if (Player == null)
{
Debug.Log("请分配Player");
return;
}
target = Player;
targetPos = target.position;
TargetLookAt = new GameObject("targetLookAt").transform;
TargetLookAt.position = target.position;
TargetLookAt.hideFlags = HideFlags.HideInHierarchy;
//HideFlags.HideInHierarchy :隐藏在Hierarchy
TargetLookAt.rotation = target.rotation;
ChangeState("Normal", false);
mouseY = target.eulerAngles.x;
mouseX = target.eulerAngles.y;
//返回或设置旋转的euler角度表示。
//围绕z轴旋转euler.z度的旋转,围绕x轴旋转euler.x度,围绕y轴旋转euler.y度(
}
private void FixedUpdate()
{
if (TargetLookAt == null)
return;
CameraMovement();
}
//改变状态(名,是否平滑)
public void ChangeState(string stateName,bool hasSmooth)
{
var state = CameraStateList.thirdCameraStates
.Find(delegate (ThirdCameraState obj)
{
return obj.Name.Equals(stateName);
//Equals.判断两值是不相等
});
if(state !=null)
{
currentStateName = stateName;
lerpState = state;
if (currentState != null && !hasSmooth)
currentState.CopyState(state);
target = Player;
}
else
{
currentStateName = stateName;
state = CameraStateList.thirdCameraStates[0];
lerpState = state;
if (currentState != null && !hasSmooth)
currentState.CopyState(state);
target = Player;
}
if (currentState == null)
currentState = new ThirdCameraState("");
index = CameraStateList.thirdCameraStates.IndexOf(state);
//IndexOf :查找字串中指定字符或字串首次出现的位置,返首索引值
}
//改变状态(状态名,位置:范围目标,物理节点:获取范围图片)
public void ChangeState(string stateName,Transform scopeTarget,SpringJoint scopeImage)
{
var state = CameraStateList.thirdCameraStates
.Find(delegate (ThirdCameraState obj)
{
return obj.Name.Equals(stateName);
});
if(scopeTarget !=null)
{
transform.position = scopeTarget.position;
}
if(state !=null)
{
currentStateName = stateName;
lerpState = state;
if (currentState != null)
currentState.CopyState(state);
if (scopeTarget != null)
target = scopeTarget;
else
target = Player;
}
else
{
currentStateName = stateName;
state = CameraStateList.thirdCameraStates[0];
lerpState = state;
if (currentState != null)
currentState.CopyState(state);
if (scopeTarget != null)
target = Player;
}
if (currentState == null)
currentState = new ThirdCameraState("");
index = CameraStateList.thirdCameraStates.IndexOf(state);
}
//摄像机移动
void CameraMovement()
{
currentState.Slerp(lerpState, SmoothBetweenState);
RaycastHit hitInfo;
Distance = currentState.maxDistance;
targetHeight = currentState.Height;
var camDir = (currentState.forward * TargetLookAt.forward)
+
(currentState.right * TargetLookAt.right);
camDir = camDir.normalized;
if (target == null)
return;
targetPos = Vector3.Slerp(targetPos, target.position, SmoothFollow * Time.deltaTime);
var cPos = targetPos + new Vector3(0, targetHeight, 0);
if(Physics.Raycast(cPos,camDir,out hitInfo,Distance,cullingLayer))
{
var t = hitInfo.distance - 0.1f;
t -= currentState.minDistance;
t /= (Distance - currentState.minDistance);
targetHeight = Mathf.Lerp(cullingHeight, targetHeight, Mathf.Clamp(t, 0.0f, 1.0f));
cPos = target.position + new Vector3(0, targetHeight, 0);
//显示摄像机目标位置
//Debug.DrawLine(cPos,transform.position,Color.red)
}
if (Physics.Raycast(cPos, camDir, out hitInfo, Distance + 0.2f,cullingLayer))
{
Distance = hitInfo.distance - 0.1f;
}
var lookPoint = cPos;
lookPoint += (TargetLookAt.right * Vector3.Dot(camDir * Distance, TargetLookAt.right));
//Vector3.Dot两个向量的点乘积。
//a*b =x1 * x2 + y1 * y2 +z1 * z2
//a*b=a*b*cos(Angle)
//对于normalized向量,
//如果他们指向在完全相同的方向,Dot返回1。
//如果他们指向完全相反的方向,返回 - 1。
//对于其他的情况返回一个数(例如:如果是垂直的Dot返回0)。
//对于任意长度的向量,Dot返回值是相同的:当向量之间的角度减小,它们得到更大的值。
transform.position = cPos + (camDir * Distance);
if (lockTarget != null)
{
Vector3 relatvePos = lockTarget.position - transform.position;
Quaternion rotation = Quaternion.LookRotation(relatvePos);
transform.rotation = Quaternion.Slerp(transform.rotation, rotation, 3.5f * Time.deltaTime);
//transform.LookAt(lockTarget.position);
}
else
transform.LookAt(lookPoint);
TargetLookAt.position = cPos;
Quaternion newRot = Quaternion.Euler(mouseY, mouseX, 0);
TargetLookAt.rotation = Quaternion.Slerp(TargetLookAt.rotation, newRot, SmoothCameraRotation * Time.deltaTime);
}
}
public static class ExtensionMethods_Qing
{
//两摄像机状态平滑
public static void Slerp(this ThirdCameraState to,ThirdCameraState from,float time)
{
to.forward = Mathf.Lerp(to.forward, from.forward, time);
to.right = Mathf.Lerp(to.right, from.right, time);
to.maxDistance = Mathf.Lerp(to.maxDistance, from.maxDistance, time);
to.Height = Mathf.Lerp(to.Height, from.Height, time);
}
//复制一个摄像状态
public static void CopyState(this ThirdCameraState to, ThirdCameraState from)
{
to.forward = from.forward;
to.right = from.right;
to.maxDistance = from.maxDistance;
to.Height = from.Height;
}
}
ThirdCameraEditor (第三人称摄像机设置)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
[CustomEditor (typeof(ThirdCamera))]
public class ThirdCameraEditor : Editor {
ThirdCamera thirdCamera;
//在Inspector面板创建一个GUI
public override void OnInspectorGUI()
{
thirdCamera = (ThirdCamera)target;
EditorGUILayout.Space();
//在前一个控制和下一个控件之间留一个空格
//if(thirdCamera.CameraStateList == null)
//{
// GUILayout.EndVertical();
// return;
//}
//开始一个垂直控件的组
GUILayout.BeginVertical("摄像机状态", "window");
//创建一个包含用户消息的帮助框。
EditorGUILayout.HelpBox("此设置将始终加载到此列表中,您可以为其他人物创建更多的列表,并具有不同的设置",MessageType.Info);
thirdCamera.CameraStateList = (ThirdameraListData)EditorGUILayout.ObjectField("摄像机状态 列表", thirdCamera.CameraStateList, typeof(ThirdameraListData), false);
//开始一个水平控件的组
GUILayout.BeginHorizontal();
//创建按钮新建摄像机状态
if(GUILayout.Button(new GUIContent("创建摄像机状态")))
{
if (thirdCamera.CameraStateList.thirdCameraStates == null)
thirdCamera.CameraStateList.thirdCameraStates = new List<ThirdCameraState>();
thirdCamera.CameraStateList.thirdCameraStates.Add(new ThirdCameraState("New State" + thirdCamera.CameraStateList.thirdCameraStates.Count));
thirdCamera.index = thirdCamera.CameraStateList.thirdCameraStates.Count - 1;
}
//创建按钮删除摄像机状态
if (GUILayout.Button(new GUIContent("删除摄像机状态")) && thirdCamera.CameraStateList.thirdCameraStates.Count > 1 && thirdCamera.index != 0)
{
if (thirdCamera.index - 1 >= 0)
thirdCamera.index--;
}
GUILayout.EndHorizontal();
//结束一个水平控件的组
if(thirdCamera.CameraStateList.thirdCameraStates.Count > 0)
{
thirdCamera.index = EditorGUILayout.Popup("State", thirdCamera.index, getListName(thirdCamera.CameraStateList.thirdCameraStates));
StateData(thirdCamera.CameraStateList.thirdCameraStates[thirdCamera.index]);
}
GUILayout.EndVertical();
//结束垂直控件的组
GUILayout.BeginVertical("box");
base.OnInspectorGUI();
GUILayout.EndVertical();
EditorGUILayout.Space();
if(GUI.changed)
//如果任何控制按钮改变输入数据的值那么返回true。
{
EditorUtility.SetDirty(thirdCamera);
EditorUtility.SetDirty(thirdCamera.CameraStateList);
//将目标对象标记为脏,(仅适用于非场景对象)
}
}
void StateData(ThirdCameraState camState)
{
camState.Name = EditorGUILayout.TextField("状态 名字", camState.Name);
//创建一个文本字段。
if(CheckName(camState.Name,thirdCamera.index))
{
EditorGUILayout.HelpBox("这个名字已经存在,请选择另一个名字", MessageType.Error);
}
camState.forward = (float)((int)EditorGUILayout.Slider("前方", camState.forward, -1f, 1f));
camState.right = EditorGUILayout.Slider("水平", camState.right, -3f, 3f);
camState.maxDistance = EditorGUILayout.FloatField("最大距离", camState.maxDistance);
//文本框输入浮点值。
camState.minDistance = EditorGUILayout.FloatField("最小距离", camState.minDistance);
camState.Height = EditorGUILayout.FloatField("高度", camState.Height);
}
//检查名字
bool CheckName(string Name,int _index)
{
foreach (ThirdCameraState state in thirdCamera.CameraStateList.thirdCameraStates)
if (state.Name.Equals(Name) && thirdCamera.CameraStateList.thirdCameraStates.IndexOf(state) != _index)
return true;
return false;
//foreach循环用于列举出集合中所有的元素,
//foreach语句中的表达式由关键字in隔开的两个项组成。
//in右边的项是集合名,in左边的项是变量名,用来存放该集合中的每个元素。
}
[MenuItem("ThirdPerson Controller/Resources/New CameraState List Data")]
//MenuItem属性允许您将菜单项添加到主菜单和检查器上下文菜单。
static void NewCameraStateData()
{
ScriptableObjectUtility_Qing.CreateAsset<ThirdameraListData>();
}
private string[] getListName(List<ThirdCameraState> list)
{
string[] names = new string[list.Count];
for(int i = 0;i<list.Count;i++)
{
names[i] = list[i].Name;
}
return names;
}
}
效果: