Initial commit
This commit is contained in:
+164
@@ -0,0 +1,164 @@
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Strategy 2 — axis-constrained translation via UI handle.
|
||||
/// Call BeginDrag() when the user grabs an axis arrow, passing the world-space
|
||||
/// axis direction and the hit point on that arrow.
|
||||
/// Call EndDrag() when the pointer is released.
|
||||
/// The ghost slides along the axis; rotation is frozen.
|
||||
/// Snapping still applies (position only).
|
||||
/// </summary>
|
||||
public class AxisMoveStrategy : IGhostMovementStrategy
|
||||
{
|
||||
private readonly Quaternion _lockedRotation;
|
||||
|
||||
// Drag state
|
||||
private bool _isDragging;
|
||||
private Vector3 _axisDirection; // world-space unit vector of the active axis
|
||||
private Vector3 _axisOrigin; // point on the axis at drag-begin (ghost position)
|
||||
private float _clampMin;
|
||||
private float _clampMax;
|
||||
private Camera _camera;
|
||||
private Vector3 _frozenPosition;
|
||||
private Vector2 _lastMousePos;
|
||||
private float _accumulated;
|
||||
|
||||
/// <param name="lockedRotation">Ghost rotation frozen for the entire interaction.</param>
|
||||
/// <param name="clampMin">Min distance from drag origin along the axis (negative = behind).</param>
|
||||
/// <param name="clampMax">Max distance from drag origin along the axis.</param>
|
||||
public AxisMoveStrategy(Quaternion lockedRotation, float clampMin = -1000f, float clampMax = 1000f)
|
||||
{
|
||||
_lockedRotation = lockedRotation;
|
||||
_clampMin = clampMin;
|
||||
_clampMax = clampMax;
|
||||
_camera = Camera.main;
|
||||
}
|
||||
|
||||
private float _dragOffsetT;
|
||||
|
||||
/// <summary>
|
||||
/// Call from your UI when the user begins dragging an axis arrow.
|
||||
/// </summary>
|
||||
/// <param name="axisDirection">World-space direction of the axis (e.g. ghost.transform.right).</param>
|
||||
/// <param name="ghostPosition">Ghost world position at the moment dragging begins.</param>
|
||||
public void BeginDrag(Vector3 axisDirection, Vector3 ghostPosition)
|
||||
{
|
||||
_camera = Camera.main; // Ensure camera reference is up-to-date
|
||||
_axisDirection = axisDirection.normalized;
|
||||
_axisOrigin = ghostPosition;
|
||||
_frozenPosition = ghostPosition;
|
||||
_accumulated = 0f;
|
||||
_isDragging = true;
|
||||
|
||||
if (_camera != null)
|
||||
{
|
||||
Ray mouseRay = _camera.ScreenPointToRay(Input.mousePosition);
|
||||
_dragOffsetT = CalculateT(mouseRay);
|
||||
}
|
||||
}
|
||||
|
||||
private float CalculateT(Ray mouseRay)
|
||||
{
|
||||
Vector3 w = mouseRay.origin - _axisOrigin;
|
||||
float b = Vector3.Dot(_axisDirection, mouseRay.direction);
|
||||
float d = Vector3.Dot(_axisDirection, w);
|
||||
float e = Vector3.Dot(mouseRay.direction, w);
|
||||
|
||||
float denominator = 1f - b * b;
|
||||
|
||||
if (Mathf.Abs(denominator) < 0.0001f)
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
|
||||
return (d - b * e) / denominator;
|
||||
}
|
||||
|
||||
/// <summary>Call from your UI when the pointer is released.</summary>
|
||||
public void EndDrag() => _isDragging = false;
|
||||
|
||||
public void UpdateMovement(ref GhostMovementContext ctx)
|
||||
{
|
||||
// Rotation always frozen
|
||||
ctx.GhostTransform.rotation = _lockedRotation;
|
||||
|
||||
if (!_isDragging)
|
||||
{
|
||||
ctx.RotationAxis = _axisDirection != Vector3.zero ? _axisDirection : Vector3.up;
|
||||
TrySocketSnap(ref ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_camera != null)
|
||||
{
|
||||
Ray mouseRay = _camera.ScreenPointToRay(Input.mousePosition);
|
||||
float currentT = CalculateT(mouseRay);
|
||||
float targetAccumulated = currentT - _dragOffsetT;
|
||||
_accumulated = Mathf.Clamp(targetAccumulated, _clampMin, _clampMax);
|
||||
}
|
||||
|
||||
// Only the axis component changes — all other axes stay frozen
|
||||
ctx.GhostTransform.position = _frozenPosition + _axisDirection * _accumulated;
|
||||
|
||||
ctx.RotationAxis = _axisDirection;
|
||||
|
||||
TrySocketSnap(ref ctx);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private bool TrySocketSnap(ref GhostMovementContext ctx)
|
||||
{
|
||||
SocketPoint[] mySockets = ctx.GhostTransform.GetComponentsInChildren<SocketPoint>();
|
||||
if (mySockets.Length == 0) return false;
|
||||
|
||||
SocketPoint bestMySocket = null;
|
||||
SocketPoint bestTargetSocket = null;
|
||||
float bestDist = 0.5f;
|
||||
|
||||
foreach (var mySocket in mySockets)
|
||||
{
|
||||
Collider[] hits = Physics.OverlapSphere(mySocket.transform.position, bestDist, Physics.AllLayers, QueryTriggerInteraction.Collide);
|
||||
foreach (var hit in hits)
|
||||
{
|
||||
if (hit.transform.root == ctx.GhostTransform.root) continue;
|
||||
|
||||
SocketPoint[] targetSockets = hit.transform.root.GetComponentsInChildren<SocketPoint>();
|
||||
foreach (var target in targetSockets)
|
||||
{
|
||||
if (target.IsOccupied()) continue;
|
||||
if (!target.CanAccept(mySocket)) continue;
|
||||
|
||||
float dist = Vector3.Distance(mySocket.transform.position, target.transform.position);
|
||||
if (dist < bestDist)
|
||||
{
|
||||
bestDist = dist;
|
||||
bestMySocket = mySocket;
|
||||
bestTargetSocket = target;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bestMySocket != null && bestTargetSocket != null)
|
||||
{
|
||||
Vector3 desiredPosition = bestTargetSocket.transform.position - (bestMySocket.transform.position - ctx.GhostTransform.position);
|
||||
|
||||
if (_isDragging && _axisDirection != Vector3.zero)
|
||||
{
|
||||
Vector3 toDesired = desiredPosition - _axisOrigin;
|
||||
float scalar = Vector3.Dot(toDesired, _axisDirection);
|
||||
scalar = Mathf.Clamp(scalar, _clampMin, _clampMax);
|
||||
|
||||
ctx.GhostTransform.position = _axisOrigin + _axisDirection * scalar;
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx.GhostTransform.position = desiredPosition;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user