_xiaofang/xiaofang/Assets/Script/Character/CameraControl.cs

306 lines
11 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraControl : MonoBehaviour
{
public Transform Character; // 玩家的引用。
public Vector3 pivotOffset = new Vector3(0.0f, 1.7f, 0.0f); // 相机相对于玩家的偏移量,用于调整相机的中心点。
public Vector3 camOffset = new Vector3(0.4f, 0.0f, -2.0f); // 相机相对于玩家位置的偏移量,用于定位相机。
public float smooth = 10f; // 相机响应的平滑速度。
public float horizontalAimingSpeed = 6f; // 水平转动速度。
public float verticalAimingSpeed = 6f; // 垂直转动速度。
public float maxVerticalAngle = 30f; // 相机最大垂直夹角。
public float minVerticalAngle = -60f; // 相机最小垂直夹角。
private float angleH = 0; // 用于存储相机水平角度的变量,基于鼠标移动。
private float angleV = 0; // 用于存储相机垂直角度的变量,基于鼠标移动。
private Transform cam; // 相机的变换组件。
private Vector3 smoothPivotOffset; // 当前的中心点偏移量,用于插值计算。
private Vector3 smoothCamOffset; // 当前的相机偏移量,用于插值计算。
private Vector3 targetPivotOffset; // 中心点偏移量目标,用于插值计算。
private Vector3 targetCamOffset; // 相机偏移量目标,用于插值计算。
private float defaultFOV; // 默认相机视场角FOV
private float targetFOV; // 目标相机视场角。
private float targetMaxVerticalAngle; // 自定义相机的最大垂直夹角。
private bool isCustomOffset; // 布尔值,判断是否正在使用自定义相机偏移量。
private float deltaH = 0; // 用于锁定相机方向时的水平旋转增量。
private Vector3 firstDirection; // 用于第一次锁定相机方向时的方向。
private Vector3 directionToLock; // 当前锁定相机的方向。
private float recoilAngle = 0f; // 垂直抖动相机时的角度,用于模拟后坐力。
private Vector3 forwardHorizontalRef; // 水平平面上的前向参考方向,用于限制相机旋转。
private float leftRelHorizontalAngle, rightRelHorizontalAngle; // 用于限制相机水平旋转的左右相对角度。
// 获取相机的水平角度。
public float GetH => angleH;
void Awake()
{
// 引用相机的变换组件。
cam = transform;
// 设置相机的默认位置。
cam.position = Character.position + Quaternion.identity * pivotOffset + Quaternion.identity * camOffset;
cam.rotation = Quaternion.identity;
// 设置参考值和默认参数。
smoothPivotOffset = pivotOffset;
smoothCamOffset = camOffset;
defaultFOV = cam.GetComponent<Camera>().fieldOfView;
angleH = Character.eulerAngles.y;
ResetTargetOffsets();
ResetFOV();
ResetMaxVerticalAngle();
// 检查是否存在垂直偏移。
if (camOffset.y > 0)
Debug.LogWarning("垂直相机偏移量 (Y) 在碰撞时会被忽略!\n" +"建议将所有垂直偏移量设置在 Pivot Offset 中。");
}
void Update()
{
// 获取鼠标移动来控制相机的旋转。
// 鼠标控制:
// 设置垂直移动的限制。
angleV = Mathf.Clamp(angleV, minVerticalAngle, targetMaxVerticalAngle);
// 设置垂直相机抖动(模拟后坐力)。
angleV = Mathf.LerpAngle(angleV, angleV + recoilAngle, 10f * Time.deltaTime);
//// 处理相机方向锁定。
if (firstDirection != Vector3.zero)
{
angleH -= deltaH;
UpdateLockAngle();
angleH += deltaH;
}
// 处理相机的水平旋转限制(如果设置了)。
if (forwardHorizontalRef != default(Vector3))
{
ClampHorizontal();
}
// 设置相机的方向。
Quaternion camYRotation = Quaternion.Euler(0, angleH, 0);
Quaternion aimRotation = Quaternion.Euler(-angleV, angleH, 0);
cam.rotation = aimRotation;
// 设置相机的视场角。
cam.GetComponent<Camera>().fieldOfView = Mathf.Lerp(cam.GetComponent<Camera>().fieldOfView, targetFOV, Time.deltaTime);
// 基于当前相机位置,检测与环境的碰撞。
Vector3 baseTempPosition = Character.position + camYRotation * targetPivotOffset;
Vector3 noCollisionOffset = targetCamOffset;
while (noCollisionOffset.magnitude >= 0.2f)
{
if (DoubleViewingPosCheck(baseTempPosition + aimRotation * noCollisionOffset))
break;
noCollisionOffset -= noCollisionOffset.normalized * 0.2f;
}
if (noCollisionOffset.magnitude < 0.2f)
noCollisionOffset = Vector3.zero;
// 如果有自定义偏移,并且相机碰撞,将进入第一人称模式。
bool customOffsetCollision = isCustomOffset && noCollisionOffset.sqrMagnitude < targetCamOffset.sqrMagnitude;
// 重新定位相机。
smoothPivotOffset = Vector3.Lerp(smoothPivotOffset, customOffsetCollision ? pivotOffset : targetPivotOffset, smooth * Time.deltaTime);
smoothCamOffset = Vector3.Lerp(smoothCamOffset, customOffsetCollision ? Vector3.zero : noCollisionOffset, smooth * Time.deltaTime);
cam.position = Character.position + camYRotation * smoothPivotOffset + aimRotation * smoothCamOffset;
// 平滑相机的垂直抖动(后坐力)。
if (recoilAngle > 0)
recoilAngle -= 5 * Time.deltaTime;
else if (recoilAngle < 0)
recoilAngle += 5 * Time.deltaTime;
}
// 设置/取消水平旋转的限制。
public void ToggleClampHorizontal(float LeftAngle = 0, float RightAngle = 0, Vector3 fwd = default(Vector3))
{
forwardHorizontalRef = fwd;
leftRelHorizontalAngle = LeftAngle;
rightRelHorizontalAngle = RightAngle;
}
// 限制相机的水平旋转。
private void ClampHorizontal()
{
// 获取相机当前朝向和参考方向之间的角度。
Vector3 cam2dFwd = this.transform.forward;
cam2dFwd.y = 0;
float angleBetween = Vector3.Angle(cam2dFwd, forwardHorizontalRef);
float sign = Mathf.Sign(Vector3.Cross(cam2dFwd, forwardHorizontalRef).y);
angleBetween = angleBetween * sign;
// 获取当前输入移动,以在达到限制角度时进行补偿。
float acc = Mathf.Clamp(Input.GetAxis("Mouse X"), -1, 1) * horizontalAimingSpeed;
acc += Mathf.Clamp(Input.GetAxis("Analog X"), -1, 1) * 60 * horizontalAimingSpeed * Time.deltaTime;
// 限制左侧角度。
if (sign < 0 && angleBetween < leftRelHorizontalAngle)
{
if (acc > 0)
angleH -= acc;
}
// 限制右侧角度。
else if (angleBetween > rightRelHorizontalAngle)
{
if (acc < 0)
angleH -= acc;
}
}
// 处理锁定特定方向时的相机朝向。
private void UpdateLockAngle()
{
directionToLock.y = 0f;
float centerLockAngle = Vector3.Angle(firstDirection, directionToLock);
Vector3 cross = Vector3.Cross(firstDirection, directionToLock);
if (cross.y < 0) centerLockAngle = -centerLockAngle;
deltaH = centerLockAngle;
}
// 锁定相机朝向跟随特定方向。通常用于短暂的动作。
public void LockOnDirection(Vector3 direction)
{
if (firstDirection == Vector3.zero)
{
firstDirection = direction;
firstDirection.y = 0f;
}
directionToLock = Vector3.Lerp(directionToLock, direction, 0.15f * smooth * Time.deltaTime);
}
// 解锁相机方向。
public void UnlockOnDirection()
{
deltaH = 0;
firstDirection = directionToLock = Vector3.zero;
}
// 设置自定义相机偏移量。
public void SetTargetOffsets(Vector3 newPivotOffset, Vector3 newCamOffset)
{
targetPivotOffset = newPivotOffset;
targetCamOffset = newCamOffset;
isCustomOffset = true;
}
// 重置相机偏移量为默认值。
public void ResetTargetOffsets()
{
targetPivotOffset = pivotOffset;
targetCamOffset = camOffset;
isCustomOffset = false;
}
// 重置相机垂直偏移量。
public void ResetYCamOffset()
{
targetCamOffset.y = camOffset.y;
}
// 设置相机垂直偏移量。
public void SetYCamOffset(float y)
{
targetCamOffset.y = y;
}
// 设置相机水平偏移量。
public void SetXCamOffset(float x)
{
targetCamOffset.x = x;
}
// 设置自定义视场角FOV
public void SetFOV(float customFOV)
{
this.targetFOV = customFOV;
}
// 重置视场角为默认值。
public void ResetFOV()
{
this.targetFOV = defaultFOV;
}
// 设置最大垂直相机旋转角度。
public void SetMaxVerticalAngle(float angle)
{
this.targetMaxVerticalAngle = angle;
}
// 重置最大垂直相机旋转角度为默认值。
public void ResetMaxVerticalAngle()
{
this.targetMaxVerticalAngle = maxVerticalAngle;
}
// 双重检测碰撞:某些凹面物体无法从外部检测到,因此从两个方向进行检测。
bool DoubleViewingPosCheck(Vector3 checkPos)
{
return ViewingPosCheck(checkPos) && ReverseViewingPosCheck(checkPos);
}
// 检查相机到玩家的碰撞。
bool ViewingPosCheck(Vector3 checkPos)
{
// 定义目标和方向。
Vector3 target = Character.position + pivotOffset;
Vector3 direction = target - checkPos;
// 如果从检测位置到玩家的射线检测到障碍物...
if (Physics.SphereCast(checkPos, 0.2f, direction, out RaycastHit hit, direction.magnitude))
{
// 如果不是玩家...
if (hit.transform != Character && !hit.transform.GetComponent<Collider>().isTrigger)
{
// 这个位置不合适。
return false;
}
}
// 如果没有检测到任何障碍物,或者检测到的是玩家,这个位置合适。
return true;
}
// 检查从玩家到相机的碰撞。
bool ReverseViewingPosCheck(Vector3 checkPos)
{
// 定义起点和方向。
Vector3 origin = Character.position + pivotOffset;
Vector3 direction = checkPos - origin;
if (Physics.SphereCast(origin, 0.2f, direction, out RaycastHit hit, direction.magnitude))
{
if (hit.transform != Character && hit.transform != transform && !hit.transform.GetComponent<Collider>().isTrigger)
{
return false;
}
}
return true;
}
// 获取当前相机的中心点偏移量的大小。
public float GetCurrentPivotMagnitude(Vector3 finalPivotOffset)
{
return Mathf.Abs((finalPivotOffset - smoothPivotOffset).magnitude);
}
public void CamerMove()
{
angleH += Mathf.Clamp(Input.GetAxis("Mouse X"), -1, 1) * horizontalAimingSpeed;
angleV += Mathf.Clamp(Input.GetAxis("Mouse Y"), -1, 1) * verticalAimingSpeed;
}
}