Skip to content

Commit 6267a3e

Browse files
update
Renaming ObjectController to ComponentController. Added some additional validation checking and handling. Updated XML API.
1 parent 43906be commit 6267a3e

File tree

4 files changed

+236
-121
lines changed

4 files changed

+236
-121
lines changed

com.unity.netcode.gameobjects/Runtime/Components/Helpers/AttachableBehaviour.cs

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,50 @@
11
using System;
2+
#if UNITY_EDITOR
3+
using UnityEditor;
4+
#endif
25
using UnityEngine;
36

47
namespace Unity.Netcode.Components
58
{
69
/// <summary>
7-
/// Handles parenting the <see cref="GameObject"/> that is a child under a root network prefab
8-
/// without having to use the same <see cref="NetworkObject"/> parenting rules.
10+
/// Handles parenting of the <see cref="GameObject"/> this component is attached to and is a nested <see cref="NetworkBehaviour"/>.<br />
11+
/// The <see cref="GameObject"/> can reside under a parent <see cref="NetworkObject"/> or some higher generational parent.
912
/// </summary>
1013
public class AttachableBehaviour : NetworkBehaviour
1114
{
15+
#if UNITY_EDITOR
16+
/// <inheritdoc/>
17+
/// <remarks>
18+
/// In the event an <see cref="AttachableBehaviour"/> is placed on the same <see cref="GameObject"/>
19+
/// as the <see cref="NetworkObject"/>, this will automatically create a child and add an
20+
/// <see cref="AttachableBehaviour"/> to that.
21+
/// </remarks>
22+
protected virtual void OnValidate()
23+
{
24+
var networkObject = gameObject.GetComponentInParent<NetworkObject>();
25+
if (!networkObject)
26+
{
27+
networkObject = gameObject.GetComponent<NetworkObject>();
28+
}
29+
if (networkObject && networkObject.gameObject == gameObject)
30+
{
31+
Debug.LogWarning($"[{name}][{nameof(AttachableBehaviour)}] Cannot be placed on the same {nameof(GameObject)} as the {nameof(NetworkObject)}!");
32+
// Wait for the next editor update to create a nested child and add the AttachableBehaviour
33+
EditorApplication.update += CreatedNestedChild;
34+
}
35+
}
36+
37+
private void CreatedNestedChild()
38+
{
39+
EditorApplication.update -= CreatedNestedChild;
40+
var childGameObject = new GameObject($"{name}-Child");
41+
childGameObject.transform.parent = transform;
42+
childGameObject.AddComponent<AttachableBehaviour>();
43+
Debug.Log($"[{name}][Created Child] Adding {nameof(AttachableBehaviour)} to newly created child {childGameObject.name}.");
44+
DestroyImmediate(this);
45+
}
46+
#endif
47+
1248
/// <summary>
1349
/// Invoked just prior to the parent being applied.
1450
/// </summary>
@@ -133,6 +169,7 @@ public void ApplyParent(NetworkBehaviour parent)
133169
Debug.LogError($"[{name}][Not Authority] Client-{NetworkManager.LocalClientId} is not the authority!");
134170
return;
135171
}
172+
136173
// Notify any subscriptions
137174
ParentIsBeingApplied?.Invoke(parent);
138175

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
using System.Collections.Generic;
2+
using System.Reflection;
3+
using UnityEngine;
4+
using Object = UnityEngine.Object;
5+
6+
namespace Unity.Netcode.Components
7+
{
8+
/// <summary>
9+
/// Handles enabling or disabling commonly used components, behaviours, RenderMeshes, etc.<br />
10+
/// Anything that derives from <see cref="Component"/> and has an enabled property can be added
11+
/// to the list of objects.<br />
12+
/// <see cref="NetworkBehaviour"/> derived components are not allowed and will be automatically removed.
13+
/// </summary>
14+
/// <remarks>
15+
/// This will synchronize the enabled or disabled state of the <see cref="Component"/>s with
16+
/// connected and late joining clients.
17+
/// </remarks>
18+
public class ComponentController : NetworkBehaviour
19+
{
20+
/// <summary>
21+
/// Determines whether the selected <see cref="Components"/>s will start enabled or disabled when spawned.
22+
/// </summary>
23+
[Tooltip("The initial state of the components when spawned.")]
24+
public bool InitialState = true;
25+
26+
/// <summary>
27+
/// The list of <see cref="Components"/>s to be enabled and disabled.
28+
/// </summary>
29+
[Tooltip("The list of components to control. You can drag and drop an entire GameObject on this to include all components.")]
30+
public List<Object> Components;
31+
32+
private Dictionary<Component, PropertyInfo> m_ValidComponents = new Dictionary<Component, PropertyInfo>();
33+
private NetworkVariable<bool> m_IsEnabled = new NetworkVariable<bool>();
34+
35+
#if UNITY_EDITOR
36+
/// <inheritdoc/>
37+
/// <remarks>
38+
/// Checks for invalid <see cref="Object"/> entries.
39+
/// </remarks>
40+
protected virtual void OnValidate()
41+
{
42+
if (Components == null || Components.Count == 0)
43+
{
44+
return;
45+
}
46+
47+
var gameObjectsToScan = new List<GameObject>();
48+
for (int i = Components.Count - 1; i >= 0; i--)
49+
{
50+
if (Components[i] == null)
51+
{
52+
continue;
53+
}
54+
var componentType = Components[i].GetType();
55+
if (componentType == typeof(GameObject))
56+
{
57+
gameObjectsToScan.Add(Components[i] as GameObject);
58+
Components.RemoveAt(i);
59+
continue;
60+
}
61+
62+
if (componentType.IsSubclassOf(typeof(NetworkBehaviour)))
63+
{
64+
Debug.LogWarning($"Removing {Components[i].name} since {nameof(NetworkBehaviour)}s are not allowed to be controlled by this component.");
65+
Components.RemoveAt(i);
66+
continue;
67+
}
68+
69+
var propertyInfo = Components[i].GetType().GetProperty("enabled", BindingFlags.Instance | BindingFlags.Public);
70+
if (propertyInfo == null && propertyInfo.PropertyType != typeof(bool))
71+
{
72+
Debug.LogWarning($"{Components[i].name} does not contain a public enabled property! (Removing)");
73+
Components.RemoveAt(i);
74+
}
75+
}
76+
77+
foreach (var entry in gameObjectsToScan)
78+
{
79+
var components = entry.GetComponents<Component>();
80+
foreach (var component in components)
81+
{
82+
// Ignore any NetworkBehaviour derived components
83+
if (component.GetType().IsSubclassOf(typeof(NetworkBehaviour)))
84+
{
85+
continue;
86+
}
87+
88+
var propertyInfo = component.GetType().GetProperty("enabled", BindingFlags.Instance | BindingFlags.Public);
89+
if (propertyInfo != null && propertyInfo.PropertyType == typeof(bool))
90+
{
91+
Components.Add(component);
92+
}
93+
}
94+
}
95+
gameObjectsToScan.Clear();
96+
}
97+
#endif
98+
99+
/// <inheritdoc/>
100+
/// <remarks>
101+
/// Also checks to assure all <see cref="Component"/> entries are valid and creates a final table of
102+
/// <see cref="Component"/>s paired to their <see cref="PropertyInfo"/>.
103+
/// </remarks>
104+
protected virtual void Awake()
105+
{
106+
var emptyEntries = 0;
107+
foreach (var someObject in Components)
108+
{
109+
if (someObject == null)
110+
{
111+
emptyEntries++;
112+
continue;
113+
}
114+
var propertyInfo = someObject.GetType().GetProperty("enabled", BindingFlags.Instance | BindingFlags.Public);
115+
if (propertyInfo != null && propertyInfo.PropertyType == typeof(bool))
116+
{
117+
m_ValidComponents.Add(someObject as Component, propertyInfo);
118+
}
119+
else
120+
{
121+
Debug.LogWarning($"{name} does not contain a public enable property! (Ignoring)");
122+
}
123+
}
124+
if (emptyEntries > 0)
125+
{
126+
Debug.LogWarning($"{name} has {emptyEntries} emtpy(null) entries in the {nameof(Components)} list!");
127+
}
128+
else
129+
{
130+
Debug.Log($"{name} has {m_ValidComponents.Count} valid {nameof(Component)} entries.");
131+
}
132+
}
133+
134+
/// <inheritdoc/>
135+
public override void OnNetworkSpawn()
136+
{
137+
if (HasAuthority)
138+
{
139+
m_IsEnabled.Value = InitialState;
140+
}
141+
base.OnNetworkSpawn();
142+
}
143+
144+
/// <inheritdoc/>
145+
/// <remarks>
146+
/// Assures all instances subscribe to the internal <see cref="NetworkVariable{T}"/> of type
147+
/// <see cref="bool"/> that synchronizes all instances when <see cref="Object"/>s are enabled
148+
/// or disabled.
149+
/// </remarks>
150+
protected override void OnNetworkPostSpawn()
151+
{
152+
m_IsEnabled.OnValueChanged += OnEnabledChanged;
153+
ApplyEnabled(m_IsEnabled.Value);
154+
base.OnNetworkPostSpawn();
155+
}
156+
157+
/// <inheritdoc/>
158+
public override void OnNetworkDespawn()
159+
{
160+
m_IsEnabled.OnValueChanged -= OnEnabledChanged;
161+
base.OnNetworkDespawn();
162+
}
163+
164+
private void OnEnabledChanged(bool previous, bool current)
165+
{
166+
ApplyEnabled(current);
167+
}
168+
169+
private void ApplyEnabled(bool enabled)
170+
{
171+
foreach (var entry in m_ValidComponents)
172+
{
173+
entry.Value.SetValue(entry.Key, enabled);
174+
}
175+
}
176+
177+
/// <summary>
178+
/// Invoke on the authority side to enable or disable the <see cref="Object"/>s.
179+
/// </summary>
180+
/// <param name="isEnabled">true = enabled | false = disabled</param>
181+
public void SetEnabled(bool isEnabled)
182+
{
183+
if (!IsSpawned)
184+
{
185+
Debug.Log($"[{name}] Must be spawned to use {nameof(SetEnabled)}!");
186+
return;
187+
}
188+
189+
if (!HasAuthority)
190+
{
191+
Debug.Log($"[Client-{NetworkManager.LocalClientId}] Attempting to invoke {nameof(SetEnabled)} without authority!");
192+
return;
193+
}
194+
m_IsEnabled.Value = isEnabled;
195+
}
196+
}
197+
}

com.unity.netcode.gameobjects/Runtime/Components/Helpers/ObjectController.cs

Lines changed: 0 additions & 119 deletions
This file was deleted.

0 commit comments

Comments
 (0)