更新
This commit is contained in:
@@ -0,0 +1,305 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace GraphicsCat
|
||||
{
|
||||
public class SlideRotationController : MonoBehaviour, IMGUIDockable
|
||||
{
|
||||
private const float INERTIA_THRESHOLD = 0.1f; // Threshold to stop inertia rotation
|
||||
|
||||
[Header("Slide Rotation")]
|
||||
[InspectorDisplayName("Enable")]
|
||||
public bool slideRotationEnabled = false;
|
||||
[InspectorDisplayName("Sensitivity")]
|
||||
public Vector3 slideRotationSensitivity = new(0, 20, 0);
|
||||
[InspectorDisplayName("Inertia Damping")]
|
||||
[Range(0f, 1f)]
|
||||
public float inertiaDamping = 0.8f; // 0 = no inertia, 1 = maximum inertia
|
||||
|
||||
[Header("Auto Rotation")]
|
||||
[InspectorDisplayName("Enable")]
|
||||
public bool autoRotationEnabled = true;
|
||||
[InspectorDisplayName("Speed")]
|
||||
public Vector3 autoRotationSpeed = new(0, 5, 0);
|
||||
|
||||
[Header("Other")]
|
||||
public bool displayInGameGUI = false;
|
||||
|
||||
// Input state
|
||||
private bool m_IsDragging = false;
|
||||
private Vector3 m_LastPointerPosition = Vector3.zero;
|
||||
private Vector3 m_PointerDelta = Vector3.zero;
|
||||
private Transform m_Target = null;
|
||||
|
||||
// Touch input
|
||||
private Touch m_MainTouch;
|
||||
private bool m_IsTouchActive = false;
|
||||
|
||||
// Auto rotation
|
||||
private bool m_WasAutoRotationEnabled;
|
||||
private bool m_IsAutoRotationPaused = false;
|
||||
|
||||
// Inertia
|
||||
private Vector3 m_CurrentInertia = Vector3.zero;
|
||||
private Vector3 m_LastAngularVelocity = Vector3.zero;
|
||||
private bool m_HasAngularMovement = false; // Track if there was actual angular movement
|
||||
|
||||
private void Start()
|
||||
{
|
||||
m_Target = transform;
|
||||
IMGUIDock.topRight.DockGUI(this);
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!slideRotationEnabled)
|
||||
return;
|
||||
|
||||
m_PointerDelta = Vector3.zero;
|
||||
|
||||
HandleMouseInput();
|
||||
|
||||
if (Application.isMobilePlatform)
|
||||
HandleTouchInput();
|
||||
}
|
||||
|
||||
private void FixedUpdate()
|
||||
{
|
||||
Vector3 slideRotationDelta = Vector3.zero;
|
||||
|
||||
if (slideRotationEnabled)
|
||||
{
|
||||
|
||||
// Handle manual rotation
|
||||
if (m_PointerDelta != Vector3.zero)
|
||||
{
|
||||
slideRotationDelta = ComputeRotationDelta(m_PointerDelta.x, m_PointerDelta.y);
|
||||
}
|
||||
// Apply inertia when not actively rotating
|
||||
else if (m_CurrentInertia.magnitude > INERTIA_THRESHOLD)
|
||||
{
|
||||
slideRotationDelta = m_CurrentInertia;
|
||||
m_CurrentInertia *= inertiaDamping;
|
||||
|
||||
if (m_CurrentInertia.magnitude < INERTIA_THRESHOLD)
|
||||
m_CurrentInertia = Vector3.zero;
|
||||
}
|
||||
|
||||
// Apply manual rotation or inertia
|
||||
if (slideRotationDelta != Vector3.zero)
|
||||
{
|
||||
Quaternion deltaRotation = Quaternion.Euler(slideRotationDelta * Time.fixedDeltaTime);
|
||||
m_Target.localRotation = deltaRotation * m_Target.localRotation;
|
||||
m_LastAngularVelocity = slideRotationDelta;
|
||||
m_HasAngularMovement = true; // There was actual rotation this frame
|
||||
}
|
||||
else
|
||||
{
|
||||
// No rotation this frame
|
||||
m_HasAngularMovement = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply auto rotation
|
||||
if (slideRotationDelta == Vector3.zero && autoRotationEnabled && !m_IsAutoRotationPaused)
|
||||
{
|
||||
ApplyAutoRotation();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply auto rotation according to configured speed.
|
||||
/// </summary>
|
||||
private void ApplyAutoRotation()
|
||||
{
|
||||
Quaternion rotation = Quaternion.Euler(autoRotationSpeed * Time.fixedDeltaTime);
|
||||
m_Target.localRotation = rotation * m_Target.localRotation;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute rotation delta based on pointer movement.
|
||||
/// </summary>
|
||||
private Vector3 ComputeRotationDelta(float x, float y)
|
||||
{
|
||||
return Vector3.Scale(new Vector3(y, x, 0), -slideRotationSensitivity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle mouse input for slide rotation.
|
||||
/// </summary>
|
||||
private void HandleMouseInput()
|
||||
{
|
||||
if (Input.GetMouseButtonDown(0))
|
||||
{
|
||||
if (IsPointerOverTarget(Input.mousePosition))
|
||||
{
|
||||
m_IsDragging = true;
|
||||
m_LastPointerPosition = Input.mousePosition;
|
||||
m_CurrentInertia = Vector3.zero; // Reset inertia when starting drag
|
||||
m_HasAngularMovement = false;
|
||||
|
||||
PauseAutoRotation();
|
||||
}
|
||||
}
|
||||
else if (Input.GetMouseButtonUp(0))
|
||||
{
|
||||
if (m_IsDragging)
|
||||
{
|
||||
m_IsDragging = false;
|
||||
// Only apply inertia if there was actual angular movement
|
||||
if (m_HasAngularMovement)
|
||||
ApplyInertia();
|
||||
else
|
||||
m_CurrentInertia = Vector3.zero; // No movement, no inertia
|
||||
|
||||
ResumeAutoRotation();
|
||||
}
|
||||
}
|
||||
else if (Input.GetMouseButton(0) && m_IsDragging)
|
||||
{
|
||||
Vector3 currentPosition = Input.mousePosition;
|
||||
m_PointerDelta = currentPosition - m_LastPointerPosition;
|
||||
m_LastPointerPosition = currentPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_IsDragging = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle touch input for slide rotation.
|
||||
/// </summary>
|
||||
private void HandleTouchInput()
|
||||
{
|
||||
int touchCount = Input.touchCount;
|
||||
if (touchCount <= 0)
|
||||
{
|
||||
m_IsTouchActive = false;
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < touchCount; i++)
|
||||
{
|
||||
Touch touch = Input.GetTouch(i);
|
||||
|
||||
switch (touch.phase)
|
||||
{
|
||||
case TouchPhase.Began:
|
||||
if (IsPointerOverTarget(touch.position))
|
||||
{
|
||||
m_MainTouch = touch;
|
||||
m_IsTouchActive = true;
|
||||
m_LastPointerPosition = touch.position;
|
||||
m_CurrentInertia = Vector3.zero; // Reset inertia when starting touch
|
||||
m_HasAngularMovement = false;
|
||||
PauseAutoRotation();
|
||||
}
|
||||
break;
|
||||
|
||||
case TouchPhase.Moved:
|
||||
if (m_IsTouchActive && touch.fingerId == m_MainTouch.fingerId)
|
||||
{
|
||||
Vector3 currentTouchPosition = touch.position;
|
||||
m_PointerDelta = currentTouchPosition - m_LastPointerPosition;
|
||||
m_LastPointerPosition = currentTouchPosition;
|
||||
}
|
||||
break;
|
||||
|
||||
case TouchPhase.Ended:
|
||||
case TouchPhase.Canceled:
|
||||
if (m_IsTouchActive && touch.fingerId == m_MainTouch.fingerId)
|
||||
{
|
||||
m_IsTouchActive = false;
|
||||
m_PointerDelta = Vector3.zero;
|
||||
// Only apply inertia if there was actual angular movement
|
||||
if (m_HasAngularMovement)
|
||||
ApplyInertia();
|
||||
else
|
||||
m_CurrentInertia = Vector3.zero; // No movement, no inertia
|
||||
|
||||
ResumeAutoRotation();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply inertia based on the last angular velocity.
|
||||
/// Inertia is directly proportional to the angular velocity at the moment of release.
|
||||
/// </summary>
|
||||
private void ApplyInertia()
|
||||
{
|
||||
// Only apply inertia if there was significant angular velocity
|
||||
// This ensures inertia magnitude correlates with rotation speed at release
|
||||
if (inertiaDamping > 0 && m_LastAngularVelocity.magnitude > INERTIA_THRESHOLD)
|
||||
m_CurrentInertia = m_LastAngularVelocity * inertiaDamping;
|
||||
else
|
||||
m_CurrentInertia = Vector3.zero;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Temporarily pause auto rotation during user interaction.
|
||||
/// </summary>
|
||||
private void PauseAutoRotation()
|
||||
{
|
||||
if (autoRotationEnabled)
|
||||
{
|
||||
m_WasAutoRotationEnabled = autoRotationEnabled;
|
||||
autoRotationEnabled = false;
|
||||
m_IsAutoRotationPaused = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resume auto rotation after user interaction ends.
|
||||
/// </summary>
|
||||
private void ResumeAutoRotation()
|
||||
{
|
||||
if (m_IsAutoRotationPaused)
|
||||
{
|
||||
autoRotationEnabled = m_WasAutoRotationEnabled;
|
||||
m_IsAutoRotationPaused = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the given screen point is over this object using renderer bounds.
|
||||
/// </summary>
|
||||
private bool IsPointerOverTarget(Vector3 screenPoint)
|
||||
{
|
||||
Camera camera = Camera.main;
|
||||
if (camera == null)
|
||||
return false;
|
||||
|
||||
Renderer[] renderers = m_Target.GetComponentsInChildren<Renderer>();
|
||||
if (renderers.Length == 0)
|
||||
return false;
|
||||
|
||||
Ray ray = camera.ScreenPointToRay(screenPoint);
|
||||
foreach (Renderer renderer in renderers)
|
||||
{
|
||||
if (renderer.enabled && renderer.bounds.IntersectRay(ray))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnDockGUI()
|
||||
{
|
||||
if (!displayInGameGUI)
|
||||
return;
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("Slide Rotation " + (slideRotationEnabled ? "O" : "X")))
|
||||
slideRotationEnabled = !slideRotationEnabled;
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("Auto Rotation " + (autoRotationEnabled ? "O" : "X")))
|
||||
autoRotationEnabled = !autoRotationEnabled;
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user