Added UI to Furnance, fixed carry and drag obj and added fish
This commit is contained in:
@@ -2,6 +2,14 @@ using System.Collections.Generic;
|
||||
using Unity.Netcode;
|
||||
using Unity.Netcode.Components;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
public enum DraggableMovementMode
|
||||
{
|
||||
Free3D = 0,
|
||||
KeepWorldHeight = 1,
|
||||
FollowGround = 2
|
||||
}
|
||||
|
||||
[RequireComponent(typeof(Rigidbody), typeof(NetworkRigidbody))]
|
||||
public sealed class NetworkDraggableInteractable : NetworkInteractable
|
||||
@@ -18,7 +26,23 @@ public sealed class NetworkDraggableInteractable : NetworkInteractable
|
||||
[SerializeField, Min(0.1f)] private float maximumDragSpeed = 3f;
|
||||
[SerializeField, Range(0.05f, 1f)] private float minimumSpeedMultiplier = 0.35f;
|
||||
[SerializeField, Min(0.5f)] private float maximumDraggerDistance = 4.5f;
|
||||
[SerializeField] private bool keepCurrentHeight;
|
||||
[SerializeField, Min(0.01f)] private float positionSmoothTime = 0.12f;
|
||||
[SerializeField, Min(0.1f)] private float rotationFollowSpeed = 8f;
|
||||
[SerializeField] private Vector3 rotationOffset;
|
||||
[FormerlySerializedAs("keepCurrentHeight")]
|
||||
[SerializeField] private DraggableMovementMode movementMode;
|
||||
|
||||
[Header("Ground Following")]
|
||||
[SerializeField] private LayerMask groundLayers = ~0;
|
||||
[SerializeField] private Transform groundContactPoint;
|
||||
[SerializeField, Min(0.01f)] private float groundProbeRadius = 0.2f;
|
||||
[SerializeField, Min(0.1f)] private float groundProbeHeight = 1.25f;
|
||||
[SerializeField, Min(0.1f)] private float maximumGroundDrop = 2f;
|
||||
[SerializeField, Range(0f, 89f)] private float maximumGroundAngle = 55f;
|
||||
[SerializeField, Min(0f)] private float groundClearance = 0.02f;
|
||||
[SerializeField, Min(0.1f)] private float maximumGroundVerticalSpeed = 6f;
|
||||
[SerializeField] private bool alignToGround = true;
|
||||
[SerializeField, Min(0.1f)] private float groundAlignmentSpeed = 10f;
|
||||
|
||||
[Header("Physics")]
|
||||
[SerializeField] private Rigidbody physicsBody;
|
||||
@@ -36,7 +60,11 @@ public sealed class NetworkDraggableInteractable : NetworkInteractable
|
||||
NetworkVariableWritePermission.Server);
|
||||
|
||||
private readonly HashSet<Collider> ignoredPlayerColliders = new();
|
||||
private readonly RaycastHit[] groundHits = new RaycastHit[16];
|
||||
private float nextCollisionRefreshTime;
|
||||
private Vector3 localGroundContactPoint;
|
||||
private Vector3 smoothedGroundNormal = Vector3.up;
|
||||
private Vector3 dragVelocity;
|
||||
|
||||
public float Weight => weight;
|
||||
public int ActiveDraggerCount
|
||||
@@ -120,18 +148,33 @@ public sealed class NetworkDraggableInteractable : NetworkInteractable
|
||||
return;
|
||||
|
||||
RemoveInvalidDraggers();
|
||||
if (!HasEnoughPullStrength || !TryGetCombinedDragTarget(out Vector3 target))
|
||||
if (!HasEnoughPullStrength ||
|
||||
!TryGetCombinedDragTarget(out Vector3 target, out Vector3 forward))
|
||||
{
|
||||
dragVelocity = Vector3.zero;
|
||||
return;
|
||||
|
||||
if (keepCurrentHeight)
|
||||
target.y = physicsBody.position.y;
|
||||
}
|
||||
|
||||
float totalStrength = ActiveDraggerCount * pullStrengthPerPlayer;
|
||||
float loadRatio = Mathf.Clamp01(weight / totalStrength);
|
||||
float speedMultiplier = Mathf.Lerp(1f, minimumSpeedMultiplier, loadRatio);
|
||||
float movementStep = maximumDragSpeed * speedMultiplier * Time.fixedDeltaTime;
|
||||
Vector3 nextPosition = Vector3.MoveTowards(physicsBody.position, target, movementStep);
|
||||
physicsBody.MovePosition(nextPosition);
|
||||
float movementSpeed = maximumDragSpeed * speedMultiplier;
|
||||
|
||||
switch (movementMode)
|
||||
{
|
||||
case DraggableMovementMode.KeepWorldHeight:
|
||||
target.y = physicsBody.position.y;
|
||||
FollowDragTarget(target, forward, movementSpeed, Vector3.up);
|
||||
break;
|
||||
|
||||
case DraggableMovementMode.FollowGround:
|
||||
MoveAlongGround(target, forward, movementSpeed);
|
||||
break;
|
||||
|
||||
default:
|
||||
FollowDragTarget(target, forward, movementSpeed, Vector3.up);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool CanInteractOnServer(ulong senderClientId) =>
|
||||
@@ -188,27 +231,42 @@ public sealed class NetworkDraggableInteractable : NetworkInteractable
|
||||
return (playerObject.transform.position - physicsBody.position).sqrMagnitude <= maximumDistanceSquared;
|
||||
}
|
||||
|
||||
private bool TryGetCombinedDragTarget(out Vector3 target)
|
||||
private bool TryGetCombinedDragTarget(out Vector3 target, out Vector3 forward)
|
||||
{
|
||||
target = Vector3.zero;
|
||||
forward = Vector3.zero;
|
||||
int validTargets = 0;
|
||||
|
||||
AddDragTarget(firstDraggerClientId.Value, ref target, ref validTargets);
|
||||
AddDragTarget(secondDraggerClientId.Value, ref target, ref validTargets);
|
||||
AddDragTarget(firstDraggerClientId.Value, ref target, ref forward, ref validTargets);
|
||||
AddDragTarget(secondDraggerClientId.Value, ref target, ref forward, ref validTargets);
|
||||
|
||||
if (validTargets == 0)
|
||||
return false;
|
||||
|
||||
target /= validTargets;
|
||||
forward = Vector3.ProjectOnPlane(forward, Vector3.up);
|
||||
|
||||
if (forward.sqrMagnitude < 0.0001f)
|
||||
forward = Vector3.ProjectOnPlane(target - physicsBody.position, Vector3.up);
|
||||
|
||||
if (forward.sqrMagnitude < 0.0001f)
|
||||
forward = Vector3.ProjectOnPlane(physicsBody.transform.forward, Vector3.up);
|
||||
|
||||
forward.Normalize();
|
||||
return true;
|
||||
}
|
||||
|
||||
private void AddDragTarget(ulong clientId, ref Vector3 target, ref int validTargets)
|
||||
private void AddDragTarget(
|
||||
ulong clientId,
|
||||
ref Vector3 target,
|
||||
ref Vector3 forward,
|
||||
ref int validTargets)
|
||||
{
|
||||
if (clientId == NoDragger || !TryGetPlayerObject(clientId, out NetworkObject playerObject))
|
||||
return;
|
||||
|
||||
target += playerObject.transform.TransformPoint(dragOffset);
|
||||
forward += playerObject.transform.forward;
|
||||
validTargets++;
|
||||
}
|
||||
|
||||
@@ -221,15 +279,32 @@ public sealed class NetworkDraggableInteractable : NetworkInteractable
|
||||
{
|
||||
if (physicsBody != null)
|
||||
{
|
||||
physicsBody.isKinematic = HasAnyDragger || !IsServer;
|
||||
physicsBody.linearVelocity = Vector3.zero;
|
||||
physicsBody.angularVelocity = Vector3.zero;
|
||||
bool shouldBeKinematic = HasAnyDragger || !IsServer;
|
||||
|
||||
if (physicsBody.isKinematic && !shouldBeKinematic)
|
||||
physicsBody.isKinematic = false;
|
||||
|
||||
if (!physicsBody.isKinematic)
|
||||
{
|
||||
physicsBody.linearVelocity = Vector3.zero;
|
||||
physicsBody.angularVelocity = Vector3.zero;
|
||||
}
|
||||
|
||||
physicsBody.isKinematic = shouldBeKinematic;
|
||||
}
|
||||
|
||||
if (HasAnyDragger)
|
||||
{
|
||||
dragVelocity = Vector3.zero;
|
||||
CacheGroundContactPoint();
|
||||
smoothedGroundNormal = physicsBody != null ? physicsBody.transform.up : Vector3.up;
|
||||
IgnoreCollisionsWithPlayers();
|
||||
}
|
||||
else
|
||||
{
|
||||
dragVelocity = Vector3.zero;
|
||||
RestorePlayerCollisions();
|
||||
}
|
||||
}
|
||||
|
||||
private void ResolvePhysicsReferences()
|
||||
@@ -239,6 +314,8 @@ public sealed class NetworkDraggableInteractable : NetworkInteractable
|
||||
|
||||
if (objectColliders == null || objectColliders.Length == 0)
|
||||
objectColliders = GetComponentsInChildren<Collider>(true);
|
||||
|
||||
CacheGroundContactPoint();
|
||||
}
|
||||
|
||||
private void ApplyWeight()
|
||||
@@ -247,6 +324,178 @@ public sealed class NetworkDraggableInteractable : NetworkInteractable
|
||||
physicsBody.mass = Mathf.Max(0.1f, weight);
|
||||
}
|
||||
|
||||
private void FollowDragTarget(
|
||||
Vector3 target,
|
||||
Vector3 forward,
|
||||
float movementSpeed,
|
||||
Vector3 surfaceNormal)
|
||||
{
|
||||
Vector3 nextPosition = Vector3.SmoothDamp(
|
||||
physicsBody.position,
|
||||
target,
|
||||
ref dragVelocity,
|
||||
positionSmoothTime,
|
||||
movementSpeed,
|
||||
Time.fixedDeltaTime);
|
||||
|
||||
physicsBody.MovePosition(nextPosition);
|
||||
physicsBody.MoveRotation(CalculateDragRotation(forward, surfaceNormal));
|
||||
}
|
||||
|
||||
private void MoveAlongGround(
|
||||
Vector3 dragTarget,
|
||||
Vector3 forward,
|
||||
float movementSpeed)
|
||||
{
|
||||
Vector3 currentPosition = physicsBody.position;
|
||||
Vector3 planarTarget = new(dragTarget.x, currentPosition.y, dragTarget.z);
|
||||
Vector3 planarVelocity = new(dragVelocity.x, 0f, dragVelocity.z);
|
||||
Vector3 nextPlanarPosition = Vector3.SmoothDamp(
|
||||
currentPosition,
|
||||
planarTarget,
|
||||
ref planarVelocity,
|
||||
positionSmoothTime,
|
||||
movementSpeed,
|
||||
Time.fixedDeltaTime);
|
||||
nextPlanarPosition.y = currentPosition.y;
|
||||
dragVelocity = new Vector3(planarVelocity.x, dragVelocity.y, planarVelocity.z);
|
||||
|
||||
Vector3 probePosition = nextPlanarPosition;
|
||||
|
||||
if (!TryGetGroundSurface(probePosition, out Vector3 groundPoint, out Vector3 groundNormal))
|
||||
return;
|
||||
|
||||
Quaternion targetRotation = CalculateDragRotation(forward, groundNormal);
|
||||
Vector3 scaledContactPoint = Vector3.Scale(localGroundContactPoint, physicsBody.transform.lossyScale);
|
||||
float contactOffsetY = (targetRotation * scaledContactPoint).y;
|
||||
float targetHeight = groundPoint.y - contactOffsetY + groundClearance;
|
||||
float nextHeight = Mathf.MoveTowards(
|
||||
currentPosition.y,
|
||||
targetHeight,
|
||||
maximumGroundVerticalSpeed * Time.fixedDeltaTime);
|
||||
|
||||
physicsBody.MovePosition(new Vector3(nextPlanarPosition.x, nextHeight, nextPlanarPosition.z));
|
||||
|
||||
physicsBody.MoveRotation(targetRotation);
|
||||
}
|
||||
|
||||
private bool TryGetGroundSurface(
|
||||
Vector3 probePosition,
|
||||
out Vector3 groundPoint,
|
||||
out Vector3 groundNormal)
|
||||
{
|
||||
Vector3 origin = probePosition + Vector3.up * groundProbeHeight;
|
||||
float castDistance = groundProbeHeight + maximumGroundDrop;
|
||||
int hitCount = Physics.SphereCastNonAlloc(
|
||||
origin,
|
||||
groundProbeRadius,
|
||||
Vector3.down,
|
||||
groundHits,
|
||||
castDistance,
|
||||
groundLayers,
|
||||
QueryTriggerInteraction.Ignore);
|
||||
|
||||
float nearestDistance = float.PositiveInfinity;
|
||||
groundPoint = default;
|
||||
groundNormal = Vector3.up;
|
||||
|
||||
for (int index = 0; index < hitCount; index++)
|
||||
{
|
||||
RaycastHit hit = groundHits[index];
|
||||
if (hit.collider == null || IsOwnCollider(hit.collider) ||
|
||||
hit.collider.GetComponentInParent<CharacterController>() != null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Vector3.Angle(Vector3.up, hit.normal) > maximumGroundAngle ||
|
||||
hit.distance >= nearestDistance)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
nearestDistance = hit.distance;
|
||||
groundPoint = hit.point;
|
||||
groundNormal = hit.normal;
|
||||
}
|
||||
|
||||
return !float.IsPositiveInfinity(nearestDistance);
|
||||
}
|
||||
|
||||
private Quaternion CalculateDragRotation(Vector3 forward, Vector3 groundNormal)
|
||||
{
|
||||
Vector3 targetUp = alignToGround ? groundNormal : Vector3.up;
|
||||
float normalBlend = 1f - Mathf.Exp(-groundAlignmentSpeed * Time.fixedDeltaTime);
|
||||
smoothedGroundNormal = Vector3.Slerp(
|
||||
smoothedGroundNormal,
|
||||
targetUp,
|
||||
normalBlend).normalized;
|
||||
|
||||
Vector3 surfaceForward = Vector3.ProjectOnPlane(forward, smoothedGroundNormal);
|
||||
if (surfaceForward.sqrMagnitude < 0.0001f)
|
||||
surfaceForward = Vector3.ProjectOnPlane(physicsBody.transform.forward, smoothedGroundNormal);
|
||||
|
||||
Quaternion desiredRotation = Quaternion.LookRotation(
|
||||
surfaceForward.normalized,
|
||||
smoothedGroundNormal) * Quaternion.Euler(rotationOffset);
|
||||
float rotationBlend = 1f - Mathf.Exp(-rotationFollowSpeed * Time.fixedDeltaTime);
|
||||
return Quaternion.Slerp(physicsBody.rotation, desiredRotation, rotationBlend);
|
||||
}
|
||||
|
||||
private void CacheGroundContactPoint()
|
||||
{
|
||||
if (physicsBody == null)
|
||||
return;
|
||||
|
||||
if (groundContactPoint != null)
|
||||
{
|
||||
localGroundContactPoint = physicsBody.transform.InverseTransformPoint(groundContactPoint.position);
|
||||
return;
|
||||
}
|
||||
|
||||
bool hasBounds = false;
|
||||
Bounds combinedBounds = default;
|
||||
|
||||
if (objectColliders != null)
|
||||
{
|
||||
foreach (Collider objectCollider in objectColliders)
|
||||
{
|
||||
if (objectCollider == null || objectCollider.isTrigger || !objectCollider.enabled)
|
||||
continue;
|
||||
|
||||
if (!hasBounds)
|
||||
{
|
||||
combinedBounds = objectCollider.bounds;
|
||||
hasBounds = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
combinedBounds.Encapsulate(objectCollider.bounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 contactWorldPosition = hasBounds
|
||||
? new Vector3(combinedBounds.center.x, combinedBounds.min.y, combinedBounds.center.z)
|
||||
: physicsBody.position;
|
||||
|
||||
localGroundContactPoint = physicsBody.transform.InverseTransformPoint(contactWorldPosition);
|
||||
}
|
||||
|
||||
private bool IsOwnCollider(Collider candidate)
|
||||
{
|
||||
if (objectColliders == null)
|
||||
return false;
|
||||
|
||||
foreach (Collider objectCollider in objectColliders)
|
||||
{
|
||||
if (objectCollider == candidate)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void IgnoreCollisionsWithPlayers()
|
||||
{
|
||||
foreach (CharacterController playerCollider in FindObjectsByType<CharacterController>(
|
||||
|
||||
Reference in New Issue
Block a user