Started make greybox for main location
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
public sealed class NetworkButtonInteractable : NetworkInteractable
|
||||
{
|
||||
[SerializeField] private UnityEvent<bool> buttonStateChanged;
|
||||
|
||||
private readonly NetworkVariable<bool> isPressed = new(
|
||||
false,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server);
|
||||
|
||||
public bool IsPressed => isPressed.Value;
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
isPressed.OnValueChanged += HandleStateChanged;
|
||||
ApplyState(isPressed.Value);
|
||||
}
|
||||
|
||||
public override void OnNetworkDespawn()
|
||||
{
|
||||
isPressed.OnValueChanged -= HandleStateChanged;
|
||||
}
|
||||
|
||||
protected override bool CanInteractOnServer(ulong senderClientId) => true;
|
||||
|
||||
protected override void InteractOnServer(ulong senderClientId, NetworkObject playerObject)
|
||||
{
|
||||
isPressed.Value = !isPressed.Value;
|
||||
ApplyState(isPressed.Value);
|
||||
}
|
||||
|
||||
private void HandleStateChanged(bool _, bool currentValue) => ApplyState(currentValue);
|
||||
|
||||
private void ApplyState(bool currentValue) => buttonStateChanged?.Invoke(currentValue);
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 43cd66292a303c3478352a33bc74db87
|
||||
@@ -0,0 +1,9 @@
|
||||
using UnityEngine;
|
||||
|
||||
public sealed class NetworkCarryableInteractable : NetworkHeldInteractable
|
||||
{
|
||||
[SerializeField] private Vector3 carryOffset = new(0f, 1.15f, 0.75f);
|
||||
|
||||
protected override Vector3 HoldingOffset => carryOffset;
|
||||
protected override string DropPrompt => "Put down";
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fad79bb3d620ffb46b527a9e361c28ae
|
||||
@@ -0,0 +1,109 @@
|
||||
using DG.Tweening;
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
[RequireComponent(typeof(NetworkObject))]
|
||||
public sealed class NetworkDoorController : NetworkBehaviour
|
||||
{
|
||||
[Header("Visual")]
|
||||
[SerializeField] private Transform doorVisual;
|
||||
[SerializeField] private Vector3 openLocalPositionOffset = new(0f, 2.5f, 0f);
|
||||
[SerializeField] private Vector3 openLocalRotationOffset;
|
||||
[SerializeField, Min(0.01f)] private float animationDuration = 0.65f;
|
||||
[SerializeField] private Ease animationEase = Ease.OutCubic;
|
||||
|
||||
private readonly NetworkVariable<bool> isOpen = new(
|
||||
false,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server);
|
||||
|
||||
private Vector3 closedLocalPosition;
|
||||
private Vector3 closedLocalEulerAngles;
|
||||
private Tween doorTween;
|
||||
|
||||
public bool IsOpen => isOpen.Value;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (doorVisual == null)
|
||||
doorVisual = transform;
|
||||
|
||||
closedLocalPosition = doorVisual.localPosition;
|
||||
closedLocalEulerAngles = doorVisual.localEulerAngles;
|
||||
}
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
isOpen.OnValueChanged += HandleOpenStateChanged;
|
||||
ApplyVisualState(isOpen.Value, true);
|
||||
}
|
||||
|
||||
public override void OnNetworkDespawn()
|
||||
{
|
||||
isOpen.OnValueChanged -= HandleOpenStateChanged;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
doorTween?.Kill();
|
||||
}
|
||||
|
||||
public void SetOpen(bool shouldOpen)
|
||||
{
|
||||
if (!IsServer)
|
||||
return;
|
||||
|
||||
isOpen.Value = shouldOpen;
|
||||
ApplyVisualState(shouldOpen, false);
|
||||
}
|
||||
|
||||
public void Toggle()
|
||||
{
|
||||
SetOpen(!isOpen.Value);
|
||||
}
|
||||
|
||||
[ContextMenu("Preview/Open")]
|
||||
private void PreviewOpen() => PreviewVisual(true);
|
||||
|
||||
[ContextMenu("Preview/Close")]
|
||||
private void PreviewClose() => PreviewVisual(false);
|
||||
|
||||
[ContextMenu("Preview/Toggle")]
|
||||
private void PreviewToggle() =>
|
||||
PreviewVisual(doorVisual != null && doorVisual.localPosition == closedLocalPosition);
|
||||
|
||||
private void PreviewVisual(bool shouldOpen)
|
||||
{
|
||||
ApplyVisualState(shouldOpen, !Application.isPlaying);
|
||||
}
|
||||
|
||||
private void HandleOpenStateChanged(bool _, bool currentValue) =>
|
||||
ApplyVisualState(currentValue, false);
|
||||
|
||||
private void ApplyVisualState(bool shouldOpen, bool immediately)
|
||||
{
|
||||
if (doorVisual == null)
|
||||
return;
|
||||
|
||||
doorTween?.Kill();
|
||||
|
||||
Vector3 targetPosition = closedLocalPosition +
|
||||
(shouldOpen ? openLocalPositionOffset : Vector3.zero);
|
||||
Vector3 targetRotation = closedLocalEulerAngles +
|
||||
(shouldOpen ? openLocalRotationOffset : Vector3.zero);
|
||||
|
||||
if (immediately)
|
||||
{
|
||||
doorVisual.localPosition = targetPosition;
|
||||
doorVisual.localEulerAngles = targetRotation;
|
||||
return;
|
||||
}
|
||||
|
||||
doorTween = DOTween.Sequence()
|
||||
.Join(doorVisual.DOLocalMove(targetPosition, animationDuration))
|
||||
.Join(doorVisual.DOLocalRotate(targetRotation, animationDuration))
|
||||
.SetEase(animationEase)
|
||||
.SetLink(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 402a356b266639c448f71cfce624ec90
|
||||
@@ -0,0 +1,9 @@
|
||||
using UnityEngine;
|
||||
|
||||
public sealed class NetworkDraggableInteractable : NetworkHeldInteractable
|
||||
{
|
||||
[SerializeField] private Vector3 dragOffset = new(0f, 0.35f, 1.8f);
|
||||
|
||||
protected override Vector3 HoldingOffset => dragOffset;
|
||||
protected override string DropPrompt => "Release";
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e5e737931b3bc22468ea0fe857062359
|
||||
@@ -0,0 +1,158 @@
|
||||
using System.Collections.Generic;
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
public abstract class NetworkHeldInteractable : NetworkInteractable
|
||||
{
|
||||
protected const ulong NoHolder = ulong.MaxValue;
|
||||
|
||||
[Header("Physics")]
|
||||
[SerializeField] private Rigidbody physicsBody;
|
||||
[SerializeField] private Collider[] objectColliders;
|
||||
[SerializeField, Min(0.05f)] private float collisionRefreshInterval = 0.25f;
|
||||
|
||||
private readonly NetworkVariable<ulong> holderClientId = new(
|
||||
NoHolder,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server);
|
||||
|
||||
private readonly HashSet<Collider> ignoredPlayerColliders = new();
|
||||
private float nextCollisionRefreshTime;
|
||||
|
||||
protected abstract Vector3 HoldingOffset { get; }
|
||||
protected abstract string DropPrompt { get; }
|
||||
|
||||
private bool IsHeld => holderClientId.Value != NoHolder;
|
||||
private bool IsHeldByLocalPlayer =>
|
||||
NetworkManager != null && holderClientId.Value == NetworkManager.LocalClientId;
|
||||
|
||||
public override string InteractionPrompt => !IsHeld
|
||||
? InteractionLabel
|
||||
: IsHeldByLocalPlayer
|
||||
? DropPrompt
|
||||
: "In use";
|
||||
|
||||
public override bool IsInteractionAvailable => base.IsInteractionAvailable &&
|
||||
(!IsHeld || IsHeldByLocalPlayer);
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (physicsBody == null)
|
||||
physicsBody = GetComponent<Rigidbody>();
|
||||
|
||||
if (objectColliders == null || objectColliders.Length == 0)
|
||||
objectColliders = GetComponentsInChildren<Collider>(true);
|
||||
}
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
holderClientId.OnValueChanged += HandleHolderChanged;
|
||||
ApplyHoldingState(IsHeld);
|
||||
}
|
||||
|
||||
public override void OnNetworkDespawn()
|
||||
{
|
||||
holderClientId.OnValueChanged -= HandleHolderChanged;
|
||||
ApplyHoldingState(false);
|
||||
}
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
if (!IsSpawned || !IsHeld)
|
||||
return;
|
||||
|
||||
if (!TryGetPlayerObject(holderClientId.Value, out NetworkObject holder))
|
||||
{
|
||||
if (IsServer)
|
||||
StopHolding();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
transform.SetPositionAndRotation(
|
||||
holder.transform.TransformPoint(HoldingOffset),
|
||||
holder.transform.rotation);
|
||||
|
||||
if (Time.time >= nextCollisionRefreshTime)
|
||||
{
|
||||
nextCollisionRefreshTime = Time.time + collisionRefreshInterval;
|
||||
IgnoreCollisionsWithPlayers();
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool CanInteractOnServer(ulong senderClientId) =>
|
||||
!IsHeld || holderClientId.Value == senderClientId;
|
||||
|
||||
protected override void InteractOnServer(ulong senderClientId, NetworkObject playerObject)
|
||||
{
|
||||
if (holderClientId.Value == senderClientId)
|
||||
StopHolding();
|
||||
else if (!IsHeld)
|
||||
StartHolding(senderClientId);
|
||||
}
|
||||
|
||||
private void StartHolding(ulong clientId)
|
||||
{
|
||||
holderClientId.Value = clientId;
|
||||
ApplyHoldingState(true);
|
||||
}
|
||||
|
||||
private void StopHolding()
|
||||
{
|
||||
holderClientId.Value = NoHolder;
|
||||
ApplyHoldingState(false);
|
||||
}
|
||||
|
||||
private void HandleHolderChanged(ulong _, ulong currentHolder) =>
|
||||
ApplyHoldingState(currentHolder != NoHolder);
|
||||
|
||||
private void ApplyHoldingState(bool isHeld)
|
||||
{
|
||||
if (physicsBody != null)
|
||||
{
|
||||
physicsBody.isKinematic = isHeld;
|
||||
if (!isHeld)
|
||||
physicsBody.linearVelocity = Vector3.zero;
|
||||
}
|
||||
|
||||
if (isHeld)
|
||||
IgnoreCollisionsWithPlayers();
|
||||
else
|
||||
RestorePlayerCollisions();
|
||||
}
|
||||
|
||||
private void IgnoreCollisionsWithPlayers()
|
||||
{
|
||||
foreach (CharacterController playerCollider in FindObjectsByType<CharacterController>(
|
||||
FindObjectsInactive.Exclude,
|
||||
FindObjectsSortMode.None))
|
||||
{
|
||||
foreach (Collider objectCollider in objectColliders)
|
||||
{
|
||||
if (objectCollider == null || playerCollider == null)
|
||||
continue;
|
||||
|
||||
Physics.IgnoreCollision(objectCollider, playerCollider, true);
|
||||
}
|
||||
|
||||
ignoredPlayerColliders.Add(playerCollider);
|
||||
}
|
||||
}
|
||||
|
||||
private void RestorePlayerCollisions()
|
||||
{
|
||||
foreach (Collider playerCollider in ignoredPlayerColliders)
|
||||
{
|
||||
if (playerCollider == null)
|
||||
continue;
|
||||
|
||||
foreach (Collider objectCollider in objectColliders)
|
||||
{
|
||||
if (objectCollider != null)
|
||||
Physics.IgnoreCollision(objectCollider, playerCollider, false);
|
||||
}
|
||||
}
|
||||
|
||||
ignoredPlayerColliders.Clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 12231d0b12920fd468d1117aa9a3c59c
|
||||
@@ -0,0 +1,61 @@
|
||||
using Unity.Netcode;
|
||||
using Unity.Netcode.Components;
|
||||
using UnityEngine;
|
||||
|
||||
public interface IInteractable
|
||||
{
|
||||
string InteractionPrompt { get; }
|
||||
bool IsInteractionAvailable { get; }
|
||||
void RequestInteraction();
|
||||
}
|
||||
|
||||
[AddComponentMenu("")]
|
||||
[RequireComponent(typeof(NetworkObject), typeof(NetworkTransform))]
|
||||
public class NetworkInteractable : NetworkBehaviour, IInteractable
|
||||
{
|
||||
[Header("Interaction")]
|
||||
[SerializeField] private string interactionLabel = "Interact";
|
||||
[SerializeField, Min(0.1f)] private float maximumInteractionDistance = 3f;
|
||||
|
||||
protected string InteractionLabel => interactionLabel;
|
||||
|
||||
public virtual string InteractionPrompt => interactionLabel;
|
||||
public virtual bool IsInteractionAvailable => IsSpawned;
|
||||
|
||||
public void RequestInteraction()
|
||||
{
|
||||
if (!IsSpawned || !IsInteractionAvailable)
|
||||
return;
|
||||
|
||||
RequestInteractionServerRpc();
|
||||
}
|
||||
|
||||
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
|
||||
private void RequestInteractionServerRpc(RpcParams rpcParams = default)
|
||||
{
|
||||
ulong senderClientId = rpcParams.Receive.SenderClientId;
|
||||
if (!TryGetPlayerObject(senderClientId, out NetworkObject playerObject))
|
||||
return;
|
||||
|
||||
float maximumDistanceSquared = maximumInteractionDistance * maximumInteractionDistance;
|
||||
if ((playerObject.transform.position - transform.position).sqrMagnitude > maximumDistanceSquared)
|
||||
return;
|
||||
|
||||
if (!CanInteractOnServer(senderClientId))
|
||||
return;
|
||||
|
||||
InteractOnServer(senderClientId, playerObject);
|
||||
}
|
||||
|
||||
protected virtual bool CanInteractOnServer(ulong senderClientId) => false;
|
||||
protected virtual void InteractOnServer(ulong senderClientId, NetworkObject playerObject) { }
|
||||
|
||||
protected bool TryGetPlayerObject(ulong clientId, out NetworkObject playerObject)
|
||||
{
|
||||
playerObject = NetworkManager != null
|
||||
? NetworkManager.SpawnManager.GetPlayerNetworkObject(clientId)
|
||||
: null;
|
||||
return playerObject != null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2aecb06ed06d9e145acd3163b0bdda1c
|
||||
Reference in New Issue
Block a user