Initial commit
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fc458c0abfe5b264d96cd9ee6413f12c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+288
@@ -0,0 +1,288 @@
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
#endif
|
||||
|
||||
namespace XRMultiplayer
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
[CustomEditor(typeof(NetworkObjectDispenser))]
|
||||
public class NetworkObjectDispenserEditor : Editor
|
||||
{
|
||||
public override VisualElement CreateInspectorGUI()
|
||||
{
|
||||
return DrawBaseContainer();
|
||||
}
|
||||
|
||||
VisualElement DrawBaseContainer()
|
||||
{
|
||||
var root = new VisualElement();
|
||||
|
||||
Box backgroundBox = new();
|
||||
Foldout foldout = new Foldout
|
||||
{
|
||||
name = "Dispenser Panels Foldout",
|
||||
text = "Dispenser Spawn Groups",
|
||||
};
|
||||
|
||||
// Creating a second nested foldout to help with the layout
|
||||
Foldout persistentFoldout = new Foldout
|
||||
{
|
||||
name = "Persistent Panel Foldout",
|
||||
text = "Persistent Spawn Group",
|
||||
};
|
||||
|
||||
persistentFoldout.style.paddingLeft = 11.0f;
|
||||
|
||||
|
||||
persistentFoldout.Add(new PropertyField(serializedObject.FindProperty("m_PersistentPanel")));
|
||||
foldout.Add(persistentFoldout);
|
||||
|
||||
var togglePanels = new PropertyField(serializedObject.FindProperty("m_Panels"), "Toggleable Spawn Groups");
|
||||
foldout.Add(togglePanels);
|
||||
|
||||
backgroundBox.Add(foldout);
|
||||
root.Add(backgroundBox);
|
||||
|
||||
var capacityContainer = GetCapacityContainer();
|
||||
root.Add(capacityContainer);
|
||||
capacityContainer.PlaceBehind(backgroundBox);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
VisualElement GetCapacityContainer()
|
||||
{
|
||||
var container = new VisualElement();
|
||||
container.Add(new PropertyField(serializedObject.FindProperty("m_ClearButton")));
|
||||
container.Add(new PropertyField(serializedObject.FindProperty("m_Capacity")));
|
||||
container.Add(new PropertyField(serializedObject.FindProperty("m_CountText")));
|
||||
container.Add(new PropertyField(serializedObject.FindProperty("m_DistanceCheckTimeInterval")));
|
||||
return container;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[CustomPropertyDrawer(typeof(DispenserPanel))]
|
||||
public class DispenserPanelDrawer : PropertyDrawer
|
||||
{
|
||||
NetworkObjectDispenser m_target = null;
|
||||
public override VisualElement CreatePropertyGUI(SerializedProperty property)
|
||||
{
|
||||
if(Selection.activeGameObject != null && Selection.activeGameObject.GetComponent<NetworkObjectDispenser>() != null)
|
||||
{
|
||||
m_target = (NetworkObjectDispenser)Selection.activeGameObject.GetComponent(typeof(NetworkObjectDispenser));
|
||||
}
|
||||
var container = new VisualElement();
|
||||
|
||||
int panelId = property.FindPropertyRelative("panelId").intValue;
|
||||
|
||||
Box backgroundBox = new();
|
||||
backgroundBox.style.backgroundColor = new UnityEngine.Color(0.1f, 0.1f, 0.1f, .5f);
|
||||
backgroundBox.Add(new PropertyField(property.FindPropertyRelative("panelName"), "Group Name"));
|
||||
backgroundBox.Add(new PropertyField(property.FindPropertyRelative("panel"), "Group GameObject"));
|
||||
|
||||
|
||||
if(m_target == null)
|
||||
{
|
||||
Label errorLabel = new Label("Please select a NetworkObjectDispenser GameObject to Spawn Previews.");
|
||||
backgroundBox.Add(errorLabel);
|
||||
container.Add(backgroundBox);
|
||||
return container;
|
||||
}
|
||||
|
||||
var buttonContainer = new VisualElement();
|
||||
buttonContainer.style.flexDirection = FlexDirection.Row;
|
||||
TextElement textElement = new()
|
||||
{
|
||||
style =
|
||||
{
|
||||
fontSize = 12,
|
||||
marginTop = 5.0f,
|
||||
marginLeft = 5.0f,
|
||||
marginBottom = 5.0f
|
||||
},
|
||||
text = "Group Preview"
|
||||
};
|
||||
buttonContainer.Add(textElement);
|
||||
var showButton = ObjectDispenserEditorButton.CreateDefaultButton("Show");
|
||||
showButton.style.marginLeft = 50.0f;
|
||||
showButton.style.alignSelf = Align.FlexEnd;
|
||||
var hideButton = ObjectDispenserEditorButton.CreateDefaultButton("Hide");
|
||||
hideButton.style.alignSelf = Align.FlexEnd;
|
||||
|
||||
PanelToggleGroup panelToggle = new(showButton, hideButton, panelId, -1);
|
||||
|
||||
showButton.clicked += () => SpawnPanel(panelToggle);
|
||||
hideButton.clicked += () => ClearPanel(panelToggle);
|
||||
m_target.OnProxiesUpdated += () => UpdateButtonsState(panelToggle);
|
||||
|
||||
UpdateButtonsState(panelToggle);
|
||||
|
||||
buttonContainer.Add(showButton);
|
||||
buttonContainer.Add(hideButton);
|
||||
|
||||
backgroundBox.Add(buttonContainer);
|
||||
|
||||
backgroundBox.Add(new PropertyField(property.FindPropertyRelative("dispenserSlots"), "Spawn Slots"));
|
||||
container.Add(backgroundBox);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
void SpawnPanel(PanelToggleGroup panelToggle)
|
||||
{
|
||||
if(m_target != null)
|
||||
m_target.SpawnProxyPanel(panelToggle.panelId);
|
||||
UpdateButtonsState(panelToggle);
|
||||
}
|
||||
|
||||
void ClearPanel(PanelToggleGroup panelToggle)
|
||||
{
|
||||
if(m_target != null)
|
||||
m_target.ClearProxyPanel(panelToggle.panelId);
|
||||
UpdateButtonsState(panelToggle);
|
||||
}
|
||||
|
||||
void UpdateButtonsState(PanelToggleGroup panelToggle)
|
||||
{
|
||||
bool isShowing = m_target.IsProxyPanelShowing(panelToggle.panelId);
|
||||
bool IsFull = m_target.IsProxyPanelFull(panelToggle.panelId);
|
||||
panelToggle.showButton.SetEnabled(!IsFull);
|
||||
panelToggle.hideButton.SetEnabled(isShowing);
|
||||
}
|
||||
}
|
||||
|
||||
[CustomPropertyDrawer(typeof(DispenserSlot))]
|
||||
public class DispenserSlotDrawer : PropertyDrawer
|
||||
{
|
||||
NetworkObjectDispenser m_target = null;
|
||||
public override VisualElement CreatePropertyGUI(SerializedProperty property)
|
||||
{
|
||||
if(Selection.activeGameObject != null && Selection.activeGameObject.GetComponent<NetworkObjectDispenser>() != null)
|
||||
{
|
||||
m_target = (NetworkObjectDispenser)Selection.activeGameObject.GetComponent(typeof(NetworkObjectDispenser));
|
||||
}
|
||||
|
||||
var container = new VisualElement();
|
||||
|
||||
int slotId = property.FindPropertyRelative("slotId").intValue;
|
||||
int panelId = property.FindPropertyRelative("panelId").intValue;
|
||||
bool hasSpawnedProxy = property.FindPropertyRelative("hasSpawnedProxy").boolValue;
|
||||
|
||||
Box backgroundBox = new();
|
||||
backgroundBox.style.backgroundColor = new UnityEngine.Color(0.1f, 0.1f, 0.1f, .5f);
|
||||
backgroundBox.Add(new PropertyField(property.FindPropertyRelative("freezeOnSpawn"), "Freeze On Spawn"));
|
||||
backgroundBox.Add(new PropertyField(property.FindPropertyRelative("distanceToSpawnNew"), "Distance To Spawn New"));
|
||||
backgroundBox.Add(new PropertyField(property.FindPropertyRelative("spawnCooldown"), "Spawn Cooldown"));
|
||||
backgroundBox.Add(new PropertyField(property.FindPropertyRelative("dispenserSlotTransform"), "Spawn Transform"));
|
||||
backgroundBox.Add(new PropertyField(property.FindPropertyRelative("spawnableInteractablePrefab"), "Spawn Prefab"));
|
||||
|
||||
if(m_target == null)
|
||||
{
|
||||
Label errorLabel = new Label("Please select a NetworkObjectDispenser GameObject to Spawn Previews.");
|
||||
backgroundBox.Add(errorLabel);
|
||||
container.Add(backgroundBox);
|
||||
return container;
|
||||
}
|
||||
|
||||
|
||||
var buttonContainer = new VisualElement();
|
||||
buttonContainer.style.flexDirection = FlexDirection.Row;
|
||||
|
||||
|
||||
TextElement textElement = new()
|
||||
{
|
||||
style =
|
||||
{
|
||||
fontSize = 12,
|
||||
marginTop = 5.0f,
|
||||
marginLeft = 5.0f,
|
||||
marginBottom = 5.0f
|
||||
},
|
||||
text = "Object Preview"
|
||||
};
|
||||
buttonContainer.Add(textElement);
|
||||
var showButton = ObjectDispenserEditorButton.CreateDefaultButton("Show");
|
||||
showButton.style.marginLeft = 50.0f;
|
||||
var hideButton = ObjectDispenserEditorButton.CreateDefaultButton("Hide");
|
||||
|
||||
PanelToggleGroup panelToggle = new(showButton, hideButton, panelId, slotId);
|
||||
|
||||
showButton.clicked += () => SpawnButton(panelToggle);
|
||||
hideButton.clicked += () => ClearButton(panelToggle);
|
||||
m_target.OnProxiesUpdated += () => UpdateButtonsState(panelToggle);
|
||||
|
||||
UpdateButtonsState(panelToggle);
|
||||
|
||||
buttonContainer.Add(showButton);
|
||||
buttonContainer.Add(hideButton);
|
||||
|
||||
backgroundBox.Add(buttonContainer);
|
||||
|
||||
container.Add(backgroundBox);
|
||||
return container;
|
||||
}
|
||||
|
||||
void SpawnButton(PanelToggleGroup panelToggle)
|
||||
{
|
||||
if(m_target != null)
|
||||
{
|
||||
m_target.SpawnProxy(panelToggle.panelId, panelToggle.slotId);
|
||||
}
|
||||
UpdateButtonsState(panelToggle);
|
||||
}
|
||||
|
||||
void ClearButton(PanelToggleGroup panelToggle)
|
||||
{
|
||||
if(m_target != null)
|
||||
{
|
||||
m_target.ClearProxy(panelToggle.panelId, panelToggle.slotId);
|
||||
}
|
||||
UpdateButtonsState(panelToggle);
|
||||
}
|
||||
|
||||
void UpdateButtonsState(PanelToggleGroup panelToggle)
|
||||
{
|
||||
bool isProxyShowing = m_target.IsProxySlotShowing(panelToggle.panelId, panelToggle.slotId);
|
||||
panelToggle.showButton.SetEnabled(!isProxyShowing);
|
||||
panelToggle.hideButton.SetEnabled(isProxyShowing);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ObjectDispenserEditorButton
|
||||
{
|
||||
public static Button CreateDefaultButton(string buttonText)
|
||||
{
|
||||
var button = new Button
|
||||
{
|
||||
text = buttonText,
|
||||
style =
|
||||
{
|
||||
height = 20.0f,
|
||||
width = 75.0f,
|
||||
}
|
||||
};
|
||||
|
||||
return button;
|
||||
}
|
||||
}
|
||||
|
||||
public struct PanelToggleGroup
|
||||
{
|
||||
public Button showButton;
|
||||
public Button hideButton;
|
||||
public int panelId;
|
||||
public int slotId;
|
||||
|
||||
public PanelToggleGroup(Button showButton, Button hideButton, int panelId, int slotId)
|
||||
{
|
||||
this.showButton = showButton;
|
||||
this.hideButton = hideButton;
|
||||
this.panelId = panelId;
|
||||
this.slotId = slotId;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9a054c1bb9cd10142bfd82ee243710a5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,614 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using Unity.Netcode;
|
||||
using System;
|
||||
using UnityEngine.Events;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor.SceneManagement;
|
||||
#endif
|
||||
|
||||
namespace XRMultiplayer
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a networked object dispenser that can spawn and despawn interactable objects.
|
||||
/// </summary>
|
||||
public class NetworkObjectDispenser : NetworkBehaviour
|
||||
{
|
||||
const int k_NonToggleablePanelId = -1;
|
||||
public Action OnProxiesUpdated;
|
||||
|
||||
/// <summary>
|
||||
/// The button used to clear the current interactables.
|
||||
/// </summary>
|
||||
[SerializeField] UnityEngine.UI.Button m_ClearButton;
|
||||
|
||||
// [SerializeField] bool m_UseCapacity = false;
|
||||
/// <summary>
|
||||
/// The maximum capacity of the dispenser.
|
||||
/// </summary>
|
||||
[SerializeField] int m_Capacity;
|
||||
|
||||
[SerializeField] float m_DistanceCheckTimeInterval = .5f;
|
||||
/// <summary>
|
||||
/// The text component displaying the current capacity.
|
||||
/// </summary>
|
||||
[SerializeField] TMP_Text m_CountText;
|
||||
|
||||
/// <summary>
|
||||
/// The network variable representing the current capacity.
|
||||
/// </summary>
|
||||
NetworkVariable<int> m_CurrentCapacityNetworked = new NetworkVariable<int>(0, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Server);
|
||||
|
||||
/// <summary>
|
||||
/// The network variable representing the current panel ID.
|
||||
/// </summary>
|
||||
NetworkVariable<int> m_CurrentPanelIdNetworked = new NetworkVariable<int>(-1, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
|
||||
|
||||
/// <summary>
|
||||
/// The list of currently active interactables.
|
||||
/// </summary>
|
||||
List<NetworkBaseInteractable> m_ActiveInteractables = new List<NetworkBaseInteractable>();
|
||||
|
||||
// /// <summary>
|
||||
// /// The panels containing the dispenser slots.
|
||||
// /// </summary>
|
||||
[SerializeField] DispenserPanel[] m_Panels;
|
||||
|
||||
// /// <summary>
|
||||
// /// This panel is persistent and does not switch on or off.
|
||||
// /// /// </summary>
|
||||
[SerializeField] DispenserPanel m_PersistentPanel;
|
||||
|
||||
[SerializeField] Transform m_DefaultSpawnTransform;
|
||||
|
||||
///<inheritdoc/>
|
||||
private void Start()
|
||||
{
|
||||
CheckForProxies();
|
||||
m_CurrentCapacityNetworked.OnValueChanged += UpdateCapacity;
|
||||
}
|
||||
|
||||
///<inheritdoc/>
|
||||
public override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
m_CurrentCapacityNetworked.OnValueChanged -= UpdateCapacity;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
void OnValidate()
|
||||
{
|
||||
CheckForProxies(false);
|
||||
|
||||
PrefabStage.prefabStageOpened -= (PrefabStage stage) => CheckForProxies();
|
||||
PrefabStage.prefabStageOpened += (PrefabStage stage) => CheckForProxies();
|
||||
|
||||
for(int i = 0; i < m_Panels.Length; i++)
|
||||
{
|
||||
m_Panels[i].panelId = i;
|
||||
for(int j = 0; j < m_Panels[i].dispenserSlots.Length; j++)
|
||||
{
|
||||
if(m_Panels[i].dispenserSlots[j] == null) continue;
|
||||
m_Panels[i].dispenserSlots[j].panelId = i;
|
||||
m_Panels[i].dispenserSlots[j].slotId = j;
|
||||
}
|
||||
}
|
||||
|
||||
m_PersistentPanel.panelId = k_NonToggleablePanelId;
|
||||
for(int j = 0; j < m_PersistentPanel.dispenserSlots.Length; j++)
|
||||
{
|
||||
if(m_PersistentPanel.dispenserSlots[j] == null) continue;
|
||||
m_PersistentPanel.dispenserSlots[j].panelId = k_NonToggleablePanelId;
|
||||
m_PersistentPanel.dispenserSlots[j].slotId = j;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
IEnumerator ServerSpawnCooldownRoutine()
|
||||
{
|
||||
float deltaTime;
|
||||
while (IsServer)
|
||||
{
|
||||
deltaTime = Time.deltaTime;
|
||||
foreach (DispenserPanel panel in m_Panels)
|
||||
{
|
||||
foreach (DispenserSlot slot in panel.dispenserSlots)
|
||||
{
|
||||
if (!slot.dispenserSlotTransform.gameObject.activeInHierarchy) continue;
|
||||
if (slot.CanSpawn(deltaTime))
|
||||
{
|
||||
AddInteractableToDispenser(panel, slot.slotId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (DispenserSlot slot in m_PersistentPanel.dispenserSlots)
|
||||
{
|
||||
if (!slot.dispenserSlotTransform.gameObject.activeInHierarchy) continue;
|
||||
if (slot.CanSpawn(deltaTime))
|
||||
{
|
||||
AddInteractableToDispenser(m_PersistentPanel, slot.slotId);
|
||||
}
|
||||
}
|
||||
|
||||
yield return new WaitForEndOfFrame();
|
||||
}
|
||||
}
|
||||
IEnumerator ServerDistanceCheckRoutine()
|
||||
{
|
||||
while (IsServer)
|
||||
{
|
||||
foreach (DispenserPanel panel in m_Panels)
|
||||
{
|
||||
foreach (DispenserSlot slot in panel.dispenserSlots)
|
||||
{
|
||||
if (slot.CheckInteractablePosition())
|
||||
{
|
||||
m_ActiveInteractables.Add(slot.currentInteractable);
|
||||
m_CurrentCapacityNetworked.Value = m_ActiveInteractables.Count;
|
||||
slot.currentInteractable = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (DispenserSlot slot in m_PersistentPanel.dispenserSlots)
|
||||
{
|
||||
if (slot.CheckInteractablePosition())
|
||||
{
|
||||
m_ActiveInteractables.Add(slot.currentInteractable);
|
||||
m_CurrentCapacityNetworked.Value = m_ActiveInteractables.Count;
|
||||
slot.currentInteractable = null;
|
||||
}
|
||||
}
|
||||
|
||||
yield return new WaitForSeconds(m_DistanceCheckTimeInterval);
|
||||
}
|
||||
}
|
||||
|
||||
///<inheritdoc/>
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
base.OnNetworkSpawn();
|
||||
|
||||
m_ActiveInteractables.Clear();
|
||||
if (IsServer)
|
||||
{
|
||||
EnablePanel(0);
|
||||
StartCoroutine(ServerDistanceCheckRoutine());
|
||||
StartCoroutine(ServerSpawnCooldownRoutine());
|
||||
m_CurrentCapacityNetworked.Value = 0;
|
||||
}
|
||||
else if (m_CurrentPanelIdNetworked.Value != -1)
|
||||
{
|
||||
EnablePanel(m_CurrentPanelIdNetworked.Value);
|
||||
if (m_CountText != null)
|
||||
m_CountText.text = $"Current Capacity: {m_CurrentCapacityNetworked.Value} / {m_Capacity}";
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnNetworkDespawn()
|
||||
{
|
||||
base.OnNetworkDespawn();
|
||||
StopAllCoroutines();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the capacity UI based on the current capacity value.
|
||||
/// </summary>
|
||||
/// <param name="old">The old capacity value.</param>
|
||||
/// <param name="current">The current capacity value.</param>
|
||||
void UpdateCapacity(int old, int current)
|
||||
{
|
||||
if (m_CountText != null)
|
||||
{
|
||||
m_CountText.text = $"Current Capacity: {m_CurrentCapacityNetworked.Value} / {m_Capacity}";
|
||||
m_CountText.color = m_CurrentCapacityNetworked.Value > m_Capacity ? Color.red : Color.white;
|
||||
}
|
||||
|
||||
if (m_ClearButton != null)
|
||||
m_ClearButton.interactable = m_CurrentCapacityNetworked.Value > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the current interactables on the server.
|
||||
/// </summary>
|
||||
public void ClearCurrentInteractables()
|
||||
{
|
||||
if (IsServer)
|
||||
{
|
||||
for (int i = m_ActiveInteractables.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (m_ActiveInteractables[i] != null & !m_ActiveInteractables[i].isInteracting)
|
||||
{
|
||||
m_ActiveInteractables[i].NetworkObject.Despawn();
|
||||
m_ActiveInteractables.Remove(m_ActiveInteractables[i]);
|
||||
}
|
||||
}
|
||||
m_CurrentCapacityNetworked.Value = m_ActiveInteractables.Count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Picks up an object from a dispenser panel slot.
|
||||
/// </summary>
|
||||
/// <param name="panel">The dispenser panel.</param>
|
||||
/// <param name="slotId">The slot ID.</param>
|
||||
void PickupObject(UnityAction<bool> bindingAction, int panelId, int slotId)
|
||||
{
|
||||
var panel = GetPanelById(panelId);
|
||||
if (!IsServer) { Utils.Log("Trying to Spawn Object from Non-Server Client"); return; }
|
||||
NetworkBaseInteractable netInteractable = panel.dispenserSlots[slotId].currentInteractable;
|
||||
if (netInteractable == null) return;
|
||||
|
||||
netInteractable.OnInteractingChanged.RemoveListener(bindingAction);
|
||||
|
||||
if (netInteractable.TryGetComponent(out Rigidbody rb))
|
||||
{
|
||||
rb.constraints = RigidbodyConstraints.None;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an interactable to a dispenser panel slot on the server.
|
||||
/// </summary>
|
||||
/// <param name="panel">The dispenser panel.</param>
|
||||
/// <param name="slotId">The slot ID.</param>
|
||||
/// <param name="prefabId">The prefab ID, if default (-1) it will spawn based on the index of the prefab list.</param>
|
||||
void AddInteractableToDispenser(DispenserPanel panel, int slotId)
|
||||
{
|
||||
if (panel.dispenserSlots[slotId].currentInteractable != null)
|
||||
{
|
||||
Utils.Log($"Cannot Spawn. Interactable already exists in Panel {panel.panelId}, slot {panel.dispenserSlots[slotId].slotId}");
|
||||
return;
|
||||
}
|
||||
|
||||
Transform spawnerTransform = panel.dispenserSlots[slotId].dispenserSlotTransform;
|
||||
panel.dispenserSlots[slotId].m_SpawnCooldownTimer = panel.dispenserSlots[slotId].spawnCooldown;
|
||||
NetworkBaseInteractable spawnedInteractable = panel.dispenserSlots[slotId].SpawnInteractablePrefab(spawnerTransform);
|
||||
|
||||
panel.dispenserSlots[slotId].currentInteractable = spawnedInteractable;
|
||||
panel.dispenserSlots[slotId].currentInteractable.NetworkObject.Spawn();
|
||||
|
||||
// Creates a UnityAction<bool> that calls PickupObject with the correct parameters
|
||||
void PickupBinding(bool arg0)
|
||||
{
|
||||
PickupObject(PickupBinding, panel.panelId, slotId);
|
||||
}
|
||||
|
||||
spawnedInteractable.OnInteractingChanged.AddListener(PickupBinding);
|
||||
|
||||
if (panel.dispenserSlots[slotId].freezeOnSpawn && spawnedInteractable.TryGetComponent(out Rigidbody rb))
|
||||
{
|
||||
rb.constraints = RigidbodyConstraints.FreezeAll;
|
||||
}
|
||||
}
|
||||
|
||||
DispenserPanel GetPanelById(int Id)
|
||||
{
|
||||
if (Id == k_NonToggleablePanelId) return m_PersistentPanel;
|
||||
return m_Panels[Id];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the current panel on the server.
|
||||
/// </summary>
|
||||
void ClearPanel()
|
||||
{
|
||||
StopAllCoroutines();
|
||||
foreach (var slot in m_Panels[m_CurrentPanelIdNetworked.Value].dispenserSlots)
|
||||
{
|
||||
if (slot.currentInteractable != null)
|
||||
{
|
||||
slot.currentInteractable.NetworkObject.Despawn();
|
||||
slot.currentInteractable = null;
|
||||
}
|
||||
}
|
||||
StartCoroutine(ServerDistanceCheckRoutine());
|
||||
StartCoroutine(ServerSpawnCooldownRoutine());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables a specific panel and adds interactables to its slots on the server.
|
||||
/// </summary>
|
||||
/// <param name="panelId">The panel ID.</param>
|
||||
public void EnablePanel(int panelId)
|
||||
{
|
||||
for (int i = 0; i < m_Panels.Length; i++)
|
||||
{
|
||||
m_Panels[i].panel.SetActive(i == panelId);
|
||||
}
|
||||
if (IsServer)
|
||||
{
|
||||
if (m_CurrentPanelIdNetworked.Value != -1)
|
||||
{
|
||||
ClearPanel();
|
||||
}
|
||||
|
||||
m_CurrentPanelIdNetworked.Value = panelId;
|
||||
}
|
||||
}
|
||||
|
||||
[ContextMenu("Spawn Random Object")]
|
||||
void SpawnRandomObject()
|
||||
{
|
||||
SpawnRandomRpc(m_DefaultSpawnTransform.position, m_DefaultSpawnTransform.rotation);
|
||||
}
|
||||
|
||||
[Rpc(SendTo.Server)]
|
||||
public void SpawnRandomRpc(Vector3 spawnPosition, Quaternion spawnRotation)
|
||||
{
|
||||
var spawnObject = m_PersistentPanel.dispenserSlots[UnityEngine.Random.Range(0, m_PersistentPanel.dispenserSlots.Length)].spawnableInteractablePrefab;
|
||||
if (UnityEngine.Random.value < .85f)
|
||||
{
|
||||
int randomPanel = UnityEngine.Random.Range(0, m_Panels.Length);
|
||||
int randomSlot = UnityEngine.Random.Range(0, m_Panels[randomPanel].dispenserSlots.Length);
|
||||
spawnObject = m_Panels[randomPanel].dispenserSlots[randomSlot].spawnableInteractablePrefab;
|
||||
}
|
||||
|
||||
NetworkPhysicsInteractable spawnedObject = Instantiate(spawnObject.gameObject, spawnPosition, spawnRotation).GetComponent<NetworkPhysicsInteractable>();
|
||||
spawnedObject.spawnLocked = false;
|
||||
spawnedObject.NetworkObject.Spawn();
|
||||
|
||||
m_ActiveInteractables.Add(spawnedObject.GetComponent<NetworkBaseInteractable>());
|
||||
m_CurrentCapacityNetworked.Value = m_ActiveInteractables.Count;
|
||||
}
|
||||
|
||||
[ContextMenu("Clear Proxies")]
|
||||
public void ClearProxies()
|
||||
{
|
||||
foreach (var panel in m_Panels)
|
||||
{
|
||||
foreach (var slot in panel.dispenserSlots)
|
||||
{
|
||||
slot.ClearProxy();
|
||||
}
|
||||
}
|
||||
foreach (var slot in m_PersistentPanel.dispenserSlots)
|
||||
{
|
||||
slot.ClearProxy();
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearProxyPanel(int panelId)
|
||||
{
|
||||
(panelId == k_NonToggleablePanelId ? m_PersistentPanel : m_Panels[panelId]).ClearProxyPanel();
|
||||
OnProxiesUpdated?.Invoke();
|
||||
}
|
||||
|
||||
public void ClearProxy(int panelId, int slotId)
|
||||
{
|
||||
(panelId == k_NonToggleablePanelId ? m_PersistentPanel : m_Panels[panelId]).dispenserSlots[slotId].ClearProxy();
|
||||
OnProxiesUpdated?.Invoke();
|
||||
}
|
||||
|
||||
public void SpawnProxyPanel(int panelId)
|
||||
{
|
||||
if (panelId != k_NonToggleablePanelId)
|
||||
{
|
||||
for (int i = 0; i < m_Panels.Length; i++)
|
||||
{
|
||||
if (panelId != i)
|
||||
{
|
||||
m_Panels[i].ClearProxyPanel();
|
||||
}
|
||||
}
|
||||
}
|
||||
(panelId == k_NonToggleablePanelId ? m_PersistentPanel : m_Panels[panelId]).SpawnProxyPanel();
|
||||
|
||||
OnProxiesUpdated?.Invoke();
|
||||
}
|
||||
|
||||
public void SpawnProxy(int panelId, int slotId)
|
||||
{
|
||||
(panelId == k_NonToggleablePanelId ? m_PersistentPanel : m_Panels[panelId]).dispenserSlots[slotId].SpawnProxy();
|
||||
|
||||
NetworkBaseInteractable interactable = (panelId == k_NonToggleablePanelId ? m_PersistentPanel : m_Panels[panelId]).dispenserSlots[slotId].currentInteractable;
|
||||
OnProxiesUpdated?.Invoke();
|
||||
}
|
||||
|
||||
public bool IsProxyPanelShowing(int panelId)
|
||||
{
|
||||
if (m_Panels.Length == 0) return false;
|
||||
DispenserPanel panel = panelId == k_NonToggleablePanelId ? m_PersistentPanel : m_Panels[panelId];
|
||||
foreach (var slot in panel.dispenserSlots)
|
||||
{
|
||||
if (slot.hasSpawnedProxy) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsProxyPanelFull(int panelId)
|
||||
{
|
||||
if (m_Panels.Length == 0) return false;
|
||||
DispenserPanel panel = panelId == k_NonToggleablePanelId ? m_PersistentPanel : m_Panels[panelId];
|
||||
foreach (var slot in panel.dispenserSlots)
|
||||
{
|
||||
if (!slot.hasSpawnedProxy) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsProxySlotShowing(int panelId, int slotId)
|
||||
{
|
||||
if (m_Panels.Length == 0) return false;
|
||||
return (panelId == k_NonToggleablePanelId ? m_PersistentPanel : m_Panels[panelId]).dispenserSlots[slotId].hasSpawnedProxy;
|
||||
}
|
||||
|
||||
public void CheckForProxies(bool clearAllProxies = true)
|
||||
{
|
||||
foreach (var panel in m_Panels)
|
||||
{
|
||||
foreach (var slot in panel.dispenserSlots)
|
||||
{
|
||||
if (slot.currentInteractable == null && slot.dispenserSlotTransform != null)
|
||||
{
|
||||
if (slot.dispenserSlotTransform.parent.GetComponentInChildren<NetworkBaseInteractable>() != null)
|
||||
{
|
||||
slot.currentInteractable = slot.dispenserSlotTransform.parent.GetComponentInChildren<NetworkBaseInteractable>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (var slot in m_PersistentPanel.dispenserSlots)
|
||||
{
|
||||
if (slot.currentInteractable == null && slot.dispenserSlotTransform != null)
|
||||
{
|
||||
if (slot.dispenserSlotTransform.parent.GetComponentInChildren<NetworkBaseInteractable>() != null)
|
||||
{
|
||||
slot.currentInteractable = slot.dispenserSlotTransform.parent.GetComponentInChildren<NetworkBaseInteractable>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (clearAllProxies)
|
||||
ClearProxies();
|
||||
|
||||
OnProxiesUpdated?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
/// <summary>
|
||||
/// Represents a dispenser panel.
|
||||
/// </summary>
|
||||
public class DispenserPanel
|
||||
{
|
||||
/// <summary>
|
||||
/// The type of physics used by this panel.
|
||||
/// </summary>
|
||||
public string panelName;
|
||||
|
||||
/// <summary>
|
||||
/// The panel game object associated with the object dispenser.
|
||||
/// </summary>
|
||||
public GameObject panel;
|
||||
|
||||
/// <summary>
|
||||
/// The array of dispenser slots used by the object dispenser.
|
||||
/// </summary>
|
||||
[SerializeField] public DispenserSlot[] dispenserSlots;
|
||||
|
||||
public int panelId;
|
||||
|
||||
public void SpawnProxyPanel()
|
||||
{
|
||||
for (int i = 0; i < dispenserSlots.Length; i++)
|
||||
{
|
||||
dispenserSlots[i].SpawnProxy();
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearProxyPanel()
|
||||
{
|
||||
for (int i = 0; i < dispenserSlots.Length; i++)
|
||||
{
|
||||
dispenserSlots[i].ClearProxy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
/// <summary>
|
||||
/// Represents a dispenser slot.
|
||||
/// </summary>
|
||||
public class DispenserSlot
|
||||
{
|
||||
/// <summary>
|
||||
/// The transform of the dispenser slot.
|
||||
/// </summary>
|
||||
public Transform dispenserSlotTransform;
|
||||
|
||||
public NetworkBaseInteractable spawnableInteractablePrefab;
|
||||
|
||||
public NetworkObjectSpawner objectSpawner;
|
||||
public int panelId;
|
||||
public int slotId;
|
||||
|
||||
public bool freezeOnSpawn = true;
|
||||
public float distanceToSpawnNew = .5f;
|
||||
public float spawnCooldown = .5f;
|
||||
|
||||
internal float m_SpawnCooldownTimer = 0f;
|
||||
|
||||
|
||||
[SerializeField] public bool hasSpawnedProxy = false;
|
||||
/// <summary>
|
||||
/// The current network interactable object in the dispenser slot.
|
||||
/// </summary>
|
||||
[SerializeField] public NetworkBaseInteractable currentInteractable;
|
||||
public void ClearProxy()
|
||||
{
|
||||
if (currentInteractable == null) return;
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
UnityEngine.Object.Destroy(currentInteractable.gameObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(currentInteractable.gameObject);
|
||||
}
|
||||
hasSpawnedProxy = false;
|
||||
}
|
||||
|
||||
public void SpawnProxy()
|
||||
{
|
||||
if (currentInteractable != null) ClearProxy();
|
||||
NetworkBaseInteractable spawnedInteractable = UnityEngine.Object.Instantiate(spawnableInteractablePrefab, dispenserSlotTransform.position, dispenserSlotTransform.rotation);
|
||||
spawnedInteractable.transform.localScale = dispenserSlotTransform.localScale;
|
||||
spawnedInteractable.transform.parent = dispenserSlotTransform.transform;
|
||||
currentInteractable = spawnedInteractable;
|
||||
hasSpawnedProxy = true;
|
||||
}
|
||||
|
||||
public bool CheckInteractablePosition()
|
||||
{
|
||||
if (currentInteractable == null)
|
||||
return false;
|
||||
|
||||
float currentDistance = Vector3.Distance(currentInteractable.transform.position, dispenserSlotTransform.position);
|
||||
if (objectSpawner != null && currentDistance > 0.001f)
|
||||
objectSpawner.OnSpawnDistanceUpdated.Invoke(Mathf.Clamp01(currentDistance / distanceToSpawnNew));
|
||||
|
||||
return currentDistance > distanceToSpawnNew;
|
||||
}
|
||||
|
||||
public bool CanSpawn(float deltaTime)
|
||||
{
|
||||
if (currentInteractable != null) return false;
|
||||
if (m_SpawnCooldownTimer > 0)
|
||||
{
|
||||
UpdateCooldown(m_SpawnCooldownTimer - deltaTime);
|
||||
return false;
|
||||
}
|
||||
|
||||
UpdateCooldown(spawnCooldown);
|
||||
return true;
|
||||
}
|
||||
|
||||
void UpdateCooldown(float newTime)
|
||||
{
|
||||
m_SpawnCooldownTimer = newTime;
|
||||
if (objectSpawner != null)
|
||||
objectSpawner.OnSpawnCooldownUpdated.Invoke(Mathf.Clamp01(1 - (m_SpawnCooldownTimer / spawnCooldown)));
|
||||
}
|
||||
|
||||
public NetworkBaseInteractable SpawnInteractablePrefab(Transform spawnerTransform)
|
||||
{
|
||||
UpdateCooldown(spawnCooldown);
|
||||
NetworkBaseInteractable spawnedInteractable = UnityEngine.Object.Instantiate
|
||||
(
|
||||
spawnableInteractablePrefab,
|
||||
spawnerTransform.position,
|
||||
spawnerTransform.rotation
|
||||
);
|
||||
spawnedInteractable.transform.localScale = spawnerTransform.localScale;
|
||||
if (objectSpawner != null)
|
||||
objectSpawner.OnObjectSpawned.Invoke();
|
||||
|
||||
return spawnedInteractable;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1fc88142560a94e45b25b25e04ed4b0b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,31 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace XRMultiplayer
|
||||
{
|
||||
public class NetworkObjectSpawner : MonoBehaviour
|
||||
{
|
||||
public UnityEvent<float> OnSpawnDistanceUpdated;
|
||||
public UnityEvent<float> OnSpawnCooldownUpdated;
|
||||
public UnityEvent OnObjectSpawned;
|
||||
|
||||
public Renderer fadeRenderer;
|
||||
|
||||
Vector2 minMax = new Vector2(-1.0f, -0.25f);
|
||||
|
||||
public void UpdateDistance(float distance)
|
||||
{
|
||||
fadeRenderer.material.SetFloat("_FadeOffset", Mathf.Lerp(minMax.x, minMax.y, distance));
|
||||
if (distance >= 1.0f)
|
||||
{
|
||||
fadeRenderer.gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetDistance()
|
||||
{
|
||||
fadeRenderer.material.SetFloat("_FadeOffset", minMax.x);
|
||||
fadeRenderer.gameObject.SetActive(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4f7b90925e8011b46b35eebd00c5e38d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user