Unity Udexreal开发插件包
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

819 lines
33 KiB

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;
namespace UDE_HAND_INTERACTION
{
public class FingerPressInteractor : InteractDetector<FingerPressInteractor, FingerPressInteractable>
{
private class SurfaceHitCache
{
private readonly struct HitInfo
{
public readonly bool IsValid;
public readonly SurfaceHit Hit;
public HitInfo(bool isValid, SurfaceHit hit)
{
IsValid = isValid;
Hit = hit;
}
}
private Dictionary<FingerPressInteractable, HitInfo> _surfacePatchHitCache;
private Dictionary<FingerPressInteractable, HitInfo> _backingSurfaceHitCache;
private Vector3 _origin;
public bool GetPatchHit(FingerPressInteractable interactable, out SurfaceHit hit)
{
if (!_surfacePatchHitCache.ContainsKey(interactable))
{
bool isValid = interactable.SurfacePatch
.ClosestSurfacePoint(_origin, out SurfaceHit patchHit);
HitInfo info = new HitInfo(isValid, patchHit);
_surfacePatchHitCache.Add(interactable, info);
}
hit = _surfacePatchHitCache[interactable].Hit;
return _surfacePatchHitCache[interactable].IsValid;
}
public bool GetBackingHit(FingerPressInteractable interactable, out SurfaceHit hit)
{
if (!_backingSurfaceHitCache.ContainsKey(interactable))
{
bool isValid = interactable.SurfacePatch.BackingSurface
.ClosestSurfacePoint(_origin, out SurfaceHit backingHit);
HitInfo info = new HitInfo(isValid, backingHit);
_backingSurfaceHitCache.Add(interactable, info);
}
hit = _backingSurfaceHitCache[interactable].Hit;
return _backingSurfaceHitCache[interactable].IsValid;
}
public SurfaceHitCache()
{
_surfacePatchHitCache = new Dictionary<FingerPressInteractable, HitInfo>();
_backingSurfaceHitCache = new Dictionary<FingerPressInteractable, HitInfo>();
}
public void Reset(Vector3 origin)
{
_origin = origin;
_surfacePatchHitCache.Clear();
_backingSurfaceHitCache.Clear();
}
}
[SerializeField]
private Transform TipTransform;
//public Vector3 _localPositionOffset;
[SerializeField]
private float _radius = 0.005f;
private float _touchReleaseThreshold = 0.002f;
[FormerlySerializedAs("_zThreshold")]
private float _equalDistanceThreshold = 0.001f;
public Vector3 ClosestPoint { get; private set; }
public Vector3 TouchPoint { get; private set; }
public Vector3 TouchNormal { get; private set; }
public float Radius => _radius;
public Vector3 Origin { get; private set; }
private Vector3 _previousFingerPressOrigin;
private FingerPressInteractable _previousCandidate = null;
private FingerPressInteractable _hitInteractable = null;
private FingerPressInteractable _recoilInteractable = null;
private Vector3 _previousSurfacePointLocal;
private Vector3 _firstTouchPointLocal;
private Vector3 _targetTouchPointLocal;
private Vector3 _easeTouchPointLocal;
private bool _isRecoiled;
private bool _isDragging;
private ProgressCurve _dragEaseCurve;
private ProgressCurve _pinningResyncCurve;
private Vector3 _dragCompareSurfacePointLocal;
private float _maxDistanceFromFirstTouchPoint;
private float _recoilVelocityExpansion;
private float _selectMaxDepth;
private float _reEnterDepth;
private float _lastUpdateTime;
private Func<float> _timeProvider;
private bool _isPassedSurface;
public bool IsPassedSurface
{
get
{
return _isPassedSurface;
}
set
{
bool previousValue = _isPassedSurface;
_isPassedSurface = value;
if (value != previousValue)
{
WhenPassedSurfaceChanged.Invoke(value);
}
}
}
public Action<bool> WhenPassedSurfaceChanged = delegate { };
private SurfaceHitCache _hitCache;
private Dictionary<FingerPressInteractable, Matrix4x4> _previousSurfaceTransformMap;
private float _previousDragCurveProgress;
private float _previousPinningCurveProgress;
protected override void Awake()
{
base.Awake();
_timeProvider = () => Time.time;
}
protected override void Start()
{
base.Start();
this.AssertField(TipTransform, nameof(TipTransform));
this.AssertField(_timeProvider, nameof(_timeProvider));
_dragEaseCurve = new ProgressCurve();
_pinningResyncCurve = new ProgressCurve();
_hitCache = new SurfaceHitCache();
_previousSurfaceTransformMap = new Dictionary<FingerPressInteractable, Matrix4x4>();
}
protected override void DoPreprocess()
{
base.DoPreprocess();
_previousFingerPressOrigin = Origin;
Origin = TipTransform.position;
_hitCache.Reset(Origin);
}
protected override void DoPostprocess()
{
base.DoPostprocess();
var interactables = FingerPressInteractable.Registry.List(this);
foreach (FingerPressInteractable interactable in interactables)
{
_previousSurfaceTransformMap[interactable] = interactable.SurfacePatch.BackingSurface.Transform.worldToLocalMatrix;
}
_lastUpdateTime = _timeProvider();
}
protected override bool ComputeShouldSelect()
{
if (_recoilInteractable != null)
{
float depth = ComputeFingerPressDepth(_recoilInteractable, Origin);
_reEnterDepth = Mathf.Min(depth + _recoilInteractable.RecoilAssist.ReEnterDistance, _reEnterDepth);
_hitInteractable = depth > _reEnterDepth ? _recoilInteractable : null;
}
return _hitInteractable != null;
}
protected override bool ComputeShouldUnselect()
{
return _hitInteractable == null;
}
private bool GetBackingHit(FingerPressInteractable interactable, out SurfaceHit hit)
{
return _hitCache.GetBackingHit(interactable, out hit);
}
private bool GetPatchHit(FingerPressInteractable interactable, out SurfaceHit hit)
{
return _hitCache.GetPatchHit(interactable, out hit);
}
private bool InteractableInRange(FingerPressInteractable interactable)
{
if (!_previousSurfaceTransformMap.ContainsKey(interactable))
{
return true;
}
Vector3 previousLocalFingerPressOrigin = _previousSurfaceTransformMap[interactable].MultiplyPoint(_previousFingerPressOrigin);
Vector3 adjustedFingerPressOrigin = interactable.SurfacePatch.BackingSurface.Transform.TransformPoint(previousLocalFingerPressOrigin);
float hoverDistance = interactable == Interactable ? Mathf.Max(interactable.ExitHoverTangent, interactable.ExitHoverNormal) : Mathf.Max(interactable.EnterHoverTangent, interactable.EnterHoverNormal);
float moveDistance = Vector3.Distance(Origin, adjustedFingerPressOrigin);
float maxDistance = moveDistance + Radius + hoverDistance + _equalDistanceThreshold + interactable.CloseDistanceThreshold;
return interactable.SurfacePatch.ClosestSurfacePoint(Origin, out _, maxDistance);
}
protected override void DoHoverUpdate()
{
if (_interactable != null && GetBackingHit(_interactable, out SurfaceHit backingHit))
{
TouchPoint = backingHit.Point;
TouchNormal = backingHit.Normal;
}
if (_recoilInteractable != null)
{
bool withinSurface = SurfaceUpdate(_recoilInteractable);
if (!withinSurface)
{
_isRecoiled = false;
_recoilInteractable = null;
_recoilVelocityExpansion = 0;
IsPassedSurface = false;
return;
}
if (ShouldCancel(_recoilInteractable))
{
GenerateDetectorEvent(DetectorEventType.Cancel, _recoilInteractable);
_previousFingerPressOrigin = Origin;
_previousCandidate = null;
_hitInteractable = null;
_recoilInteractable = null;
_recoilVelocityExpansion = 0;
IsPassedSurface = false;
_isRecoiled = false;
}
}
}
protected override FingerPressInteractable ComputeCandidate()
{
if (_recoilInteractable != null)
{
return _recoilInteractable;
}
if (_hitInteractable != null)
{
return _hitInteractable;
}
FingerPressInteractable closestInteractable = ComputeSelectCandidate();
if (closestInteractable != null)
{
_hitInteractable = closestInteractable;
_previousCandidate = closestInteractable;
return _hitInteractable;
}
closestInteractable = ComputeHoverCandidate();
_previousCandidate = closestInteractable;
return closestInteractable;
}
private FingerPressInteractable ComputeSelectCandidate()
{
FingerPressInteractable closestInteractable = null;
float closestNormalDistance = float.MaxValue;
float closestTangentDistance = float.MaxValue;
var interactables = FingerPressInteractable.Registry.List(this);
foreach (FingerPressInteractable interactable in interactables)
{
if (!InteractableInRange(interactable) || !GetBackingHit(interactable, out SurfaceHit backingHit) || !GetPatchHit(interactable, out SurfaceHit patchHit))
{
continue;
}
Matrix4x4 previousSurfaceMatrix = _previousSurfaceTransformMap.ContainsKey(interactable) ? _previousSurfaceTransformMap[interactable] : interactable.SurfacePatch.BackingSurface.Transform.worldToLocalMatrix;
Vector3 localFingerPressOrigin = previousSurfaceMatrix.MultiplyPoint(_previousFingerPressOrigin);
Vector3 adjustedFingerPressOrigin = interactable.SurfacePatch.BackingSurface.Transform.TransformPoint(localFingerPressOrigin);
if (!PassesEnterHoverDistanceCheck(adjustedFingerPressOrigin, interactable))
{
continue;
}
Vector3 moveDirection = Origin - adjustedFingerPressOrigin;
float magnitude = moveDirection.magnitude;
if (magnitude == 0f)
{
continue;
}
moveDirection /= magnitude;
Ray ray = new(adjustedFingerPressOrigin, moveDirection);
Vector3 closestSurfaceNormal = backingHit.Normal;
if (Vector3.Dot(moveDirection, closestSurfaceNormal) < 0f)
{
bool hit = interactable.SurfacePatch.BackingSurface.Raycast(ray, out SurfaceHit surfaceHit);
hit = hit && surfaceHit.Distance <= magnitude;
if (!hit)
{
float distance = ComputeDistanceAbove(interactable, Origin);
if (distance <= 0)
{
Vector3 closestSurfacePointToOrigin = backingHit.Point;
hit = true;
surfaceHit = new SurfaceHit()
{
Point = closestSurfacePointToOrigin,
Normal = backingHit.Normal,
Distance = distance
};
}
}
if (hit)
{
float tangentDistance = ComputeTangentDistance(interactable, surfaceHit.Point);
if (tangentDistance > (interactable != _previousCandidate ? interactable.EnterHoverTangent : interactable.ExitHoverTangent))
{
continue;
}
float normalDistance = Vector3.Dot(adjustedFingerPressOrigin - surfaceHit.Point, surfaceHit.Normal);
bool normalDistanceEqual = Mathf.Abs(normalDistance - closestNormalDistance) < _equalDistanceThreshold;
if (normalDistance > closestNormalDistance + interactable.CloseDistanceThreshold)
{
continue;
}
if (closestInteractable == null || normalDistance < closestNormalDistance - closestInteractable.CloseDistanceThreshold)
{
closestNormalDistance = normalDistance;
closestTangentDistance = tangentDistance;
closestInteractable = interactable;
continue;
}
if (tangentDistance < closestTangentDistance)
{
closestNormalDistance = normalDistance;
closestTangentDistance = tangentDistance;
closestInteractable = interactable;
}
}
}
}
if (closestInteractable != null)
{
GetBackingHit(closestInteractable, out SurfaceHit backingHitClosest);
GetPatchHit(closestInteractable, out SurfaceHit patchHitClosest);
ClosestPoint = patchHitClosest.Point;
TouchPoint = backingHitClosest.Point;
TouchNormal = backingHitClosest.Normal;
foreach (FingerPressInteractable interactable in interactables)
{
if (interactable == closestInteractable)
{
continue;
}
if (!InteractableInRange(interactable) || !GetBackingHit(interactable, out SurfaceHit backingHit) || !GetPatchHit(interactable, out SurfaceHit patchHit))
{
continue;
}
Matrix4x4 previousSurfaceMatrix = _previousSurfaceTransformMap.ContainsKey(interactable) ? _previousSurfaceTransformMap[interactable] : interactable.SurfacePatch.BackingSurface.Transform.worldToLocalMatrix;
Vector3 localFingerPressOrigin = previousSurfaceMatrix.MultiplyPoint(_previousFingerPressOrigin);
Vector3 adjustedFingerPressOrigin = interactable.SurfacePatch.BackingSurface.Transform.TransformPoint(localFingerPressOrigin);
if (!PassesEnterHoverDistanceCheck(adjustedFingerPressOrigin, interactable))
{
continue;
}
Vector3 backingToTouchPoint = TouchPoint - backingHit.Point;
float normalDistance = Vector3.Dot(backingToTouchPoint, backingHit.Normal);
if (normalDistance <= 0 || normalDistance > interactable.CloseDistanceThreshold)
{
continue;
}
float tangentDistance = ComputeTangentDistance(interactable, TouchPoint);
if (tangentDistance > interactable.EnterHoverTangent || tangentDistance > closestTangentDistance)
{
continue;
}
return null;
}
}
return closestInteractable;
}
private bool PassesEnterHoverDistanceCheck(Vector3 position, FingerPressInteractable interactable)
{
if (interactable == _previousCandidate)
{
return true;
}
float distanceThreshold = 0f;
if (interactable.MinThresholds.Enabled)
{
distanceThreshold = Mathf.Min(interactable.MinThresholds.MinNormal, MinFingerPressDepth(interactable));
}
return ComputeDistanceAbove(interactable, position) > distanceThreshold;
}
public float MinFingerPressDepth(FingerPressInteractable interactable)
{
float minDepth = interactable.ExitHoverNormal;
foreach (FingerPressInteractor fingerPressInteractor in interactable.Interactors)
{
float normalDistance = ComputeFingerPressDepth(interactable, fingerPressInteractor.Origin);
minDepth = Mathf.Min(normalDistance, minDepth);
}
return minDepth;
}
private FingerPressInteractable ComputeHoverCandidate()
{
FingerPressInteractable closestInteractable = null;
float closestNormalDistance = float.MaxValue;
float closestTangentDistance = float.MaxValue;
var interactables = FingerPressInteractable.Registry.List(this);
foreach (FingerPressInteractable interactable in interactables)
{
if (!InteractableInRange(interactable) || !GetBackingHit(interactable, out SurfaceHit backingHit) || !GetPatchHit(interactable, out SurfaceHit patchHit))
{
continue;
}
if (!PassesEnterHoverDistanceCheck(Origin, interactable) && !PassesEnterHoverDistanceCheck(_previousFingerPressOrigin, interactable))
{
continue;
}
Vector3 closestSurfacePoint = backingHit.Point;
Vector3 closestSurfaceNormal = backingHit.Normal;
Vector3 surfaceToPoint = Origin - closestSurfacePoint;
float magnitude = surfaceToPoint.magnitude;
if (magnitude != 0f)
{
if (Vector3.Dot(surfaceToPoint, closestSurfaceNormal) > 0f)
{
float normalDistance = ComputeDistanceAbove(interactable, Origin);
if (normalDistance > (_previousCandidate != interactable ? interactable.EnterHoverNormal : interactable.ExitHoverNormal))
{
continue;
}
float tangentDistance = ComputeTangentDistance(interactable, Origin);
if (tangentDistance > (_previousCandidate != interactable ? interactable.EnterHoverTangent : interactable.ExitHoverTangent))
{
continue;
}
bool normalDistanceEqual = Mathf.Abs(normalDistance - closestNormalDistance) < _equalDistanceThreshold;
if (normalDistance > closestNormalDistance + interactable.CloseDistanceThreshold)
{
continue;
}
if (closestInteractable == null || normalDistance < closestNormalDistance - closestInteractable.CloseDistanceThreshold)
{
closestInteractable = interactable;
closestNormalDistance = normalDistance;
closestTangentDistance = tangentDistance;
continue;
}
if (tangentDistance < closestTangentDistance)
{
closestInteractable = interactable;
closestNormalDistance = normalDistance;
closestTangentDistance = tangentDistance;
}
}
}
}
if (closestInteractable != null)
{
GetBackingHit(closestInteractable, out SurfaceHit backingHitClosest);
GetPatchHit(closestInteractable, out SurfaceHit patchHitClosest);
ClosestPoint = patchHitClosest.Point;
TouchPoint = backingHitClosest.Point;
TouchNormal = backingHitClosest.Normal;
}
return closestInteractable;
}
protected override void InteractableSelected(FingerPressInteractable interactable)
{
if (interactable != null && GetBackingHit(interactable, out SurfaceHit backingHit))
{
_previousSurfacePointLocal =
_firstTouchPointLocal =
_easeTouchPointLocal =
_targetTouchPointLocal =
interactable.SurfacePatch.BackingSurface.Transform.InverseTransformPoint(TouchPoint);
Vector3 lateralComparePoint = backingHit.Point;
_dragCompareSurfacePointLocal = interactable.SurfacePatch.BackingSurface.Transform.InverseTransformPoint(lateralComparePoint);
_dragEaseCurve.Copy(interactable.DragThresholds.DragEaseCurve);
_pinningResyncCurve.Copy(interactable.PositionPinning.ResyncCurve);
_isDragging = false;
_isRecoiled = false;
_maxDistanceFromFirstTouchPoint = 0;
_selectMaxDepth = 0;
}
IsPassedSurface = true;
base.InteractableSelected(interactable);
}
protected override void HandleDisabled()
{
_hitInteractable = null;
base.HandleDisabled();
}
protected override Pose ComputeDetectorPose()
{
if (Interactable == null)
{
return Pose.identity;
}
if (!Interactable.ClosestBackingSurfaceHit(TouchPoint, out SurfaceHit hit))
{
return Pose.identity;
}
return new Pose(TouchPoint, Quaternion.LookRotation(hit.Normal));
}
private float ComputeDistanceAbove(FingerPressInteractable interactable, Vector3 point)
{
return SurfaceUtils.ComputeDistanceAbove(interactable.SurfacePatch, point, _radius);
}
private float ComputeFingerPressDepth(FingerPressInteractable interactable, Vector3 point)
{
return SurfaceUtils.ComputeDepth(interactable.SurfacePatch, point, _radius);
}
private float ComputeTangentDistance(FingerPressInteractable interactable, Vector3 point)
{
return SurfaceUtils.ComputeTangentDistance(interactable.SurfacePatch, point, _radius);
}
protected virtual bool SurfaceUpdate(FingerPressInteractable interactable)
{
if (interactable == null)
{
return false;
}
if (!GetBackingHit(interactable, out SurfaceHit backingHit))
{
return false;
}
if (ComputeDistanceAbove(interactable, Origin) > _touchReleaseThreshold)
{
return false;
}
bool wasRecoiled = _isRecoiled;
_isRecoiled = _hitInteractable == null && _recoilInteractable != null;
Vector3 closestSurfacePoint = backingHit.Point;
Vector3 positionOnSurfaceLocal = interactable.SurfacePatch.BackingSurface.Transform.InverseTransformPoint(closestSurfacePoint);
if (interactable.DragThresholds.Enabled)
{
float worldDepthDelta = Mathf.Abs(ComputeFingerPressDepth(interactable, Origin) - ComputeFingerPressDepth(interactable, _previousFingerPressOrigin));
Vector3 positionDeltaLocal = positionOnSurfaceLocal - _previousSurfacePointLocal;
Vector3 positionDeltaWorld = interactable.SurfacePatch.BackingSurface.Transform.TransformVector(positionDeltaLocal);
bool isZMotion = worldDepthDelta > positionDeltaWorld.magnitude && worldDepthDelta > interactable.DragThresholds.DragNormal;
if (isZMotion)
{
_dragCompareSurfacePointLocal = positionOnSurfaceLocal;
}
if (!_isDragging)
{
if (!isZMotion)
{
Vector3 surfaceDeltaLocal = positionOnSurfaceLocal - _dragCompareSurfacePointLocal;
Vector3 surfaceDeltaWorld = interactable.SurfacePatch.BackingSurface.Transform.TransformVector(surfaceDeltaLocal);
if (surfaceDeltaWorld.magnitude > interactable.DragThresholds.DragTangent)
{
_isDragging = true;
_dragEaseCurve.Start();
_previousDragCurveProgress = 0;
_targetTouchPointLocal = positionOnSurfaceLocal;
}
}
}
else
{
if (isZMotion)
{
_isDragging = false;
}
else
{
_targetTouchPointLocal = positionOnSurfaceLocal;
}
}
}
else
{
_targetTouchPointLocal = positionOnSurfaceLocal;
}
Vector3 pinnedTouchPointLocal = _targetTouchPointLocal;
if (interactable.PositionPinning.Enabled)
{
if (!_isRecoiled)
{
Vector3 deltaFromCaptureLocal = pinnedTouchPointLocal - _firstTouchPointLocal;
Vector3 deltaFromCaptureWorld = interactable.SurfacePatch.BackingSurface.Transform.TransformVector(deltaFromCaptureLocal);
_maxDistanceFromFirstTouchPoint = Mathf.Max(deltaFromCaptureWorld.magnitude, _maxDistanceFromFirstTouchPoint);
float deltaAsPercent = 1;
if (interactable.PositionPinning.MaxPinDistance != 0f)
{
deltaAsPercent = Mathf.Clamp01(_maxDistanceFromFirstTouchPoint / interactable.PositionPinning.MaxPinDistance);
deltaAsPercent = interactable.PositionPinning.PinningEaseCurve.Evaluate(deltaAsPercent);
}
pinnedTouchPointLocal = _firstTouchPointLocal + deltaFromCaptureLocal * deltaAsPercent;
}
else
{
if (!wasRecoiled)
{
_pinningResyncCurve.Start();
_previousPinningCurveProgress = 0;
}
float pinningCurveProgress = _pinningResyncCurve.Progress();
if (pinningCurveProgress != 1f)
{
float deltaProgress = pinningCurveProgress - _previousPinningCurveProgress;
Vector3 delta = pinnedTouchPointLocal - _easeTouchPointLocal;
pinnedTouchPointLocal = _easeTouchPointLocal + deltaProgress / (1f - _previousPinningCurveProgress) * delta;
_previousPinningCurveProgress = pinningCurveProgress;
}
}
}
float dragCurveProgress = _dragEaseCurve.Progress();
if (dragCurveProgress != 1f)
{
float deltaProgress = dragCurveProgress - _previousDragCurveProgress;
Vector3 delta = pinnedTouchPointLocal - _easeTouchPointLocal;
_easeTouchPointLocal += deltaProgress / (1f - _previousDragCurveProgress) * delta;
_previousDragCurveProgress = dragCurveProgress;
}
else
{
_easeTouchPointLocal = pinnedTouchPointLocal;
}
TouchPoint = interactable.SurfacePatch.BackingSurface.Transform.TransformPoint(_easeTouchPointLocal);
interactable.ClosestBackingSurfaceHit(TouchPoint, out SurfaceHit hit);
TouchNormal = hit.Normal;
_previousSurfacePointLocal = positionOnSurfaceLocal;
return true;
}
protected virtual bool ShouldCancel(FingerPressInteractable interactable)
{
return (interactable.CancelSelectNormal > 0.0f && ComputeFingerPressDepth(interactable, Origin) > interactable.CancelSelectNormal) ||
(interactable.CancelSelectTangent > 0.0f && ComputeTangentDistance(interactable, Origin) > interactable.CancelSelectTangent);
}
protected virtual bool ShouldRecoil(FingerPressInteractable interactable)
{
if (!interactable.RecoilAssist.Enabled)
{
return false;
}
float depth = ComputeFingerPressDepth(interactable, Origin);
float deltaTime = _timeProvider() - _lastUpdateTime;
float recoilExitDistance = interactable.RecoilAssist.ExitDistance;
if (interactable.RecoilAssist.UseVelocityExpansion)
{
Vector3 frameDeltaWorld = Origin - _previousFingerPressOrigin;
float normalVelocity = Mathf.Max(0, Vector3.Dot(frameDeltaWorld, -TouchNormal));
normalVelocity = deltaTime > 0 ? normalVelocity / deltaTime : 0f;
float adjustment = Mathf.InverseLerp( interactable.RecoilAssist.VelocityExpansionMinSpeed, interactable.RecoilAssist.VelocityExpansionMaxSpeed, normalVelocity);
float targetRecoilVelocityExpansion = Mathf.Clamp01(adjustment) * interactable.RecoilAssist.VelocityExpansionDistance;
if (targetRecoilVelocityExpansion > _recoilVelocityExpansion)
{
_recoilVelocityExpansion = targetRecoilVelocityExpansion;
}
else
{
float decayRate = interactable.RecoilAssist.VelocityExpansionDecayRate * deltaTime;
_recoilVelocityExpansion = Math.Max(targetRecoilVelocityExpansion, _recoilVelocityExpansion - decayRate);
}
recoilExitDistance += _recoilVelocityExpansion;
}
if (depth > _selectMaxDepth)
{
_selectMaxDepth = depth;
}
else
{
if (interactable.RecoilAssist.UseDynamicDecay)
{
Vector3 frameDeltaWorld = Origin - _previousFingerPressOrigin;
Vector3 normalDeltaWorld = Vector3.Project(frameDeltaWorld, TouchNormal);
float normalRatio = frameDeltaWorld.sqrMagnitude > 0.0000001f ? normalDeltaWorld.magnitude / frameDeltaWorld.magnitude : 1f;
float decayFactor = interactable.RecoilAssist.DynamicDecayCurve.Evaluate(normalRatio);
_selectMaxDepth = Mathf.Lerp(_selectMaxDepth, depth, decayFactor * deltaTime);
}
if (depth < _selectMaxDepth - recoilExitDistance)
{
_reEnterDepth = depth + interactable.RecoilAssist.ReEnterDistance;
return true;
}
}
return false;
}
protected override void DoSelectUpdate()
{
bool withinSurface = SurfaceUpdate(_selectedInteractable);
if (!withinSurface)
{
_hitInteractable = null;
IsPassedSurface = _recoilInteractable != null;
return;
}
if (ShouldCancel(_selectedInteractable))
{
GenerateDetectorEvent(DetectorEventType.Cancel, _selectedInteractable);
_previousFingerPressOrigin = Origin;
_previousCandidate = null;
_hitInteractable = null;
_recoilInteractable = null;
_recoilVelocityExpansion = 0;
IsPassedSurface = false;
_isRecoiled = false;
return;
}
if (ShouldRecoil(_selectedInteractable))
{
_hitInteractable = null;
_recoilInteractable = _selectedInteractable;
_selectMaxDepth = 0;
}
}
}
}