Refactored trajectory component
This commit is contained in:
@@ -6,6 +6,21 @@ using UnityEngine;
|
||||
|
||||
namespace Common.Input
|
||||
{
|
||||
/// <summary>
|
||||
/// Cached launch parameters calculated during drag.
|
||||
/// Avoids recalculating force/direction multiple times.
|
||||
/// </summary>
|
||||
public struct LaunchParameters
|
||||
{
|
||||
public Vector2 Direction;
|
||||
public float Force;
|
||||
public float DragDistance;
|
||||
public float DragRatio;
|
||||
public float Mass;
|
||||
|
||||
public bool IsValid => Force > 0f && DragDistance > 0f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base class for drag-to-launch mechanics (Angry Birds style).
|
||||
/// Provides core drag logic, force calculation, and input handling.
|
||||
@@ -81,6 +96,9 @@ namespace Common.Input
|
||||
[Tooltip("Launch anchor point (spawn/slingshot position)")]
|
||||
[SerializeField] protected Transform launchAnchor;
|
||||
|
||||
[Tooltip("Trajectory preview component (auto-found if not assigned)")]
|
||||
[SerializeField] protected Common.Visual.TrajectoryPreview trajectoryPreview;
|
||||
|
||||
[Header("Debug")]
|
||||
[SerializeField] protected bool showDebugLogs;
|
||||
|
||||
@@ -98,7 +116,10 @@ namespace Common.Input
|
||||
private bool _isDragging;
|
||||
private Vector2 _dragStartPosition;
|
||||
private bool _isEnabled = false;
|
||||
private bool _isRegistered = false;
|
||||
private bool _isRegisteredForInput = false;
|
||||
|
||||
// Cached launch parameters - calculated once during drag, used for both preview and launch
|
||||
private LaunchParameters _cachedLaunchParams;
|
||||
|
||||
public bool IsDragging => _isDragging;
|
||||
public bool IsEnabled => _isEnabled;
|
||||
@@ -135,10 +156,10 @@ namespace Common.Input
|
||||
_isEnabled = true;
|
||||
|
||||
// Register with InputManager as override consumer
|
||||
if (InputManager.Instance != null && !_isRegistered)
|
||||
if (InputManager.Instance != null && !_isRegisteredForInput)
|
||||
{
|
||||
InputManager.Instance.RegisterOverrideConsumer(this);
|
||||
_isRegistered = true;
|
||||
_isRegisteredForInput = true;
|
||||
if (showDebugLogs) Logging.Debug($"[{GetType().Name}] Registered with InputManager");
|
||||
}
|
||||
|
||||
@@ -157,10 +178,10 @@ namespace Common.Input
|
||||
_isDragging = false;
|
||||
|
||||
// Unregister from InputManager
|
||||
if (InputManager.Instance != null && _isRegistered)
|
||||
if (InputManager.Instance != null && _isRegisteredForInput)
|
||||
{
|
||||
InputManager.Instance.UnregisterOverrideConsumer(this);
|
||||
_isRegistered = false;
|
||||
_isRegisteredForInput = false;
|
||||
if (showDebugLogs) Logging.Debug($"[{GetType().Name}] Unregistered from InputManager");
|
||||
}
|
||||
|
||||
@@ -220,29 +241,19 @@ namespace Common.Input
|
||||
/// </summary>
|
||||
protected virtual void UpdateDrag(Vector2 currentWorldPosition)
|
||||
{
|
||||
// Calculate drag vector from anchor to current drag position
|
||||
// Pull back (away from anchor) = launch forward (toward anchor direction)
|
||||
Vector2 dragVector = _dragStartPosition - currentWorldPosition;
|
||||
|
||||
// Calculate force and direction
|
||||
float dragDistance = dragVector.magnitude;
|
||||
float dragRatio = Mathf.Clamp01(dragDistance / MaxDragDistance);
|
||||
|
||||
// Use config to calculate force with multipliers
|
||||
float force = Config?.CalculateForce(dragDistance, dragRatio) ?? (dragRatio * MaxForce);
|
||||
Vector2 direction = dragVector.normalized;
|
||||
float mass = GetProjectileMass();
|
||||
// Calculate launch parameters once and cache
|
||||
_cachedLaunchParams = CalculateLaunchParameters(currentWorldPosition);
|
||||
|
||||
// Warn if mass is zero or invalid
|
||||
if (mass <= 0f && showDebugLogs)
|
||||
if (_cachedLaunchParams.Mass <= 0f && showDebugLogs)
|
||||
{
|
||||
Logging.Warning($"[{GetType().Name}] Projectile mass is {mass}! Trajectory calculation will be inaccurate. Override GetProjectileMass().");
|
||||
Logging.Warning($"[{GetType().Name}] Projectile mass is {_cachedLaunchParams.Mass}! Trajectory calculation will be inaccurate. Override GetProjectileMass().");
|
||||
}
|
||||
|
||||
// Update visuals with mass parameter
|
||||
UpdateVisuals(currentWorldPosition, direction, force, dragDistance, mass);
|
||||
// Update visuals with cached parameters
|
||||
UpdateVisuals(currentWorldPosition, _cachedLaunchParams);
|
||||
|
||||
OnDragUpdate?.Invoke(currentWorldPosition, direction, force);
|
||||
OnDragUpdate?.Invoke(currentWorldPosition, _cachedLaunchParams.Direction, _cachedLaunchParams.Force);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -255,34 +266,48 @@ namespace Common.Input
|
||||
// Hide preview
|
||||
HidePreview();
|
||||
|
||||
// Calculate final launch parameters
|
||||
// Recalculate final parameters (position may have changed since last UpdateDrag)
|
||||
_cachedLaunchParams = CalculateLaunchParameters(currentWorldPosition);
|
||||
|
||||
OnDragEnd?.Invoke(currentWorldPosition, _cachedLaunchParams.Direction, _cachedLaunchParams.Force);
|
||||
|
||||
if (showDebugLogs)
|
||||
Logging.Debug($"[{GetType().Name}] Launching with force {_cachedLaunchParams.Force:F2}");
|
||||
PerformLaunch(_cachedLaunchParams.Direction, _cachedLaunchParams.Force);
|
||||
OnLaunch?.Invoke(_cachedLaunchParams.Direction, _cachedLaunchParams.Force);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate launch parameters from current drag position.
|
||||
/// Caches results to avoid recalculating force multiple times.
|
||||
/// </summary>
|
||||
private LaunchParameters CalculateLaunchParameters(Vector2 currentWorldPosition)
|
||||
{
|
||||
// Calculate drag vector from anchor to current drag position
|
||||
// Pull back (away from anchor) = launch forward (toward anchor direction)
|
||||
Vector2 dragVector = _dragStartPosition - currentWorldPosition;
|
||||
|
||||
// Calculate distance and ratio
|
||||
float dragDistance = dragVector.magnitude;
|
||||
float dragRatio = Mathf.Clamp01(dragDistance / MaxDragDistance);
|
||||
|
||||
// Use config to calculate force with multipliers
|
||||
// Calculate force using config
|
||||
float force = Config?.CalculateForce(dragDistance, dragRatio) ?? (dragRatio * MaxForce);
|
||||
Vector2 direction = dragVector.normalized;
|
||||
|
||||
// Get minimum force from config
|
||||
float minForce = Config?.GetMinForce() ?? (MaxForce * 0.1f);
|
||||
// Normalize direction
|
||||
Vector2 direction = dragDistance > 0.01f ? dragVector.normalized : Vector2.zero;
|
||||
|
||||
if (showDebugLogs)
|
||||
Logging.Debug($"[{GetType().Name}] Drag ended - Force: {force:F2}, Min: {minForce:F2}, Distance: {dragDistance:F2}");
|
||||
// Get mass from projectile
|
||||
float mass = GetProjectileMass();
|
||||
|
||||
OnDragEnd?.Invoke(currentWorldPosition, direction, force);
|
||||
|
||||
// Launch if force exceeds minimum
|
||||
if (force >= minForce)
|
||||
return new LaunchParameters
|
||||
{
|
||||
if (showDebugLogs) Logging.Debug($"[{GetType().Name}] Launching with force {force:F2}");
|
||||
PerformLaunch(direction, force);
|
||||
OnLaunch?.Invoke(direction, force);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (showDebugLogs) Logging.Debug($"[{GetType().Name}] Drag too short - force {force:F2} < min {minForce:F2}");
|
||||
}
|
||||
Direction = direction,
|
||||
Force = force,
|
||||
DragDistance = dragDistance,
|
||||
DragRatio = dragRatio,
|
||||
Mass = mass
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -303,15 +328,23 @@ namespace Common.Input
|
||||
/// Default: Updates trajectory preview using prefab's physics properties.
|
||||
/// Override for custom visuals.
|
||||
/// </summary>
|
||||
protected virtual void UpdateVisuals(Vector2 currentPosition, Vector2 direction, float force, float dragDistance, float mass)
|
||||
/// <param name="currentPosition">Current drag position</param>
|
||||
/// <param name="launchParams">Cached launch parameters (direction, force, etc.)</param>
|
||||
protected virtual void UpdateVisuals(Vector2 currentPosition, LaunchParameters launchParams)
|
||||
{
|
||||
if (trajectoryPreview != null && dragDistance > 0.1f)
|
||||
if (trajectoryPreview != null && launchParams.DragDistance > 0.1f)
|
||||
{
|
||||
GameObject prefab = GetProjectilePrefab();
|
||||
if (prefab != null)
|
||||
{
|
||||
trajectoryPreview.UpdateTrajectory(launchAnchor.position, direction, force, prefab);
|
||||
}
|
||||
if (prefab == null) return;
|
||||
|
||||
// Get gravity from prefab's Rigidbody2D gravityScale
|
||||
var rb = prefab.GetComponent<Rigidbody2D>();
|
||||
float gravityScale = rb != null ? rb.gravityScale : 1f;
|
||||
float gravity = Physics2D.gravity.magnitude * gravityScale;
|
||||
|
||||
// Use mass from settings (already in launchParams)
|
||||
trajectoryPreview.UpdateTrajectory(launchAnchor.position, launchParams.Direction,
|
||||
launchParams.Force, launchParams.Mass, gravity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,52 +370,14 @@ namespace Common.Input
|
||||
|
||||
#endregion
|
||||
|
||||
#region Virtual Methods - Optional Override
|
||||
#region Abstract Methods - Physics Configuration
|
||||
|
||||
/// <summary>
|
||||
/// Get projectile mass for trajectory calculation.
|
||||
/// Reads from the prefab's Rigidbody2D component.
|
||||
/// Subclasses can override for custom behavior (e.g., if mass changes dynamically).
|
||||
/// MUST read from settings - the same source that Initialize() uses.
|
||||
/// Subclasses implement to return the actual runtime mass.
|
||||
/// </summary>
|
||||
protected virtual float GetProjectileMass()
|
||||
{
|
||||
GameObject prefab = GetProjectilePrefab();
|
||||
if (prefab == null)
|
||||
{
|
||||
if (showDebugLogs)
|
||||
Logging.Warning($"[{GetType().Name}] GetProjectilePrefab() returned null!");
|
||||
return 0f;
|
||||
}
|
||||
|
||||
var rb = prefab.GetComponent<Rigidbody2D>();
|
||||
if (rb == null)
|
||||
{
|
||||
if (showDebugLogs)
|
||||
Logging.Warning($"[{GetType().Name}] Projectile prefab '{prefab.name}' has no Rigidbody2D!");
|
||||
return 0f;
|
||||
}
|
||||
|
||||
return rb.mass;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get gravity value for trajectory calculation.
|
||||
/// Uses Physics2D.gravity.magnitude * prefab's Rigidbody2D gravityScale.
|
||||
/// </summary>
|
||||
protected virtual float GetGravity()
|
||||
{
|
||||
GameObject prefab = GetProjectilePrefab();
|
||||
if (prefab == null)
|
||||
{
|
||||
// Fallback to project gravity
|
||||
return Physics2D.gravity.magnitude;
|
||||
}
|
||||
|
||||
var rb = prefab.GetComponent<Rigidbody2D>();
|
||||
float gravityScale = rb != null ? rb.gravityScale : 1f;
|
||||
|
||||
return Physics2D.gravity.magnitude * gravityScale;
|
||||
}
|
||||
protected abstract float GetProjectileMass();
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -393,7 +388,7 @@ namespace Common.Input
|
||||
base.OnManagedDestroy();
|
||||
|
||||
// Ensure we unregister from InputManager
|
||||
if (_isRegistered && InputManager.Instance != null)
|
||||
if (_isRegisteredForInput && InputManager.Instance != null)
|
||||
{
|
||||
InputManager.Instance.UnregisterOverrideConsumer(this);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user