Skip to content

Commit

Permalink
feat(Cast): add cursor lock and transition duration to points cast
Browse files Browse the repository at this point in the history
The PointsCast now has a cursor lock function and a transition duration
which allows the cursor to be locked to a location until the pointer
moves a certain threshold distance away when the cursor location will
then be updated, this can be used to prevent jittering or keep a solid
lock on an object. The transition duration allows the pointer
destination cursor to transition over time to the new destination
providing a smoothing feature for the pointer cursor.

The FixedLineCast has now been deprecated as the StraightLineCast now
handles the concept of being a fixed line as it's basically the same
thing and makes it more complex to implement functionality that is
shared between the two concepts.

The StraightLineCast now has the ability to fix the line at a set
length and optionally ignore any new targets once fixed.

Along with the cursor lock and transition duration, it's also possible
to apply a drag effect to the straight line where the straight line
actually bends to try and keep the straight line from the origin
to the offset destination.
  • Loading branch information
thestonefox committed May 1, 2023
1 parent 24d5c1a commit 057201c
Show file tree
Hide file tree
Showing 7 changed files with 846 additions and 42 deletions.
16 changes: 16 additions & 0 deletions Runtime/Cast/FixedLineCast.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
namespace Zinnia.Cast
{
using System;
using UnityEngine;
using Zinnia.Extension;

/// <summary>
/// Casts a straight line in the direction of the origin for a fixed length.
/// </summary>
[Obsolete("Use `StraightLineCast.ShouldFixLength` instead.")]
public class FixedLineCast : StraightLineCast
{
[Tooltip("The current length of the cast.")]
Expand Down Expand Up @@ -43,6 +45,20 @@ public virtual void SetCurrentLength(EventData data)
}
}

/// <summary>
/// Increments the current length of the cast by the given value.
/// </summary>
/// <param name="value">The value to increment the length by.</param>
public virtual void IncrementCurrentLength(float value)
{
if (!this.IsValidState())
{
return;
}

CurrentLength += value;
}

/// <inheritdoc />
protected override void GeneratePoints()
{
Expand Down
36 changes: 10 additions & 26 deletions Runtime/Cast/ParabolicLineCast.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,21 +124,6 @@ public virtual void SetMaximumLengthY(float value)
MaximumLength = new Vector2(MaximumLength.x, value);
}

protected override void OnEnable()
{
base.OnEnable();
curvePoints.Add(default);
curvePoints.Add(default);
curvePoints.Add(default);
curvePoints.Add(default);
}

protected override void OnDisable()
{
base.OnDisable();
curvePoints.Clear();
}

/// <inheritdoc />
protected override void DoCastPoints()
{
Expand All @@ -153,7 +138,8 @@ protected override void DoCastPoints()
/// <returns>The collision point or the point being the furthest away on the cast line if nothing is hit.</returns>
protected virtual Vector3 ProjectForward()
{
float rotation = Vector3.Dot(Vector3.up, Origin.transform.forward.normalized);
Vector3 actualForward = GetTrackedForward(Origin.transform.forward);
float rotation = Vector3.Dot(Vector3.up, actualForward.normalized);
float length = MaximumLength.x;

if ((rotation * 100f) > HeightLimitAngle)
Expand All @@ -162,7 +148,7 @@ protected virtual Vector3 ProjectForward()
length = MaximumLength.x * controllerRotationOffset * controllerRotationOffset;
}

Ray ray = new Ray(Origin.transform.position, Origin.transform.forward);
Ray ray = new Ray(Origin.transform.position, actualForward);
bool hasCollided = PhysicsCast.Raycast(PhysicsCast, ray, out RaycastHit hitData, length, Physics.IgnoreRaycastLayer);

// Adjust the cast length if something is blocking it.
Expand Down Expand Up @@ -195,8 +181,8 @@ protected virtual Vector3 ProjectDown(Vector3 downwardOrigin)

if (downRayHit)
{
point = ray.GetPoint(hitData.distance);
TargetHit = hitData;
RaycastHit actualHitData = GetActualTargetHit(hitData, downRayHit);
point = actualHitData.point;
}

return point;
Expand Down Expand Up @@ -260,13 +246,11 @@ protected virtual void GeneratePoints(Vector3 forward, Vector3 down)
forward = DestinationPointOverride != null ? (Vector3)DestinationPointOverride : forward;
down = DestinationPointOverride != null ? (Vector3)DestinationPointOverride : down;

if (curvePoints.Count >= 4)
{
curvePoints[0] = Origin.transform.position;
curvePoints[1] = forward + (Vector3.up * CurveOffset);
curvePoints[2] = down;
curvePoints[3] = down;
}
curvePoints.Clear();
curvePoints.Add(Origin.transform.position);
curvePoints.Add(forward + (Vector3.up * CurveOffset));
curvePoints.Add(down);
curvePoints.Add(down);

points.Clear();
foreach (Vector3 generatedPoint in BezierCurveGenerator.GeneratePoints(SegmentCount, curvePoints))
Expand Down
78 changes: 78 additions & 0 deletions Runtime/Cast/PointsCast.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ public void Clear()
[Serializable]
public class UnityEvent : UnityEvent<EventData> { }

[Header("Cast Settings")]
[Tooltip("The origin point for the cast.")]
[SerializeField]
private GameObject origin;
Expand Down Expand Up @@ -179,6 +180,40 @@ public RuleContainer TargetPointValidity
targetPointValidity = value;
}
}
[Tooltip("The amount of distance the cursor has to move before the destination of the cursor is updated to a new position.")]
[SerializeField]
private float cursorLockThreshold;
/// <summary>
/// The amount of distance the cursor has to move before the destination of the cursor is updated to a new position.
/// </summary>
public float CursorLockThreshold
{
get
{
return cursorLockThreshold;
}
set
{
cursorLockThreshold = value;
}
}
[Tooltip("The duration it takes to transition previous destination point to the current actual target point.")]
[SerializeField]
private float transitionDuration;
/// <summary>
/// The duration it takes to transition previous destination point to the current actual target point.
/// </summary>
public float TransitionDuration
{
get
{
return transitionDuration;
}
set
{
transitionDuration = value;
}
}

/// <summary>
/// An override for the destination location point in world space.
Expand All @@ -188,6 +223,7 @@ public RuleContainer TargetPointValidity
/// <summary>
/// Emitted whenever the cast result changes.
/// </summary>
[Header("Cast Events")]
public UnityEvent ResultsChanged = new UnityEvent();

/// <summary>
Expand Down Expand Up @@ -226,6 +262,14 @@ protected set
/// The data to emit with an event.
/// </summary>
protected readonly EventData eventData = new EventData();
/// <summary>
/// The origin forward that is being tracked by the drag delay.
/// </summary>
protected Vector3? trackedOriginForward = null;
/// <summary>
/// The reference to the output velocity.
/// </summary>
protected Vector3 outVelocity = Vector3.zero;

/// <summary>
/// Clears <see cref="Origin"/>.
Expand Down Expand Up @@ -313,16 +357,50 @@ public virtual void Process()

protected virtual void OnEnable()
{
points.Clear();
OnAfterTargetHitChange();
}

protected virtual void OnDisable()
{
TargetHit = null;
trackedOriginForward = null;
IsTargetHitValid = false;
points.Clear();
ClearDestinationPointOverride();
}

/// <summary>
///
/// </summary>
/// <param name="orignForward"></param>
protected virtual Vector3 GetTrackedForward(Vector3 orignForward)
{
trackedOriginForward = TransitionDuration.ApproxEquals(0f) || trackedOriginForward == null ? orignForward : Vector3.SmoothDamp((Vector3)trackedOriginForward, orignForward, ref outVelocity, TransitionDuration, Mathf.Infinity, Time.deltaTime);
return (Vector3)trackedOriginForward;
}

/// <summary>
///
/// </summary>
/// <param name="actualHitData"></param>
/// <param name="hasCollided"></param>
/// <returns></returns>
protected virtual RaycastHit GetActualTargetHit(RaycastHit actualHitData, bool hasCollided)
{
RaycastHit newTargetHit = actualHitData;
if (CursorLockThreshold > 0f && TargetHit != null)
{
RaycastHit existingTargetHit = (RaycastHit)TargetHit;
if (Vector3.Distance(actualHitData.point, existingTargetHit.point) <= CursorLockThreshold)
{
newTargetHit = existingTargetHit;
}
}
TargetHit = hasCollided ? newTargetHit : (RaycastHit?)null;
return newTargetHit;
}

/// <summary>
/// Called after <see cref="TargetHit"/> has been changed.
/// </summary>
Expand Down
Loading

0 comments on commit 057201c

Please sign in to comment.