Added many other

This commit is contained in:
2026-08-06 18:56:32 +03:00
parent 9f2fd08ee5
commit d9d7ee49d2
2144 changed files with 1115424 additions and 1284 deletions
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: bbc2c2310069742448776b9846b4eafb
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,147 @@
using Unity.Netcode;
using Unity.Netcode.Components;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;
public static class HandItemPrototypeSetup
{
private const string PlayerPrefabPath =
"Assets/CONTENT/Friendslop/Prefabs/NetworkPlayer.prefab";
private const string ItemPrefabFolder =
"Assets/CONTENT/Game/Prefabs/Items";
private const string ItemPrefabPath =
ItemPrefabFolder + "/TestCubeHandItem.prefab";
private const string FactoryScenePath = "Assets/CONTENT/Scenes/Factory.unity";
[MenuItem("Tools/FORT-BO/Setup Hand Item Prototype")]
public static void Setup()
{
if (EditorApplication.isPlayingOrWillChangePlaymode)
return;
EnsurePlayerHandSocket();
GameObject itemPrefab = EnsureTestItemPrefab();
EnsureFactoryInstance(itemPrefab);
AssetDatabase.SaveAssets();
}
private static void EnsurePlayerHandSocket()
{
GameObject root = PrefabUtility.LoadPrefabContents(PlayerPrefabPath);
try
{
PlayerHandController hand = root.GetComponent<PlayerHandController>();
if (hand == null)
hand = root.AddComponent<PlayerHandController>();
Transform socket = root.transform.Find("HandItemSocket");
if (socket == null)
{
var socketObject = new GameObject("HandItemSocket");
socket = socketObject.transform;
socket.SetParent(root.transform, false);
socket.localPosition = new Vector3(0.38f, 1.35f, 0.65f);
}
var serializedHand = new SerializedObject(hand);
serializedHand.FindProperty("itemSocket").objectReferenceValue = socket;
serializedHand.ApplyModifiedPropertiesWithoutUndo();
PrefabUtility.SaveAsPrefabAsset(root, PlayerPrefabPath);
}
finally
{
PrefabUtility.UnloadPrefabContents(root);
}
}
private static GameObject EnsureTestItemPrefab()
{
EnsureFolder("Assets/CONTENT/Game/Prefabs");
EnsureFolder(ItemPrefabFolder);
GameObject existing = AssetDatabase.LoadAssetAtPath<GameObject>(ItemPrefabPath);
if (existing != null)
return existing;
var root = new GameObject("TestCubeHandItem");
try
{
root.AddComponent<NetworkObject>();
root.AddComponent<NetworkTransform>();
Rigidbody body = root.AddComponent<Rigidbody>();
body.mass = 0.5f;
body.interpolation = RigidbodyInterpolation.Interpolate;
BoxCollider rootCollider = root.AddComponent<BoxCollider>();
rootCollider.size = Vector3.one * 0.35f;
NetworkTestCubeItem item = root.AddComponent<NetworkTestCubeItem>();
var serializedItem = new SerializedObject(item);
serializedItem.FindProperty("interactionLabel").stringValue = "Test Cube";
GameObject visual = GameObject.CreatePrimitive(PrimitiveType.Cube);
visual.name = "Visual";
Object.DestroyImmediate(visual.GetComponent<BoxCollider>());
visual.transform.SetParent(root.transform, false);
visual.transform.localScale = Vector3.one * 0.35f;
serializedItem.FindProperty("visual").objectReferenceValue = visual.transform;
serializedItem.ApplyModifiedPropertiesWithoutUndo();
return PrefabUtility.SaveAsPrefabAsset(root, ItemPrefabPath);
}
finally
{
Object.DestroyImmediate(root);
}
}
private static void EnsureFactoryInstance(GameObject itemPrefab)
{
if (itemPrefab == null)
return;
Scene scene = SceneManager.GetSceneByPath(FactoryScenePath);
bool openedBySetup = !scene.IsValid() || !scene.isLoaded;
if (openedBySetup)
scene = EditorSceneManager.OpenScene(FactoryScenePath, OpenSceneMode.Additive);
foreach (GameObject root in scene.GetRootGameObjects())
{
if (root.name == "TestCubeHandItem")
{
if (scene.isDirty)
EditorSceneManager.SaveScene(scene);
if (openedBySetup)
EditorSceneManager.CloseScene(scene, true);
return;
}
}
GameObject instance = (GameObject)PrefabUtility.InstantiatePrefab(itemPrefab, scene);
instance.name = "TestCubeHandItem";
instance.transform.position = new Vector3(2f, 1f, 2f);
EditorSceneManager.MarkSceneDirty(scene);
EditorSceneManager.SaveScene(scene);
if (openedBySetup)
EditorSceneManager.CloseScene(scene, true);
}
private static void EnsureFolder(string folderPath)
{
if (AssetDatabase.IsValidFolder(folderPath))
return;
int separator = folderPath.LastIndexOf('/');
string parent = folderPath[..separator];
string folderName = folderPath[(separator + 1)..];
EnsureFolder(parent);
AssetDatabase.CreateFolder(parent, folderName);
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: d5e5a6d20559c47458e11f23d345823f
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9e3d9eea7e5615d41ab5243f6208a41a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a7ede43bd6aca644cb23d8194adbe41e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,267 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &2313167264979542864
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 989906489684169426}
- component: {fileID: 6213379512587125323}
- component: {fileID: 5003530258879543868}
m_Layer: 0
m_Name: Visual
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &989906489684169426
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2313167264979542864}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 0.35, y: 0.35, z: 0.35}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 8002572065248676643}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!33 &6213379512587125323
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2313167264979542864}
m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
--- !u!23 &5003530258879543868
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2313167264979542864}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 1
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!1 &8851717593389946727
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 8002572065248676643}
- component: {fileID: 1224744003078846777}
- component: {fileID: 1521611582548381119}
- component: {fileID: 5633296382699422203}
- component: {fileID: 6224464273273603393}
- component: {fileID: 32730300719699172}
m_Layer: 0
m_Name: TestCubeHandItem
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &8002572065248676643
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8851717593389946727}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 989906489684169426}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &1224744003078846777
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8851717593389946727}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3}
m_Name:
m_EditorClassIdentifier:
GlobalObjectIdHash: 2686109345
InScenePlacedSourceGlobalObjectIdHash: 0
DeferredDespawnTick: 0
Ownership: 1
AlwaysReplicateAsRoot: 0
SynchronizeTransform: 1
m_InScenePlaced: 1
ActiveSceneSynchronization: 0
SceneMigrationSynchronization: 0
SpawnWithObservers: 1
DontDestroyWithOwner: 0
AutoObjectParentSync: 1
SyncOwnerTransformWhenParented: 1
AllowOwnerToParent: 0
--- !u!114 &1521611582548381119
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8851717593389946727}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: e96cb6065543e43c4a752faaa1468eb1, type: 3}
m_Name:
m_EditorClassIdentifier:
ShowTopMostFoldoutHeaderGroup: 1
NetworkTransformExpanded: 0
AutoOwnerAuthorityTickOffset: 1
PositionInterpolationType: 0
RotationInterpolationType: 0
ScaleInterpolationType: 0
PositionLerpSmoothing: 1
PositionMaxInterpolationTime: 0.1
RotationLerpSmoothing: 1
RotationMaxInterpolationTime: 0.1
ScaleLerpSmoothing: 1
ScaleMaxInterpolationTime: 0.1
AuthorityMode: 0
TickSyncChildren: 0
UseUnreliableDeltas: 0
SyncPositionX: 1
SyncPositionY: 1
SyncPositionZ: 1
SyncRotAngleX: 1
SyncRotAngleY: 1
SyncRotAngleZ: 1
SyncScaleX: 1
SyncScaleY: 1
SyncScaleZ: 1
PositionThreshold: 0.001
RotAngleThreshold: 0.01
ScaleThreshold: 0.01
UseQuaternionSynchronization: 0
UseQuaternionCompression: 0
UseHalfFloatPrecision: 0
InLocalSpace: 0
SwitchTransformSpaceWhenParented: 0
Interpolate: 1
SlerpPosition: 0
--- !u!54 &5633296382699422203
Rigidbody:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8851717593389946727}
serializedVersion: 4
m_Mass: 0.5
m_Drag: 0
m_AngularDrag: 0.05
m_CenterOfMass: {x: 0, y: 0, z: 0}
m_InertiaTensor: {x: 1, y: 1, z: 1}
m_InertiaRotation: {x: 0, y: 0, z: 0, w: 1}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_ImplicitCom: 1
m_ImplicitTensor: 1
m_UseGravity: 1
m_IsKinematic: 0
m_Interpolate: 1
m_Constraints: 0
m_CollisionDetection: 0
--- !u!65 &6224464273273603393
BoxCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8851717593389946727}
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_IsTrigger: 0
m_ProvidesContacts: 0
m_Enabled: 1
serializedVersion: 3
m_Size: {x: 0.35, y: 0.35, z: 0.35}
m_Center: {x: 0, y: 0, z: 0}
--- !u!114 &32730300719699172
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8851717593389946727}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 9601a3599f93e74478faa19125cdb3e6, type: 3}
m_Name:
m_EditorClassIdentifier:
ShowTopMostFoldoutHeaderGroup: 1
interactionLabel: Test Cube
maximumInteractionDistance: 3
physicsBody: {fileID: 0}
itemColliders: []
throwForce: 4
throwUpwardForce: 1
visual: {fileID: 989906489684169426}
punchDuration: 0.25
punchStrength: 0.2
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 814d56a483938ef489a23f2e2973989a
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -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