491 lines
14 KiB
C#
491 lines
14 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
public class CameraController : MonoBehaviour
|
|
{
|
|
#region Singleton
|
|
|
|
/// <summary>
|
|
/// Singleton instance of the CameraController.
|
|
/// </summary>
|
|
public static CameraController Instance { get; private set; }
|
|
|
|
#endregion
|
|
|
|
#region Camera Settings
|
|
|
|
/// <summary>
|
|
/// Determines if mouse input is active for camera control.
|
|
/// </summary>
|
|
public bool mouseActive = true;
|
|
|
|
/// <summary>
|
|
/// Locks user input for camera control.
|
|
/// </summary>
|
|
public bool lockUserInput = false;
|
|
|
|
/// <summary>
|
|
/// Normal speed of camera movement.
|
|
/// </summary>
|
|
[SerializeField] private float _cameraSpeedNormal = 100.0f;
|
|
|
|
/// <summary>
|
|
/// Maximum speed of camera movement.
|
|
/// </summary>
|
|
[SerializeField] private float _cameraSpeedMax = 1000.0f;
|
|
|
|
/// <summary>
|
|
/// Current speed of camera movement.
|
|
/// </summary>
|
|
private float _cameraSpeed;
|
|
|
|
/// <summary>
|
|
/// Horizontal rotation speed.
|
|
/// </summary>
|
|
public float _speedH = 2.0f;
|
|
|
|
/// <summary>
|
|
/// Vertical rotation speed.
|
|
/// </summary>
|
|
public float _speedV = 2.0f;
|
|
|
|
/// <summary>
|
|
/// Current yaw angle of the camera.
|
|
/// </summary>
|
|
private float _yaw = 0.0f;
|
|
|
|
/// <summary>
|
|
/// Current pitch angle of the camera.
|
|
/// </summary>
|
|
private float _pitch = 0.0f;
|
|
|
|
#endregion
|
|
|
|
#region Orbit Settings
|
|
|
|
/// <summary>
|
|
/// Determines if the camera should auto-rotate.
|
|
/// </summary>
|
|
public bool _autoRotate = false;
|
|
|
|
/// <summary>
|
|
/// Threat transform for orbit rotation.
|
|
/// </summary>
|
|
public Transform target;
|
|
|
|
/// <summary>
|
|
/// Distance from the camera to the orbit target.
|
|
/// </summary>
|
|
[SerializeField] private float _orbitDistance = 5.0f;
|
|
|
|
/// <summary>
|
|
/// Horizontal orbit rotation speed.
|
|
/// </summary>
|
|
[SerializeField] private float _orbitXSpeed = 120.0f;
|
|
|
|
/// <summary>
|
|
/// Vertical orbit rotation speed.
|
|
/// </summary>
|
|
[SerializeField] private float _orbitYSpeed = 120.0f;
|
|
|
|
/// <summary>
|
|
/// Speed of camera zoom.
|
|
/// </summary>
|
|
[SerializeField] private float _zoomSpeed = 500.0f;
|
|
|
|
/// <summary>
|
|
/// Minimum vertical angle limit for orbit.
|
|
/// </summary>
|
|
public float orbitYMinLimit = -20f;
|
|
|
|
/// <summary>
|
|
/// Maximum vertical angle limit for orbit.
|
|
/// </summary>
|
|
public float orbitYMaxLimit = 80f;
|
|
|
|
/// <summary>
|
|
/// Minimum distance for orbit.
|
|
/// </summary>
|
|
private float _orbitDistanceMin = 10f;
|
|
|
|
/// <summary>
|
|
/// Maximum distance for orbit.
|
|
/// </summary>
|
|
[SerializeField]
|
|
private float _orbitDistanceMax = 20000f;
|
|
|
|
/// <summary>
|
|
/// Current horizontal orbit angle.
|
|
/// </summary>
|
|
private float _orbitX = 0.0f;
|
|
|
|
/// <summary>
|
|
/// Current vertical orbit angle.
|
|
/// </summary>
|
|
private float _orbitY = 0.0f;
|
|
|
|
#endregion
|
|
|
|
#region Rendering
|
|
|
|
/// <summary>
|
|
/// Renderer for the orbit target.
|
|
/// </summary>
|
|
public Renderer targetRenderer;
|
|
|
|
/// <summary>
|
|
/// Renderer for the floor.
|
|
/// </summary>
|
|
public Renderer floorRenderer;
|
|
|
|
/// <summary>
|
|
/// Alpha value for material transparency.
|
|
/// </summary>
|
|
public float matAlpha;
|
|
|
|
#endregion
|
|
|
|
#region Autoplay Settings
|
|
|
|
/// <summary>
|
|
/// Speed of camera movement during autoplay.
|
|
/// </summary>
|
|
public float autoplayCamSpeed = 2f;
|
|
|
|
/// <summary>
|
|
/// Duration of horizontal auto-rotation.
|
|
/// </summary>
|
|
public float xAutoRotateTime = 5f;
|
|
|
|
/// <summary>
|
|
/// Duration of vertical auto-rotation.
|
|
/// </summary>
|
|
public float yAutoRotateTime = 5f;
|
|
|
|
/// <summary>
|
|
/// Coroutine for autoplay functionality.
|
|
/// </summary>
|
|
private Coroutine autoplayRoutine;
|
|
|
|
#endregion
|
|
|
|
#region Camera Presets
|
|
|
|
/// <summary>
|
|
/// Represents a preset camera position and rotation.
|
|
/// </summary>
|
|
[System.Serializable]
|
|
public struct CameraPreset
|
|
{
|
|
public Vector3 position;
|
|
public Quaternion rotation;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Preset camera position for key 4.
|
|
/// </summary>
|
|
CameraPreset fourPos = new CameraPreset();
|
|
|
|
/// <summary>
|
|
/// Preset camera position for key 5.
|
|
/// </summary>
|
|
CameraPreset fivePos = new CameraPreset();
|
|
|
|
/// <summary>
|
|
/// Preset camera position for key 6.
|
|
/// </summary>
|
|
CameraPreset sixPos = new CameraPreset();
|
|
|
|
#endregion
|
|
|
|
#region Movement
|
|
|
|
/// <summary>
|
|
/// Mapping of translation inputs to movement vectors.
|
|
/// </summary>
|
|
private Dictionary<TranslationInput, Vector3> _translationInputToVectorMap;
|
|
|
|
/// <summary>
|
|
/// Forward movement vector.
|
|
/// </summary>
|
|
Vector3 wVector = Vector3.forward;
|
|
|
|
/// <summary>
|
|
/// Left movement vector.
|
|
/// </summary>
|
|
Vector3 aVector = Vector3.left;
|
|
|
|
/// <summary>
|
|
/// Backward movement vector.
|
|
/// </summary>
|
|
Vector3 sVector = Vector3.back;
|
|
|
|
/// <summary>
|
|
/// Right movement vector.
|
|
/// </summary>
|
|
Vector3 dVector = Vector3.right;
|
|
|
|
/// <summary>
|
|
/// Angle between forward vector and camera direction.
|
|
/// </summary>
|
|
public float forwardToCameraAngle;
|
|
|
|
#endregion
|
|
|
|
void SetCameraRotation(Quaternion rotation)
|
|
{
|
|
transform.rotation = rotation;
|
|
_pitch = rotation.eulerAngles.x;
|
|
_yaw = rotation.eulerAngles.y;
|
|
}
|
|
|
|
public void SetCameraSpeed(float speed)
|
|
{
|
|
_cameraSpeed = speed;
|
|
}
|
|
|
|
public float GetCameraSpeedMax()
|
|
{
|
|
return _cameraSpeedMax;
|
|
}
|
|
|
|
public float GetCameraSpeedNormal()
|
|
{
|
|
return _cameraSpeedNormal;
|
|
}
|
|
|
|
public bool IsAutoRotate()
|
|
{
|
|
return _autoRotate;
|
|
}
|
|
|
|
public void SetAutoRotate(bool autoRotate)
|
|
{
|
|
if (autoRotate && !_autoRotate) {
|
|
_autoRotate = true;
|
|
autoplayRoutine = StartCoroutine(AutoPlayRoutine());
|
|
} else if (!autoRotate && _autoRotate) {
|
|
_autoRotate = false;
|
|
StopCoroutine(autoplayRoutine);
|
|
}
|
|
}
|
|
|
|
public static float ClampAngle(float angle, float min, float max)
|
|
{
|
|
if (angle < -360F)
|
|
angle += 360F;
|
|
if (angle > 360F)
|
|
angle -= 360F;
|
|
return Mathf.Clamp(angle, min, max);
|
|
}
|
|
|
|
private void Awake()
|
|
{
|
|
if (Instance == null) {
|
|
Instance = this;
|
|
DontDestroyOnLoad(gameObject);
|
|
} else {
|
|
Destroy(gameObject);
|
|
}
|
|
|
|
_translationInputToVectorMap = new Dictionary<TranslationInput, Vector3> {
|
|
{TranslationInput.Forward, wVector},
|
|
{TranslationInput.Left, aVector},
|
|
{TranslationInput.Back, sVector},
|
|
{TranslationInput.Right, dVector},
|
|
{TranslationInput.Up, Vector3.up},
|
|
{TranslationInput.Down, Vector3.down}
|
|
};
|
|
}
|
|
|
|
// Start is called before the first frame update
|
|
void Start()
|
|
{
|
|
fourPos.position = new Vector3(0, 0, 0);
|
|
fourPos.rotation = Quaternion.Euler(0,0,0);
|
|
fivePos.position = new Vector3(0, 0, 0);
|
|
fivePos.rotation = Quaternion.Euler(0, 0, 0);
|
|
sixPos.position = new Vector3(0, 0, 0);
|
|
sixPos.rotation = Quaternion.Euler(0, 0, 0);
|
|
|
|
Vector3 angles = transform.eulerAngles;
|
|
_orbitX = angles.y;
|
|
_orbitY = angles.x;
|
|
|
|
UpdateTargetAlpha();
|
|
ResetCameraTarget();
|
|
}
|
|
|
|
IEnumerator AutoPlayRoutine()
|
|
{
|
|
while (true)
|
|
{
|
|
float elapsedTime = 0f;
|
|
while (elapsedTime <= xAutoRotateTime)
|
|
{
|
|
_orbitX += Time.unscaledDeltaTime * autoplayCamSpeed * _orbitDistance * 0.02f;
|
|
UpdateCamPosition(_orbitX, _orbitY);
|
|
elapsedTime += Time.unscaledDeltaTime;
|
|
yield return null;
|
|
}
|
|
elapsedTime = 0f;
|
|
while (elapsedTime <= yAutoRotateTime)
|
|
{
|
|
_orbitY -= Time.unscaledDeltaTime * autoplayCamSpeed * _orbitDistance * 0.02f;
|
|
UpdateCamPosition(_orbitX, _orbitY);
|
|
elapsedTime += Time.unscaledDeltaTime;
|
|
yield return null;
|
|
}
|
|
elapsedTime = 0f;
|
|
while (elapsedTime <= xAutoRotateTime)
|
|
{
|
|
_orbitX -= Time.unscaledDeltaTime * autoplayCamSpeed * _orbitDistance * 0.02f;
|
|
UpdateCamPosition(_orbitX, _orbitY);
|
|
elapsedTime += Time.unscaledDeltaTime;
|
|
yield return null;
|
|
}
|
|
elapsedTime = 0f;
|
|
while (elapsedTime <= yAutoRotateTime)
|
|
{
|
|
_orbitY += Time.unscaledDeltaTime * autoplayCamSpeed * _orbitDistance * 0.02f;
|
|
UpdateCamPosition(_orbitX, _orbitY);
|
|
elapsedTime += Time.unscaledDeltaTime;
|
|
yield return null;
|
|
}
|
|
yield return null;
|
|
}
|
|
|
|
}
|
|
void ResetCameraTarget()
|
|
{
|
|
RaycastHit hit;
|
|
if(Physics.Raycast(transform.position, transform.forward, out hit, float.MaxValue, LayerMask.GetMask("Floor"), QueryTriggerInteraction.Ignore))
|
|
{
|
|
target.transform.position = hit.point;
|
|
_orbitDistance = hit.distance;
|
|
Vector3 angles = transform.eulerAngles;
|
|
_orbitX = angles.y;
|
|
_orbitY = angles.x;
|
|
UpdateCamPosition(_orbitX, _orbitY);
|
|
}
|
|
else
|
|
{
|
|
target.transform.position = transform.position + (transform.forward * 100);
|
|
_orbitDistance = 100;
|
|
Vector3 angles = transform.eulerAngles;
|
|
_orbitX = angles.y;
|
|
_orbitY = angles.x;
|
|
//UpdateCamPosition();
|
|
}
|
|
}
|
|
|
|
public void EnableTargetRenderer(bool enable) { targetRenderer.enabled = enable; }
|
|
|
|
public void EnableFloorGridRenderer(bool enable) { floorRenderer.enabled = enable; }
|
|
|
|
|
|
public void OrbitCamera(float xOrbit, float yOrbit) {
|
|
if (target)
|
|
{
|
|
_orbitX += xOrbit * _orbitXSpeed * _orbitDistance * 0.02f;
|
|
_orbitY -= yOrbit * _orbitYSpeed * _orbitDistance * 0.02f;
|
|
|
|
_orbitY = ClampAngle(_orbitY, orbitYMinLimit, orbitYMaxLimit);
|
|
UpdateCamPosition(_orbitX, _orbitY);
|
|
}
|
|
}
|
|
|
|
public void RotateCamera(float xRotate, float yRotate) {
|
|
_yaw += xRotate * _speedH;
|
|
_pitch -= yRotate * _speedV;
|
|
transform.eulerAngles = new Vector3(_pitch, _yaw, 0.0f);
|
|
}
|
|
|
|
private void UpdateCamPosition(float x, float y)
|
|
{
|
|
Quaternion rotation = Quaternion.Euler(y, x, 0);
|
|
RaycastHit hit;
|
|
//Debug.DrawLine(target.position, transform.position, Color.red);
|
|
if (Physics.Linecast(target.position, transform.position, out hit, ~LayerMask.GetMask("Floor"), QueryTriggerInteraction.Ignore))
|
|
{
|
|
_orbitDistance -= hit.distance;
|
|
}
|
|
Vector3 negDistance = new Vector3(0.0f, 0.0f, -_orbitDistance);
|
|
Vector3 position = rotation * negDistance + target.position;
|
|
UpdateTargetAlpha();
|
|
|
|
SetCameraRotation(rotation);
|
|
transform.position = position;
|
|
}
|
|
|
|
|
|
public void ZoomCamera(float zoom)
|
|
{
|
|
_orbitDistance = Mathf.Clamp(_orbitDistance - zoom * _zoomSpeed, _orbitDistanceMin, _orbitDistanceMax);
|
|
UpdateCamPosition(_orbitX, _orbitY);
|
|
}
|
|
|
|
void UpdateTargetAlpha()
|
|
{
|
|
matAlpha = (_orbitDistance - _orbitDistanceMin) / (_orbitDistanceMax - _orbitDistanceMin);
|
|
matAlpha = Mathf.Max(Mathf.Sqrt(matAlpha) - 0.5f, 0);
|
|
Color matColor = targetRenderer.material.color;
|
|
matColor.a = matAlpha;
|
|
targetRenderer.material.color = matColor;
|
|
}
|
|
|
|
void UpdateDirectionVectors()
|
|
{
|
|
Vector3 cameraToTarget = target.position - transform.position;
|
|
cameraToTarget.y = 0;
|
|
forwardToCameraAngle = Vector3.SignedAngle(Vector3.forward, cameraToTarget, Vector3.down);
|
|
|
|
if(forwardToCameraAngle >-45f && forwardToCameraAngle <= 45f)
|
|
{
|
|
_translationInputToVectorMap[TranslationInput.Forward] = Vector3.forward;
|
|
_translationInputToVectorMap[TranslationInput.Left] = Vector3.left;
|
|
_translationInputToVectorMap[TranslationInput.Back] = Vector3.back;
|
|
_translationInputToVectorMap[TranslationInput.Right] = Vector3.right;
|
|
}
|
|
else if(forwardToCameraAngle > 45f && forwardToCameraAngle <= 135f)
|
|
{
|
|
_translationInputToVectorMap[TranslationInput.Forward] = Vector3.left;
|
|
_translationInputToVectorMap[TranslationInput.Left] = Vector3.back;
|
|
_translationInputToVectorMap[TranslationInput.Back] = Vector3.right;
|
|
_translationInputToVectorMap[TranslationInput.Right] = Vector3.forward;
|
|
}
|
|
else if(forwardToCameraAngle > 135f || forwardToCameraAngle <= -135f)
|
|
{
|
|
_translationInputToVectorMap[TranslationInput.Forward] = Vector3.back;
|
|
_translationInputToVectorMap[TranslationInput.Left] = Vector3.right;
|
|
_translationInputToVectorMap[TranslationInput.Back] = Vector3.forward;
|
|
_translationInputToVectorMap[TranslationInput.Right] = Vector3.left;
|
|
}
|
|
else if(forwardToCameraAngle > -135f && forwardToCameraAngle <= -45f)
|
|
{
|
|
_translationInputToVectorMap[TranslationInput.Forward] = Vector3.right;
|
|
_translationInputToVectorMap[TranslationInput.Left] = Vector3.forward;
|
|
_translationInputToVectorMap[TranslationInput.Back] = Vector3.left;
|
|
_translationInputToVectorMap[TranslationInput.Right] = Vector3.back;
|
|
}
|
|
|
|
}
|
|
|
|
public enum TranslationInput {
|
|
Forward,
|
|
Left,
|
|
Back,
|
|
Right,
|
|
Up,
|
|
Down
|
|
}
|
|
|
|
public void TranslateCamera(TranslationInput input) {
|
|
UpdateDirectionVectors();
|
|
target.Translate(_translationInputToVectorMap[input] * Time.unscaledDeltaTime * _cameraSpeed);
|
|
UpdateCamPosition(_orbitX, _orbitY);
|
|
}
|
|
|
|
}
|