265 lines
9.4 KiB
C#
265 lines
9.4 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
using UnityEngine.UIElements;
|
|
|
|
public class CameraControllerTest : MonoBehaviour
|
|
{
|
|
|
|
public float PointVerticalSpeed; // 中心点纵向移动速度
|
|
public float PointHorizontalSpeed; // 中心点横向移动速度
|
|
public float CameraToPointSpeed; // 摄像机到中心点移动速度
|
|
public Scpoe CameraToPointScpoe; // 摄像机到中心点移动限制
|
|
public float PointVerticalRotationSpeed; // 绕中心点纵向旋转速度
|
|
public float PointHorizontalRotationSpeed; // 绕中心点横向旋转速度
|
|
public Scpoe PointVerticalRotationScpoe; // 绕中心点纵向旋转限制
|
|
public Scpoe PointHorizontalRotationScpoe; // 绕中心点横向旋转限制
|
|
public Transform PointScpoePlane; // 中心点所在平面
|
|
public List<Vector3> PointScpoeData; // 中心点限制范围
|
|
|
|
public bool IsPosition; // 是否开启移动
|
|
public bool IsRotation; // 是否开启旋转
|
|
public bool IsScale; // 是否开启摄像机到中心点移动
|
|
public float CameraToPointInit; // 摄像机到中心点插值
|
|
public float PointRotationInit; // 旋转插值
|
|
|
|
private Vector3 Point; // 操作中心点
|
|
private Vector3 Point_Temp; // 中心点临时位置
|
|
private Vector3 CameraToPoint; // 摄像机到中心点单位向量
|
|
private Vector2 MouseLValue; // 鼠标左键变量
|
|
private Vector2 MouseRValue; // 鼠标右键变量
|
|
private float MouseCValue; // 鼠标中键变量
|
|
|
|
void Start()
|
|
{
|
|
Init();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 初始化
|
|
/// </summary>
|
|
void Init()
|
|
{
|
|
Point = PointScpoePlane.transform.position;
|
|
CameraToPoint = transform.position - Point;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 中心点操作
|
|
/// </summary>
|
|
void PointOperation()
|
|
{
|
|
Point_Temp = Point - Vector3.ProjectOnPlane(transform.right, Vector3.up
|
|
).normalized * PointHorizontalSpeed * MouseLValue.x - Vector3.ProjectOnPlane(transform.forward, Vector3.up
|
|
).normalized * PointVerticalSpeed * MouseLValue.y;
|
|
Point = IsPointInPolygon(PointOnPlane(Point_Temp, PointScpoePlane.right, PointScpoePlane.forward, PointScpoePlane.position), PointScpoeData) ? Point_Temp : PointNotInScpoe(Point_Temp,PointScpoeData);
|
|
|
|
CameraToPointInit += MouseCValue * CameraToPointSpeed;
|
|
CameraToPointInit = Mathf.Clamp01(CameraToPointInit);
|
|
|
|
transform.position = Point + CameraToPoint.normalized * Mathf.Lerp(CameraToPointScpoe.Min, CameraToPointScpoe.Max, CameraToPointInit);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取指定点位在指定平面内的坐标
|
|
/// </summary>
|
|
/// <param name="pos">指定点位</param>
|
|
/// <param name="plane">指定平面中心点</param>
|
|
/// <returns>指定点位在指定平面内的坐标</returns>
|
|
public static Vector3 PointOnPlane(Vector3 pos, Vector3 right, Vector3 forward, Vector3 plane)
|
|
{
|
|
Vector3 normal = Vector3.Cross(right, forward).normalized;
|
|
Vector3 onPlane = Vector3.ProjectOnPlane(pos - plane, normal);
|
|
Vector3 projectX = Vector3.Project(onPlane, right);
|
|
float dirX = Vector3.Angle(projectX, right) - 90;
|
|
Vector3 projectZ = Vector3.Project(onPlane, forward);
|
|
float dirZ = Vector3.Angle(projectZ, forward) - 90;
|
|
|
|
return new Vector3(projectX.magnitude * -Mathf.Sign(dirX), plane.y, projectZ.magnitude * -Mathf.Sign(dirZ));
|
|
}
|
|
|
|
/// <summary>
|
|
/// 判断点是否在平面内
|
|
/// </summary>
|
|
/// <param name="point"></param>
|
|
/// <param name="polygon"></param>
|
|
/// <returns></returns>
|
|
public static bool IsPointInPolygon(Vector3 point, List<Vector3> polygon)
|
|
{
|
|
int polygonLength = polygon.Count, i = 0;
|
|
bool inside = false;
|
|
float pointX = point.x, pointZ = point.z;
|
|
float startX, startZ, endX, endZ;
|
|
Vector3 endPoint = polygon[polygonLength - 1];
|
|
endX = endPoint.x;
|
|
endZ = endPoint.z;
|
|
while (i < polygonLength)
|
|
{
|
|
startX = endX; startZ = endZ;
|
|
endPoint = polygon[i++];
|
|
endX = endPoint.x; endZ = endPoint.z;
|
|
inside ^= (endZ > pointZ ^ startZ > pointZ) && ((pointX - endX) < (pointZ - endZ) * (startX - endX) / (startZ - endZ));
|
|
}
|
|
return inside;
|
|
}
|
|
|
|
Vector3 PointNotInScpoe(Vector3 outsidePoint, List<Vector3> polygon)
|
|
{
|
|
// 先拿到点在边上的投射点
|
|
// 如果点在线段上, 则直接使用两点的距离作为点边最小距离
|
|
// 如果点超出线段范围, 则使用点到两个端点的最小值作为点边最小距离
|
|
// 遍历所有点边距离, 找到点边距离最小的, 则使用该线段上的点作为最近距离点
|
|
float[] distances = new float[polygon.Count];
|
|
Vector3[] closestPoints = new Vector3[polygon.Count];
|
|
for (int i = 0; i < polygon.Count; i++)
|
|
{
|
|
Vector3 a = polygon[i];
|
|
Vector3 b = polygon[(i + 1) % polygon.Count];
|
|
Vector3 projectPoint = a + Vector3.Project(outsidePoint - a, b - a);
|
|
distances[i] = Vector3.Distance(projectPoint, outsidePoint);
|
|
closestPoints[i] = projectPoint;
|
|
float angle = Vector3.Angle(projectPoint - a, b - a);
|
|
if (angle > 90)
|
|
{
|
|
distances[i] = Vector3.Distance(outsidePoint, a);
|
|
closestPoints[i] = a;
|
|
}
|
|
angle = Vector3.Angle(projectPoint - b, a - b);
|
|
if (angle > 90)
|
|
{
|
|
distances[i] = Vector3.Distance(outsidePoint, b);
|
|
closestPoints[i] = b;
|
|
}
|
|
}
|
|
|
|
float minDistance = float.MaxValue;
|
|
Vector3 closestPoint = Vector3.zero;
|
|
for (int i = 0; i < polygon.Count; i++)
|
|
{
|
|
if (distances[i] < minDistance)
|
|
{
|
|
minDistance = distances[i];
|
|
closestPoint = closestPoints[i];
|
|
}
|
|
}
|
|
|
|
return closestPoint;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 鼠标变量更新
|
|
/// </summary>
|
|
void MouseValueUpdate()
|
|
{
|
|
if (Input.GetMouseButton(0))
|
|
MouseLValue = new Vector2(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y")) * Time.deltaTime;
|
|
MouseLValue = Vector2.Lerp(MouseLValue, Vector2.zero, Time.deltaTime);
|
|
if (Input.GetMouseButton(1))
|
|
MouseRValue = new Vector2(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y")) * Time.deltaTime;
|
|
MouseRValue = Vector2.Lerp(MouseRValue, Vector2.zero, Time.deltaTime);
|
|
MouseCValue = -Input.GetAxis("Mouse ScrollWheel") * Time.deltaTime;
|
|
MouseCValue = Mathf.Lerp(MouseCValue, 0, Time.deltaTime);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 旋转
|
|
/// </summary>
|
|
void RotationController() {
|
|
|
|
Vector3 X = Vector3.Cross(CameraToPoint, Vector3.up).normalized;
|
|
Vector3 Z = Vector3.Cross(Vector3.up, X).normalized;
|
|
Vector3 Y = Vector3.Cross(X, Z).normalized;
|
|
Debug.DrawLine(Point, Point + X * 10, Color.red);
|
|
Debug.DrawLine(Point, Point + Z * 10, Color.blue);
|
|
Debug.DrawLine(Point, Point + Y * 10, Color.green);
|
|
|
|
float Lenght = CameraToPoint.magnitude;
|
|
PointRotationInit -= MouseRValue.y;
|
|
PointRotationInit = Mathf.Clamp01(PointRotationInit);
|
|
Vector3 DirNext = Vector3.Lerp(Vector3.Lerp(Z, Y, PointVerticalRotationScpoe.Min / 90), Vector3.Lerp(Z, Y, PointVerticalRotationScpoe.Max / 90), PointRotationInit).normalized;
|
|
transform.position = Point + DirNext * Lenght;
|
|
|
|
transform.RotateAround(Point, Y, MouseRValue.x * PointHorizontalRotationSpeed);
|
|
|
|
transform.LookAt(Point);
|
|
CameraToPoint = transform.position - Point;
|
|
}
|
|
|
|
private void OnDrawGizmos()
|
|
{
|
|
Gizmos.color = Color.yellow;
|
|
|
|
for (int i = 0; i < PointScpoeData.Count; i++) {
|
|
Vector3 S = PointScpoeData[i];
|
|
S = PointScpoePlane.position + PointScpoePlane.right * S.x + PointScpoePlane.forward * S.z;
|
|
|
|
Vector3 E = PointScpoeData[(i + 1) % PointScpoeData.Count];
|
|
E = PointScpoePlane.position + PointScpoePlane.right * E.x + PointScpoePlane.forward * E.z;
|
|
|
|
Gizmos.DrawLine(S,E);
|
|
}
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
MouseValueUpdate();
|
|
RotationController();
|
|
PointOperation();
|
|
}
|
|
}
|
|
|
|
[Serializable]
|
|
public struct Scpoe
|
|
{
|
|
public float Min;
|
|
public float Max;
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
[CustomEditor(typeof(CameraControllerTest))]
|
|
public class CameraControllerTestEditor : Editor
|
|
{
|
|
|
|
private CameraControllerTest cameraControllerTest;
|
|
|
|
private void OnEnable()
|
|
{
|
|
cameraControllerTest = target as CameraControllerTest;
|
|
SceneView.duringSceneGui += SceneGUI;
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
SceneView.duringSceneGui -= SceneGUI;
|
|
}
|
|
|
|
private void SceneGUI(SceneView sceneView)
|
|
{
|
|
var plane = serializedObject.FindProperty("PointScpoePlane").objectReferenceValue as Transform;
|
|
if (plane == null)
|
|
return;
|
|
|
|
var data = serializedObject.FindProperty("PointScpoeData");
|
|
|
|
GUIStyle style = new GUIStyle();
|
|
style.normal.textColor = Color.yellow;
|
|
|
|
for (int i = 0; i < data.arraySize; i++)
|
|
{
|
|
var v3 = data.GetArrayElementAtIndex(i);
|
|
Vector3 vector = v3.vector3Value;
|
|
Vector3 point = plane.position + plane.right * vector.x + plane.forward * vector.z;
|
|
Handles.Label(point, i.ToString(), style);
|
|
Vector3 pointGUI = Handles.PositionHandle(point, plane.rotation);
|
|
v3.vector3Value = CameraControllerTest.PointOnPlane(pointGUI, plane.right,plane.forward,plane.position);
|
|
}
|
|
|
|
// 这里应用值会实时覆盖Undo, 所以要排除
|
|
serializedObject.ApplyModifiedPropertiesWithoutUndo();
|
|
}
|
|
|
|
}
|
|
#endif
|