133 lines
4.5 KiB
C#
133 lines
4.5 KiB
C#
using UnityEngine;
|
||
using UnityEngine.EventSystems;
|
||
using UnityEngine.InputSystem;
|
||
|
||
namespace Vanguard.Board3D
|
||
{
|
||
/// <summary>
|
||
/// Ограниченная орбита камеры: ПКМ — поворот, СКМ — панорама,
|
||
/// колесо — зум. Левая кнопка остаётся картам и UI.
|
||
/// </summary>
|
||
public sealed class BoardCameraRig : MonoBehaviour
|
||
{
|
||
private PlaymatLayoutConfig _config;
|
||
private Vector3 _target;
|
||
private float _distance = 15f;
|
||
private float _yaw;
|
||
private float _pitch = 35f;
|
||
private bool _orbiting;
|
||
private bool _panning;
|
||
private Vector2 _lastPointer;
|
||
|
||
public void Configure(PlaymatLayoutConfig config)
|
||
{
|
||
_config = config != null ? config : PlaymatLayoutConfig.Default;
|
||
_target = _config.cameraTarget;
|
||
var offset = _config.cameraPosition - _target;
|
||
_distance = Mathf.Clamp(
|
||
offset.magnitude,
|
||
_config.zoomMin,
|
||
_config.zoomMax);
|
||
_pitch = Mathf.Clamp(
|
||
Mathf.Asin(Mathf.Clamp(offset.y / Mathf.Max(0.001f, _distance), -1f, 1f))
|
||
* Mathf.Rad2Deg,
|
||
_config.orbitPitchMin,
|
||
_config.orbitPitchMax);
|
||
_yaw = Mathf.Atan2(-offset.x, -offset.z) * Mathf.Rad2Deg;
|
||
_yaw = Mathf.Clamp(_yaw, -_config.orbitYawLimit, _config.orbitYawLimit);
|
||
Apply();
|
||
}
|
||
|
||
private void Update()
|
||
{
|
||
if (_config == null)
|
||
return;
|
||
|
||
var mouse = Mouse.current;
|
||
if (mouse == null)
|
||
return;
|
||
|
||
var pointer = mouse.position.ReadValue();
|
||
var overUi = IsPointerOverUi();
|
||
|
||
if (mouse.rightButton.wasPressedThisFrame && !overUi)
|
||
{
|
||
_orbiting = true;
|
||
_lastPointer = pointer;
|
||
}
|
||
|
||
if (mouse.rightButton.wasReleasedThisFrame)
|
||
_orbiting = false;
|
||
|
||
if (_orbiting && mouse.rightButton.isPressed)
|
||
{
|
||
var delta = pointer - _lastPointer;
|
||
_lastPointer = pointer;
|
||
_yaw = Mathf.Clamp(
|
||
_yaw + (delta.x * 0.18f),
|
||
-_config.orbitYawLimit,
|
||
_config.orbitYawLimit);
|
||
_pitch = Mathf.Clamp(
|
||
_pitch - (delta.y * 0.16f),
|
||
_config.orbitPitchMin,
|
||
_config.orbitPitchMax);
|
||
Apply();
|
||
}
|
||
|
||
if (mouse.middleButton.wasPressedThisFrame && !overUi)
|
||
{
|
||
_panning = true;
|
||
_lastPointer = pointer;
|
||
}
|
||
|
||
if (mouse.middleButton.wasReleasedThisFrame)
|
||
_panning = false;
|
||
|
||
if (_panning && mouse.middleButton.isPressed)
|
||
{
|
||
var delta = pointer - _lastPointer;
|
||
_lastPointer = pointer;
|
||
var rotation = Quaternion.Euler(_pitch, _yaw, 0f);
|
||
var right = rotation * Vector3.right;
|
||
var forward = rotation * Vector3.forward;
|
||
right.y = 0f;
|
||
forward.y = 0f;
|
||
right.Normalize();
|
||
forward.Normalize();
|
||
|
||
var scale = _config.panSpeed * _distance;
|
||
_target -= (right * (delta.x * scale)) + (forward * (delta.y * scale));
|
||
_target.x = Mathf.Clamp(_target.x, -2.5f, 2.5f);
|
||
_target.z = Mathf.Clamp(_target.z, -2f, 2f);
|
||
Apply();
|
||
}
|
||
|
||
var scroll = mouse.scroll.ReadValue().y;
|
||
if (!overUi && Mathf.Abs(scroll) > 0.01f)
|
||
{
|
||
_distance = Mathf.Clamp(
|
||
_distance - (scroll * _config.zoomSpeed),
|
||
_config.zoomMin,
|
||
_config.zoomMax);
|
||
Apply();
|
||
}
|
||
}
|
||
|
||
private void Apply()
|
||
{
|
||
var rotation = Quaternion.Euler(_pitch, _yaw, 0f);
|
||
transform.rotation = rotation;
|
||
transform.position = _target - (rotation * Vector3.forward * _distance);
|
||
var camera = GetComponent<Camera>();
|
||
if (camera != null)
|
||
camera.fieldOfView = _config.cameraFov;
|
||
}
|
||
|
||
private static bool IsPointerOverUi()
|
||
{
|
||
var eventSystem = EventSystem.current;
|
||
return eventSystem != null && eventSystem.IsPointerOverGameObject();
|
||
}
|
||
}
|
||
}
|