Added many other
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aafd902db1441e245b27edca6032a2fc
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,183 @@
|
||||
using System.Collections.Generic;
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
public interface IUsableHandItem
|
||||
{
|
||||
string ItemName { get; }
|
||||
bool IsHeld { get; }
|
||||
bool IsHeldByLocalPlayer { get; }
|
||||
void RequestUse();
|
||||
void RequestThrow();
|
||||
}
|
||||
|
||||
public abstract class NetworkHandItemInteractable : NetworkInteractable, IUsableHandItem
|
||||
{
|
||||
private const ulong NoHolder = ulong.MaxValue;
|
||||
private static readonly HashSet<NetworkHandItemInteractable> SpawnedItems = new();
|
||||
|
||||
[Header("Hand item physics")]
|
||||
[SerializeField] private Rigidbody physicsBody;
|
||||
[SerializeField] private Collider[] itemColliders;
|
||||
[SerializeField, Min(0f)] private float throwForce = 4f;
|
||||
[SerializeField, Min(0f)] private float throwUpwardForce = 1f;
|
||||
|
||||
private readonly NetworkVariable<ulong> holderClientId = new(
|
||||
NoHolder,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server);
|
||||
|
||||
public string ItemName => InteractionLabel;
|
||||
public bool IsHeld => holderClientId.Value != NoHolder;
|
||||
public bool IsHeldByLocalPlayer =>
|
||||
NetworkManager != null && holderClientId.Value == NetworkManager.LocalClientId;
|
||||
|
||||
public override bool IsInteractionAvailable => base.IsInteractionAvailable && !IsHeld;
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
if (physicsBody == null)
|
||||
physicsBody = GetComponent<Rigidbody>();
|
||||
|
||||
if (itemColliders == null || itemColliders.Length == 0)
|
||||
itemColliders = GetComponentsInChildren<Collider>(true);
|
||||
}
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
SpawnedItems.Add(this);
|
||||
holderClientId.OnValueChanged += HandleHolderChanged;
|
||||
ApplyHeldState(IsHeld);
|
||||
}
|
||||
|
||||
public override void OnNetworkDespawn()
|
||||
{
|
||||
holderClientId.OnValueChanged -= HandleHolderChanged;
|
||||
SpawnedItems.Remove(this);
|
||||
ApplyHeldState(false);
|
||||
}
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
if (!IsSpawned || !IsHeld)
|
||||
return;
|
||||
|
||||
if (!TryGetPlayerObject(holderClientId.Value, out NetworkObject holder))
|
||||
{
|
||||
if (IsServer)
|
||||
DropWithoutThrow();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
PlayerHandController hand = holder.GetComponent<PlayerHandController>();
|
||||
if (hand == null || hand.ItemSocket == null)
|
||||
return;
|
||||
|
||||
transform.SetPositionAndRotation(
|
||||
hand.ItemSocket.position,
|
||||
hand.ItemSocket.rotation);
|
||||
}
|
||||
|
||||
public void RequestUse()
|
||||
{
|
||||
if (IsSpawned && IsHeldByLocalPlayer)
|
||||
RequestUseServerRpc();
|
||||
}
|
||||
|
||||
public void RequestThrow()
|
||||
{
|
||||
if (IsSpawned && IsHeldByLocalPlayer)
|
||||
RequestThrowServerRpc();
|
||||
}
|
||||
|
||||
protected override bool CanInteractOnServer(ulong senderClientId) =>
|
||||
!IsHeld && !TryGetHeldItem(senderClientId, out _);
|
||||
|
||||
protected override void InteractOnServer(ulong senderClientId, NetworkObject playerObject)
|
||||
{
|
||||
holderClientId.Value = senderClientId;
|
||||
ApplyHeldState(true);
|
||||
}
|
||||
|
||||
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
|
||||
private void RequestUseServerRpc(RpcParams rpcParams = default)
|
||||
{
|
||||
ulong senderClientId = rpcParams.Receive.SenderClientId;
|
||||
if (holderClientId.Value != senderClientId ||
|
||||
!TryGetPlayerObject(senderClientId, out NetworkObject playerObject))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UseOnServer(playerObject);
|
||||
}
|
||||
|
||||
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
|
||||
private void RequestThrowServerRpc(RpcParams rpcParams = default)
|
||||
{
|
||||
ulong senderClientId = rpcParams.Receive.SenderClientId;
|
||||
if (holderClientId.Value != senderClientId ||
|
||||
!TryGetPlayerObject(senderClientId, out NetworkObject playerObject))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 throwVelocity = playerObject.transform.forward * throwForce +
|
||||
Vector3.up * throwUpwardForce;
|
||||
|
||||
holderClientId.Value = NoHolder;
|
||||
ApplyHeldState(false);
|
||||
|
||||
if (physicsBody != null)
|
||||
physicsBody.linearVelocity = throwVelocity;
|
||||
}
|
||||
|
||||
protected abstract void UseOnServer(NetworkObject playerObject);
|
||||
|
||||
public static bool TryGetHeldItem(
|
||||
ulong clientId,
|
||||
out NetworkHandItemInteractable heldItem)
|
||||
{
|
||||
foreach (NetworkHandItemInteractable item in SpawnedItems)
|
||||
{
|
||||
if (item != null && item.holderClientId.Value == clientId)
|
||||
{
|
||||
heldItem = item;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
heldItem = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private void DropWithoutThrow()
|
||||
{
|
||||
holderClientId.Value = NoHolder;
|
||||
ApplyHeldState(false);
|
||||
}
|
||||
|
||||
private void HandleHolderChanged(ulong _, ulong currentHolder) =>
|
||||
ApplyHeldState(currentHolder != NoHolder);
|
||||
|
||||
private void ApplyHeldState(bool held)
|
||||
{
|
||||
if (physicsBody != null)
|
||||
{
|
||||
physicsBody.isKinematic = held || !IsServer;
|
||||
|
||||
if (held)
|
||||
{
|
||||
physicsBody.linearVelocity = Vector3.zero;
|
||||
physicsBody.angularVelocity = Vector3.zero;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Collider itemCollider in itemColliders)
|
||||
{
|
||||
if (itemCollider != null)
|
||||
itemCollider.enabled = !held;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f64a3a9624397b24c882498533a78ae7
|
||||
@@ -0,0 +1,82 @@
|
||||
using DG.Tweening;
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
public sealed class NetworkTestCubeItem : NetworkHandItemInteractable
|
||||
{
|
||||
[Header("Use feedback")]
|
||||
[SerializeField] private Transform visual;
|
||||
[SerializeField, Min(0.01f)] private float punchDuration = 0.25f;
|
||||
[SerializeField, Min(0f)] private float punchStrength = 0.2f;
|
||||
|
||||
private readonly NetworkVariable<uint> useSequence = new(
|
||||
0,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server);
|
||||
|
||||
private Vector3 restingVisualScale;
|
||||
private Tween useTween;
|
||||
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
if (visual == null)
|
||||
{
|
||||
Renderer itemRenderer = GetComponentInChildren<Renderer>(true);
|
||||
visual = itemRenderer != null ? itemRenderer.transform : transform;
|
||||
}
|
||||
|
||||
restingVisualScale = visual.localScale;
|
||||
}
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
base.OnNetworkSpawn();
|
||||
useSequence.OnValueChanged += HandleUseSequenceChanged;
|
||||
}
|
||||
|
||||
public override void OnNetworkDespawn()
|
||||
{
|
||||
useSequence.OnValueChanged -= HandleUseSequenceChanged;
|
||||
useTween?.Kill();
|
||||
base.OnNetworkDespawn();
|
||||
}
|
||||
|
||||
protected override void UseOnServer(NetworkObject playerObject)
|
||||
{
|
||||
useSequence.Value++;
|
||||
}
|
||||
|
||||
[ContextMenu("Preview/Use")]
|
||||
private void PreviewUse()
|
||||
{
|
||||
if (Application.isPlaying)
|
||||
PlayUseFeedback();
|
||||
else if (visual != null)
|
||||
visual.localScale = restingVisualScale * (1f + punchStrength);
|
||||
}
|
||||
|
||||
[ContextMenu("Preview/Reset")]
|
||||
private void PreviewReset()
|
||||
{
|
||||
useTween?.Kill();
|
||||
if (visual != null)
|
||||
visual.localScale = restingVisualScale;
|
||||
}
|
||||
|
||||
private void HandleUseSequenceChanged(uint _, uint currentValue) =>
|
||||
PlayUseFeedback();
|
||||
|
||||
private void PlayUseFeedback()
|
||||
{
|
||||
if (visual == null)
|
||||
return;
|
||||
|
||||
useTween?.Kill();
|
||||
visual.localScale = restingVisualScale;
|
||||
useTween = visual
|
||||
.DOPunchScale(Vector3.one * punchStrength, punchDuration, 6, 0.5f)
|
||||
.SetLink(gameObject);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9601a3599f93e74478faa19125cdb3e6
|
||||
@@ -0,0 +1,64 @@
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
public sealed class PlayerHandController : NetworkBehaviour, IPlayerSystem
|
||||
{
|
||||
[SerializeField] private Transform itemSocket;
|
||||
|
||||
private bool isInitialized;
|
||||
|
||||
public Transform ItemSocket => itemSocket;
|
||||
|
||||
public void Initialize(PlayerFacade facade)
|
||||
{
|
||||
if (itemSocket == null)
|
||||
{
|
||||
Transform candidate = transform.Find("HandItemSocket");
|
||||
if (candidate != null)
|
||||
itemSocket = candidate;
|
||||
}
|
||||
|
||||
isInitialized = itemSocket != null;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!isInitialized || !IsOwner || Keyboard.current == null)
|
||||
return;
|
||||
|
||||
if (!NetworkHandItemInteractable.TryGetHeldItem(
|
||||
OwnerClientId,
|
||||
out NetworkHandItemInteractable heldItem))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Mouse.current != null && Mouse.current.leftButton.wasPressedThisFrame)
|
||||
heldItem.RequestUse();
|
||||
|
||||
if (Keyboard.current.gKey.wasPressedThisFrame)
|
||||
heldItem.RequestThrow();
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
if (!IsOwner ||
|
||||
!NetworkHandItemInteractable.TryGetHeldItem(
|
||||
OwnerClientId,
|
||||
out NetworkHandItemInteractable heldItem))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GUIStyle style = new(GUI.skin.label)
|
||||
{
|
||||
alignment = TextAnchor.MiddleCenter,
|
||||
fontSize = 15,
|
||||
normal = { textColor = Color.white }
|
||||
};
|
||||
|
||||
Rect area = new(0f, Screen.height * 0.68f, Screen.width, 28f);
|
||||
GUI.Label(area, $"{heldItem.ItemName} [LMB] Use [G] Throw", style);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 50c1c3213ec617f4fb43a65d112b4736
|
||||
@@ -5,64 +5,37 @@ using UnityEngine;
|
||||
public enum RoundPhase : byte
|
||||
{
|
||||
WaitingForPlayers,
|
||||
DayInProgress,
|
||||
WaitingForPlayersToReturn
|
||||
WaitingForPlayersToLeave,
|
||||
WorkInProgress
|
||||
}
|
||||
|
||||
[RequireComponent(typeof(NetworkObject))]
|
||||
public sealed class RoundGameLoopController : NetworkBehaviour
|
||||
{
|
||||
[Header("Day")]
|
||||
[SerializeField, Min(1f)] private float dayDurationSeconds = 300f;
|
||||
[SerializeField, Min(0.05f)] private float timerSyncInterval = 0.1f;
|
||||
|
||||
private readonly NetworkVariable<RoundPhase> phase = new(
|
||||
RoundPhase.WaitingForPlayers,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server);
|
||||
|
||||
private readonly NetworkVariable<int> dayNumber = new(
|
||||
0,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server);
|
||||
|
||||
private readonly NetworkVariable<float> remainingDaySeconds = new(
|
||||
0f,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server);
|
||||
|
||||
private StartRoomZone startRoom;
|
||||
private float dayEndTime;
|
||||
private float nextTimerSyncTime;
|
||||
|
||||
public RoundPhase Phase => phase.Value;
|
||||
public int DayNumber => dayNumber.Value;
|
||||
public float RemainingDaySeconds => remainingDaySeconds.Value;
|
||||
|
||||
public event Action<RoundPhase> PhaseChanged;
|
||||
public event Action<int> DayStarted;
|
||||
public event Action<float> DayTimerChanged;
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
phase.OnValueChanged += HandlePhaseChanged;
|
||||
dayNumber.OnValueChanged += HandleDayNumberChanged;
|
||||
remainingDaySeconds.OnValueChanged += HandleTimerChanged;
|
||||
|
||||
startRoom = FindFirstObjectByType<StartRoomZone>();
|
||||
|
||||
if (IsServer)
|
||||
{
|
||||
phase.Value = RoundPhase.WaitingForPlayers;
|
||||
remainingDaySeconds.Value = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnNetworkDespawn()
|
||||
{
|
||||
phase.OnValueChanged -= HandlePhaseChanged;
|
||||
dayNumber.OnValueChanged -= HandleDayNumberChanged;
|
||||
remainingDaySeconds.OnValueChanged -= HandleTimerChanged;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
@@ -73,60 +46,70 @@ public sealed class RoundGameLoopController : NetworkBehaviour
|
||||
if (startRoom == null)
|
||||
startRoom = FindFirstObjectByType<StartRoomZone>();
|
||||
|
||||
if (startRoom == null || Time.time < nextTimerSyncTime)
|
||||
if (startRoom == null)
|
||||
return;
|
||||
|
||||
nextTimerSyncTime = Time.time + timerSyncInterval;
|
||||
|
||||
switch (phase.Value)
|
||||
{
|
||||
case RoundPhase.WaitingForPlayers:
|
||||
case RoundPhase.WaitingForPlayersToReturn:
|
||||
if (AreAllPlayersInStartRoom())
|
||||
StartNextDay();
|
||||
phase.Value = RoundPhase.WaitingForPlayersToLeave;
|
||||
break;
|
||||
|
||||
case RoundPhase.DayInProgress:
|
||||
float secondsLeft = Mathf.Max(0f, dayEndTime - Time.time);
|
||||
remainingDaySeconds.Value = secondsLeft;
|
||||
|
||||
if (secondsLeft <= 0f)
|
||||
{
|
||||
phase.Value = RoundPhase.WaitingForPlayersToReturn;
|
||||
remainingDaySeconds.Value = 0f;
|
||||
}
|
||||
case RoundPhase.WaitingForPlayersToLeave:
|
||||
if (AreAllPlayersOutsideStartRoom())
|
||||
phase.Value = RoundPhase.WorkInProgress;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private bool AreAllPlayersInStartRoom()
|
||||
{
|
||||
if (!TryGetConnectedPlayers(out int playerCount))
|
||||
return false;
|
||||
|
||||
int playersInside = 0;
|
||||
foreach (NetworkClient client in NetworkManager.ConnectedClients.Values)
|
||||
{
|
||||
if (client.PlayerObject != null && startRoom.Contains(client.PlayerObject.transform.position))
|
||||
playersInside++;
|
||||
}
|
||||
|
||||
return playersInside == playerCount;
|
||||
}
|
||||
|
||||
private bool AreAllPlayersOutsideStartRoom()
|
||||
{
|
||||
if (!TryGetConnectedPlayers(out int playerCount))
|
||||
return false;
|
||||
|
||||
int playersOutside = 0;
|
||||
foreach (NetworkClient client in NetworkManager.ConnectedClients.Values)
|
||||
{
|
||||
if (client.PlayerObject != null && !startRoom.Contains(client.PlayerObject.transform.position))
|
||||
playersOutside++;
|
||||
}
|
||||
|
||||
return playersOutside == playerCount;
|
||||
}
|
||||
|
||||
private bool TryGetConnectedPlayers(out int playerCount)
|
||||
{
|
||||
playerCount = 0;
|
||||
if (NetworkManager == null || NetworkManager.ConnectedClients.Count == 0)
|
||||
return false;
|
||||
|
||||
foreach (NetworkClient client in NetworkManager.ConnectedClients.Values)
|
||||
{
|
||||
if (client.PlayerObject == null || !startRoom.Contains(client.PlayerObject.transform.position))
|
||||
if (client.PlayerObject == null)
|
||||
return false;
|
||||
|
||||
playerCount++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void StartNextDay()
|
||||
{
|
||||
dayNumber.Value++;
|
||||
dayEndTime = Time.time + dayDurationSeconds;
|
||||
remainingDaySeconds.Value = dayDurationSeconds;
|
||||
phase.Value = RoundPhase.DayInProgress;
|
||||
return playerCount > 0;
|
||||
}
|
||||
|
||||
private void HandlePhaseChanged(RoundPhase _, RoundPhase current) =>
|
||||
PhaseChanged?.Invoke(current);
|
||||
|
||||
private void HandleDayNumberChanged(int _, int current) =>
|
||||
DayStarted?.Invoke(current);
|
||||
|
||||
private void HandleTimerChanged(float _, float current) =>
|
||||
DayTimerChanged?.Invoke(current);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
using UnityEngine;
|
||||
|
||||
public sealed class GlobalUIController : MonoBehaviour
|
||||
{
|
||||
public static GlobalUIController Instance { get; private set; }
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (Instance != null && Instance != this)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (Instance == this)
|
||||
Instance = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e3b2d63c400c82149becedd3f22f5d75
|
||||
Reference in New Issue
Block a user