quality-control/Assets/Plugins/KinematicCharacterController/ExampleCharacter/Scripts/ExampleCharacterCamera.cs

179 lines
7.2 KiB
C#
Raw Normal View History

2024-08-16 22:50:47 +00:00
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace KinematicCharacterController.Examples
{
public class ExampleCharacterCamera : MonoBehaviour
{
[Header("Framing")]
public Camera Camera;
public Vector2 FollowPointFraming = new Vector2(0f, 0f);
public float FollowingSharpness = 10000f;
[Header("Distance")]
public float DefaultDistance = 6f;
public float MinDistance = 0f;
public float MaxDistance = 10f;
public float DistanceMovementSpeed = 5f;
public float DistanceMovementSharpness = 10f;
[Header("Rotation")]
public bool InvertX = false;
public bool InvertY = false;
[Range(-90f, 90f)]
public float DefaultVerticalAngle = 20f;
[Range(-90f, 90f)]
public float MinVerticalAngle = -90f;
[Range(-90f, 90f)]
public float MaxVerticalAngle = 90f;
public float RotationSpeed = 1f;
public float RotationSharpness = 10000f;
public bool RotateWithPhysicsMover = false;
[Header("Obstruction")]
public float ObstructionCheckRadius = 0.2f;
public LayerMask ObstructionLayers = -1;
public float ObstructionSharpness = 10000f;
public List<Collider> IgnoredColliders = new List<Collider>();
public Transform Transform { get; private set; }
public Transform FollowTransform { get; private set; }
public Vector3 PlanarDirection { get; set; }
public float TargetDistance { get; set; }
private bool _distanceIsObstructed;
private float _currentDistance;
private float _targetVerticalAngle;
private RaycastHit _obstructionHit;
private int _obstructionCount;
private RaycastHit[] _obstructions = new RaycastHit[MaxObstructions];
private float _obstructionTime;
private Vector3 _currentFollowPosition;
private const int MaxObstructions = 32;
void OnValidate()
{
DefaultDistance = Mathf.Clamp(DefaultDistance, MinDistance, MaxDistance);
DefaultVerticalAngle = Mathf.Clamp(DefaultVerticalAngle, MinVerticalAngle, MaxVerticalAngle);
}
void Awake()
{
Transform = this.transform;
_currentDistance = DefaultDistance;
TargetDistance = _currentDistance;
_targetVerticalAngle = 0f;
PlanarDirection = Vector3.forward;
}
// Set the transform that the camera will orbit around
public void SetFollowTransform(Transform t)
{
FollowTransform = t;
PlanarDirection = FollowTransform.forward;
_currentFollowPosition = FollowTransform.position;
}
public void UpdateWithInput(float deltaTime, float zoomInput, Vector3 rotationInput)
{
if (FollowTransform)
{
if (InvertX)
{
rotationInput.x *= -1f;
}
if (InvertY)
{
rotationInput.y *= -1f;
}
// Process rotation input
Quaternion rotationFromInput = Quaternion.Euler(FollowTransform.up * (rotationInput.x * RotationSpeed));
PlanarDirection = rotationFromInput * PlanarDirection;
PlanarDirection = Vector3.Cross(FollowTransform.up, Vector3.Cross(PlanarDirection, FollowTransform.up));
Quaternion planarRot = Quaternion.LookRotation(PlanarDirection, FollowTransform.up);
_targetVerticalAngle -= (rotationInput.y * RotationSpeed);
_targetVerticalAngle = Mathf.Clamp(_targetVerticalAngle, MinVerticalAngle, MaxVerticalAngle);
Quaternion verticalRot = Quaternion.Euler(_targetVerticalAngle, 0, 0);
Quaternion targetRotation = Quaternion.Slerp(Transform.rotation, planarRot * verticalRot, 1f - Mathf.Exp(-RotationSharpness * deltaTime));
// Apply rotation
Transform.rotation = targetRotation;
// Process distance input
if (_distanceIsObstructed && Mathf.Abs(zoomInput) > 0f)
{
TargetDistance = _currentDistance;
}
TargetDistance += zoomInput * DistanceMovementSpeed;
TargetDistance = Mathf.Clamp(TargetDistance, MinDistance, MaxDistance);
// Find the smoothed follow position
_currentFollowPosition = Vector3.Lerp(_currentFollowPosition, FollowTransform.position, 1f - Mathf.Exp(-FollowingSharpness * deltaTime));
// Handle obstructions
{
RaycastHit closestHit = new RaycastHit();
closestHit.distance = Mathf.Infinity;
_obstructionCount = Physics.SphereCastNonAlloc(_currentFollowPosition, ObstructionCheckRadius, -Transform.forward, _obstructions, TargetDistance, ObstructionLayers, QueryTriggerInteraction.Ignore);
for (int i = 0; i < _obstructionCount; i++)
{
bool isIgnored = false;
for (int j = 0; j < IgnoredColliders.Count; j++)
{
if (IgnoredColliders[j] == _obstructions[i].collider)
{
isIgnored = true;
break;
}
}
for (int j = 0; j < IgnoredColliders.Count; j++)
{
if (IgnoredColliders[j] == _obstructions[i].collider)
{
isIgnored = true;
break;
}
}
if (!isIgnored && _obstructions[i].distance < closestHit.distance && _obstructions[i].distance > 0)
{
closestHit = _obstructions[i];
}
}
// If obstructions detecter
if (closestHit.distance < Mathf.Infinity)
{
_distanceIsObstructed = true;
_currentDistance = Mathf.Lerp(_currentDistance, closestHit.distance, 1 - Mathf.Exp(-ObstructionSharpness * deltaTime));
}
// If no obstruction
else
{
_distanceIsObstructed = false;
_currentDistance = Mathf.Lerp(_currentDistance, TargetDistance, 1 - Mathf.Exp(-DistanceMovementSharpness * deltaTime));
}
}
// Find the smoothed camera orbit position
Vector3 targetPosition = _currentFollowPosition - ((targetRotation * Vector3.forward) * _currentDistance);
// Handle framing
targetPosition += Transform.right * FollowPointFraming.x;
targetPosition += Transform.up * FollowPointFraming.y;
// Apply position
Transform.position = targetPosition;
}
}
}
}