diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md index dcfa2464e2..ea2c6243b9 100644 --- a/com.unity.netcode.gameobjects/CHANGELOG.md +++ b/com.unity.netcode.gameobjects/CHANGELOG.md @@ -9,17 +9,23 @@ Additional documentation and release notes are available at [Multiplayer Documen ## [Unreleased] ### Added + +- Added protected method `NetworkBehaviour.OnSynchronize` which is invoked during the initial `NetworkObject` synchronization process. This provides users the ability to include custom serialization information that will be applied to the `NetworkBehaviour` prior to the `NetworkObject` being spawned. (#2298) - Added support for different versions of the SDK to talk to each other in circumstances where changes permit it. Starting with this version and into future versions, patch versions should be compatible as long as the minor version is the same. (#2290) - Added `NetworkObject` auto-add helper and Multiplayer Tools install reminder settings to Project Settings. (#2285) -- Added `public string DisconnectReason` getter to `NetworkManager` and `string Reason` to `ConnectionApprovalResponse`. Allows connection approval to communicate back a reason. Also added `public void DisconnectClient(ulong clientId, string reason)` allowing setting a disconnection reason, when explicitly disconnecting a client. +- Added `public string DisconnectReason` getter to `NetworkManager` and `string Reason` to `ConnectionApprovalResponse`. Allows connection approval to communicate back a reason. Also added `public void DisconnectClient(ulong clientId, string reason)` allowing setting a disconnection reason, when explicitly disconnecting a client. (#2280) ### Changed - Changed 3rd-party `XXHash` (32 & 64) implementation with an in-house reimplementation (#2310) +- When `NetworkConfig.EnsureNetworkVariableLengthSafety` is disabled `NetworkVariable` fields do not write the additional `ushort` size value (_which helps to reduce the total synchronization message size_), but when enabled it still writes the additional `ushort` value. (#2298) - Optimized bandwidth usage by encoding most integer fields using variable-length encoding. (#2276) ### Fixed +- Fixed issue where `NetworkTransform` components nested under a parent with a `NetworkObject` component (i.e. network prefab) would not have their associated `GameObject`'s transform synchronized. (#2298) +- Fixed issue where `NetworkObject`s that failed to instantiate could cause the entire synchronization pipeline to be disrupted/halted for a connecting client. (#2298) +- Fixed issue where in-scene placed `NetworkObject`s nested under a `GameObject` would be added to the orphaned children list causing continual console warning log messages. (#2298) - Custom messages are now properly received by the local client when they're sent while running in host mode. (#2296) - Fixed issue where the host would receive more than one event completed notification when loading or unloading a scene only when no clients were connected. (#2292) - Fixed an issue in `UnityTransport` where an error would be logged if the 'Use Encryption' flag was enabled with a Relay configuration that used a secure protocol. (#2289) diff --git a/com.unity.netcode.gameobjects/Components/NetworkTransform.cs b/com.unity.netcode.gameobjects/Components/NetworkTransform.cs index f8d4dbd6a7..d9e8393f34 100644 --- a/com.unity.netcode.gameobjects/Components/NetworkTransform.cs +++ b/com.unity.netcode.gameobjects/Components/NetworkTransform.cs @@ -451,6 +451,33 @@ internal NetworkTransformState GetLastSentState() return m_LastSentState; } + /// + /// This is invoked when a new client joins (server and client sides) + /// Server Side: Serializes as if we were teleporting (everything is sent via NetworkTransformState) + /// Client Side: Adds the interpolated state which applies the NetworkTransformState as well + /// + protected override void OnSynchronize(ref BufferSerializer serializer) + { + // We don't need to synchronize NetworkTransforms that are on the same + // GameObject as the NetworkObject. + if (NetworkObject.gameObject == gameObject) + { + return; + } + var synchronizationState = new NetworkTransformState(); + if (serializer.IsWriter) + { + synchronizationState.IsTeleportingNextFrame = true; + ApplyTransformToNetworkStateWithInfo(ref synchronizationState, m_CachedNetworkManager.LocalTime.Time, transform); + synchronizationState.NetworkSerialize(serializer); + } + else + { + synchronizationState.NetworkSerialize(serializer); + AddInterpolatedState(synchronizationState); + } + } + /// /// This will try to send/commit the current transform delta states (if any) /// diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs index daa8b30ad0..1a43947852 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs @@ -763,6 +763,14 @@ internal void MarkVariablesDirty(bool dirty) } } + /// + /// Synchronizes by setting only the NetworkVariable field values that the client has permission to read. + /// Note: This is only invoked when first synchronizing a NetworkBehaviour (i.e. late join or spawned NetworkObject) + /// + /// + /// When NetworkConfig.EnsureNetworkVariableLengthSafety is enabled each NetworkVariable field will be preceded + /// by the number of bytes written for that specific field. + /// internal void WriteNetworkVariableData(FastBufferWriter writer, ulong targetClientId) { if (NetworkVariableFields.Count == 0) @@ -772,32 +780,47 @@ internal void WriteNetworkVariableData(FastBufferWriter writer, ulong targetClie for (int j = 0; j < NetworkVariableFields.Count; j++) { - bool canClientRead = NetworkVariableFields[j].CanClientRead(targetClientId); - if (canClientRead) + if (NetworkVariableFields[j].CanClientRead(targetClientId)) { - var writePos = writer.Position; - // Note: This value can't be packed because we don't know how large it will be in advance - // we reserve space for it, then write the data, then come back and fill in the space - // to pack here, we'd have to write data to a temporary buffer and copy it in - which - // isn't worth possibly saving one byte if and only if the data is less than 63 bytes long... - // The way we do packing, any value > 63 in a ushort will use the full 2 bytes to represent. - writer.WriteValueSafe((ushort)0); - var startPos = writer.Position; - NetworkVariableFields[j].WriteField(writer); - var size = writer.Position - startPos; - writer.Seek(writePos); - writer.WriteValueSafe((ushort)size); - writer.Seek(startPos + size); + if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) + { + var writePos = writer.Position; + // Note: This value can't be packed because we don't know how large it will be in advance + // we reserve space for it, then write the data, then come back and fill in the space + // to pack here, we'd have to write data to a temporary buffer and copy it in - which + // isn't worth possibly saving one byte if and only if the data is less than 63 bytes long... + // The way we do packing, any value > 63 in a ushort will use the full 2 bytes to represent. + writer.WriteValueSafe((ushort)0); + var startPos = writer.Position; + NetworkVariableFields[j].WriteField(writer); + var size = writer.Position - startPos; + writer.Seek(writePos); + writer.WriteValueSafe((ushort)size); + writer.Seek(startPos + size); + } + else + { + NetworkVariableFields[j].WriteField(writer); + } } - else + else // Only if EnsureNetworkVariableLengthSafety, otherwise just skip + if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) { writer.WriteValueSafe((ushort)0); } } } - internal void SetNetworkVariableData(FastBufferReader reader) + /// + /// Synchronizes by setting only the NetworkVariable field values that the client has permission to read. + /// Note: This is only invoked when first synchronizing a NetworkBehaviour (i.e. late join or spawned NetworkObject) + /// + /// + /// When NetworkConfig.EnsureNetworkVariableLengthSafety is enabled each NetworkVariable field will be preceded + /// by the number of bytes written for that specific field. + /// + internal void SetNetworkVariableData(FastBufferReader reader, ulong clientId) { if (NetworkVariableFields.Count == 0) { @@ -806,13 +829,23 @@ internal void SetNetworkVariableData(FastBufferReader reader) for (int j = 0; j < NetworkVariableFields.Count; j++) { - reader.ReadValueSafe(out ushort varSize); - if (varSize == 0) + var varSize = (ushort)0; + var readStartPos = 0; + if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) + { + reader.ReadValueSafe(out varSize); + if (varSize == 0) + { + continue; + } + readStartPos = reader.Position; + } + else // If the client cannot read this field, then skip it + if (!NetworkVariableFields[j].CanClientRead(clientId)) { continue; } - var readStartPos = reader.Position; NetworkVariableFields[j].ReadField(reader); if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) @@ -849,6 +882,138 @@ protected NetworkObject GetNetworkObject(ulong networkId) return NetworkManager.SpawnManager.SpawnedObjects.TryGetValue(networkId, out NetworkObject networkObject) ? networkObject : null; } + /// + /// Override this method if your derived NetworkBehaviour requires custom synchronization data. + /// Note: Use of this method is only for the initial client synchronization of NetworkBehaviours + /// and will increase the payload size for client synchronization and dynamically spawned + /// s. + /// + /// + /// When serializing (writing) this will be invoked during the client synchronization period and + /// when spawning new NetworkObjects. + /// When deserializing (reading), this will be invoked prior to the NetworkBehaviour's associated + /// NetworkObject being spawned. + /// + /// The serializer to use to read and write the data. + /// + /// Either BufferSerializerReader or BufferSerializerWriter, depending whether the serializer + /// is in read mode or write mode. + /// + protected virtual void OnSynchronize(ref BufferSerializer serializer) where T : IReaderWriter + { + + } + + /// + /// Internal method that determines if a NetworkBehaviour has additional synchronization data to + /// be synchronized when first instantiated prior to its associated NetworkObject being spawned. + /// + /// + /// This includes try-catch blocks to recover from exceptions that might occur and continue to + /// synchronize any remaining NetworkBehaviours. + /// + /// true if it wrote synchronization data and false if it did not + internal bool Synchronize(ref BufferSerializer serializer) where T : IReaderWriter + { + if (serializer.IsWriter) + { + // Get the writer to handle seeking and determining how many bytes were written + var writer = serializer.GetFastBufferWriter(); + // Save our position before we attempt to write anything so we can seek back to it (i.e. error occurs) + var positionBeforeWrite = writer.Position; + writer.WriteValueSafe(NetworkBehaviourId); + + // Save our position where we will write the final size being written so we can skip over it in the + // event an exception occurs when deserializing. + var sizePosition = writer.Position; + writer.WriteValueSafe((ushort)0); + + // Save our position before synchronizing to determine how much was written + var positionBeforeSynchronize = writer.Position; + var threwException = false; + try + { + OnSynchronize(ref serializer); + } + catch (Exception ex) + { + threwException = true; + if (NetworkManager.LogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"{name} threw an exception during synchronization serialization, this {nameof(NetworkBehaviour)} is being skipped and will not be synchronized!"); + if (NetworkManager.LogLevel == LogLevel.Developer) + { + NetworkLog.LogError($"{ex.Message}\n {ex.StackTrace}"); + } + } + } + var finalPosition = writer.Position; + + // If we wrote nothing then skip writing anything for this NetworkBehaviour + if (finalPosition == positionBeforeSynchronize || threwException) + { + writer.Seek(positionBeforeWrite); + return false; + } + else + { + // Write the number of bytes serialized to handle exceptions on the deserialization side + var bytesWritten = finalPosition - positionBeforeSynchronize; + writer.Seek(sizePosition); + writer.WriteValueSafe((ushort)bytesWritten); + writer.Seek(finalPosition); + } + return true; + } + else + { + var reader = serializer.GetFastBufferReader(); + // We will always read the expected byte count + reader.ReadValueSafe(out ushort expectedBytesToRead); + + // Save our position before we begin synchronization deserialization + var positionBeforeSynchronize = reader.Position; + var synchronizationError = false; + try + { + // Invoke synchronization + OnSynchronize(ref serializer); + } + catch (Exception ex) + { + if (NetworkManager.LogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"{name} threw an exception during synchronization deserialization, this {nameof(NetworkBehaviour)} is being skipped and will not be synchronized!"); + if (NetworkManager.LogLevel == LogLevel.Developer) + { + NetworkLog.LogError($"{ex.Message}\n {ex.StackTrace}"); + } + } + synchronizationError = true; + } + + var totalBytesRead = reader.Position - positionBeforeSynchronize; + if (totalBytesRead != expectedBytesToRead) + { + if (NetworkManager.LogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"{name} read {totalBytesRead} bytes but was expected to read {expectedBytesToRead} bytes during synchronization deserialization! This {nameof(NetworkBehaviour)} is being skipped and will not be synchronized!"); + } + synchronizationError = true; + } + + // Skip over the entry if deserialization fails + if (synchronizationError) + { + var skipToPosition = positionBeforeSynchronize + expectedBytesToRead; + reader.Seek(skipToPosition); + return false; + } + return true; + } + } + + /// /// Invoked when the the is attached to. /// NOTE: If you override this, you will want to always invoke this base class version of this diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index 5bd7411bd0..122b6f1ee4 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -1008,13 +1008,17 @@ internal static void VerifyParentingStatus() } } } - internal void SetNetworkVariableData(FastBufferReader reader) + + /// + /// Only invoked during first synchronization of a NetworkObject (late join or newly spawned) + /// + internal void SetNetworkVariableData(FastBufferReader reader, ulong clientId) { for (int i = 0; i < ChildNetworkBehaviours.Count; i++) { var behaviour = ChildNetworkBehaviours[i]; behaviour.InitializeVariables(); - behaviour.SetNetworkVariableData(reader); + behaviour.SetNetworkVariableData(reader, clientId); } } @@ -1168,16 +1172,17 @@ public void Serialize(FastBufferWriter writer) } // In-Scene NetworkObjects are uniquely identified NetworkPrefabs defined by their - // NetworkSceneHandle and GlobalObjectIdHash. Since each loaded scene has a unique - // handle, it provides us with a unique and persistent "scene prefab asset" instance. - // This is only set on in-scene placed NetworkObjects to reduce the over-all packet - // sizes for dynamically spawned NetworkObjects. + // NetworkSceneHandle and GlobalObjectIdHash. Client-side NetworkSceneManagers use + // this to locate their local instance of the in-scene placed NetworkObject instance. + // Only written for in-scene placed NetworkObjects. if (IsSceneObject) { writer.WriteValue(OwnerObject.GetSceneOriginHandle()); } - OwnerObject.WriteNetworkVariableData(writer, TargetClientId); + // Synchronize NetworkVariables and NetworkBehaviours + var bufferSerializer = new BufferSerializer(new BufferSerializerWriter(writer)); + OwnerObject.SynchronizeNetworkBehaviours(ref bufferSerializer, TargetClientId); } public void Deserialize(FastBufferReader reader) @@ -1213,10 +1218,9 @@ public void Deserialize(FastBufferReader reader) } // In-Scene NetworkObjects are uniquely identified NetworkPrefabs defined by their - // NetworkSceneHandle and GlobalObjectIdHash. Since each loaded scene has a unique - // handle, it provides us with a unique and persistent "scene prefab asset" instance. - // Client-side NetworkSceneManagers use this to locate their local instance of the - // NetworkObject instance. + // NetworkSceneHandle and GlobalObjectIdHash. Client-side NetworkSceneManagers use + // this to locate their local instance of the in-scene placed NetworkObject instance. + // Only read for in-scene placed NetworkObjects if (IsSceneObject) { reader.ReadValue(out NetworkSceneHandle); @@ -1232,6 +1236,78 @@ internal void PostNetworkVariableWrite() } } + /// + /// Handles synchronizing NetworkVariables and custom synchronization data for NetworkBehaviours. + /// + /// + /// This is where we determine how much data is written after the associated NetworkObject in order to recover + /// from a failed instantiated NetworkObject without completely disrupting client synchronization. + /// + internal void SynchronizeNetworkBehaviours(ref BufferSerializer serializer, ulong targetClientId = 0) where T : IReaderWriter + { + if (serializer.IsWriter) + { + var writer = serializer.GetFastBufferWriter(); + var positionBeforeSynchronizing = writer.Position; + writer.WriteValueSafe((ushort)0); + var sizeToSkipCalculationPosition = writer.Position; + + // Synchronize NetworkVariables + WriteNetworkVariableData(writer, targetClientId); + // Reserve the NetworkBehaviour synchronization count position + var networkBehaviourCountPosition = writer.Position; + writer.WriteValueSafe((byte)0); + + // Parse through all NetworkBehaviours and any that return true + // had additional synchronization data written. + // (See notes for reading/deserialization below) + var synchronizationCount = (byte)0; + foreach (var childBehaviour in ChildNetworkBehaviours) + { + if (childBehaviour.Synchronize(ref serializer)) + { + synchronizationCount++; + } + } + + var currentPosition = writer.Position; + // Write the total number of bytes written for NetworkVariable and NetworkBehaviour + // synchronization. + writer.Seek(positionBeforeSynchronizing); + // We want the size of everything after our size to skip calculation position + var size = (ushort)(currentPosition - sizeToSkipCalculationPosition); + writer.WriteValueSafe(size); + // Write the number of NetworkBehaviours synchronized + writer.Seek(networkBehaviourCountPosition); + writer.WriteValueSafe(synchronizationCount); + // seek back to the position after writing NetworkVariable and NetworkBehaviour + // synchronization data. + writer.Seek(currentPosition); + } + else + { + var reader = serializer.GetFastBufferReader(); + + reader.ReadValueSafe(out ushort sizeOfSynchronizationData); + var seekToEndOfSynchData = reader.Position + sizeOfSynchronizationData; + // Apply the network variable synchronization data + SetNetworkVariableData(reader, targetClientId); + // Read the number of NetworkBehaviours to synchronize + reader.ReadValueSafe(out byte numberSynchronized); + var networkBehaviourId = (ushort)0; + + // If a NetworkBehaviour writes synchronization data, it will first + // write its NetworkBehaviourId so when deserializing the client-side + // can find the right NetworkBehaviour to deserialize the synchronization data. + for (int i = 0; i < numberSynchronized; i++) + { + serializer.SerializeValue(ref networkBehaviourId); + var networkBehaviour = GetNetworkBehaviourAtOrderIndex(networkBehaviourId); + networkBehaviour.Synchronize(ref serializer); + } + } + } + internal SceneObject GetMessageSceneObject(ulong targetClientId) { var obj = new SceneObject @@ -1318,10 +1394,10 @@ internal SceneObject GetMessageSceneObject(ulong targetClientId) /// when the client is approved or during a scene transition /// /// Deserialized scene object data - /// reader for the NetworkVariable data + /// FastBufferReader for the NetworkVariable data /// NetworkManager instance /// optional to use NetworkObject deserialized - internal static NetworkObject AddSceneObject(in SceneObject sceneObject, FastBufferReader variableData, NetworkManager networkManager) + internal static NetworkObject AddSceneObject(in SceneObject sceneObject, FastBufferReader reader, NetworkManager networkManager) { //Attempt to create a local NetworkObject var networkObject = networkManager.SpawnManager.CreateLocalNetworkObject(sceneObject); @@ -1329,18 +1405,36 @@ internal static NetworkObject AddSceneObject(in SceneObject sceneObject, FastBuf if (networkObject == null) { // Log the error that the NetworkObject failed to construct - Debug.LogError($"Failed to spawn {nameof(NetworkObject)} for Hash {sceneObject.Hash}."); + if (networkManager.LogLevel <= LogLevel.Normal) + { + NetworkLog.LogError($"Failed to spawn {nameof(NetworkObject)} for Hash {sceneObject.Hash}."); + } - // If we failed to load this NetworkObject, then skip past the network variable data - variableData.ReadValueSafe(out ushort varSize); - variableData.Seek(variableData.Position + varSize); + try + { + // If we failed to load this NetworkObject, then skip past the Network Variable and (if any) synchronization data + reader.ReadValueSafe(out ushort networkBehaviourSynchronizationDataLength); + reader.Seek(reader.Position + networkBehaviourSynchronizationDataLength); + } + catch (Exception ex) + { + Debug.LogException(ex); + } // We have nothing left to do here. return null; } + // This will get set again when the NetworkObject is spawned locally, but we set it here ahead of spawning + // in order to be able to determine which NetworkVariables the client will be allowed to read. + networkObject.OwnerClientId = sceneObject.OwnerClientId; + + // Synchronize NetworkBehaviours + var bufferSerializer = new BufferSerializer(new BufferSerializerReader(reader)); + networkObject.SynchronizeNetworkBehaviours(ref bufferSerializer, networkManager.LocalClientId); + // Spawn the NetworkObject - networkManager.SpawnManager.SpawnNetworkObjectLocally(networkObject, sceneObject, variableData, false); + networkManager.SpawnManager.SpawnNetworkObjectLocally(networkObject, sceneObject, false); return networkObject; } diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs index 0b8936d332..8ccb84f725 100644 --- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs +++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs @@ -444,13 +444,13 @@ internal void WriteSceneSynchronizationData(FastBufferWriter writer) int totalBytes = 0; // Write the number of NetworkObjects we are serializing - BytePacker.WriteValuePacked(writer, m_NetworkObjectsSync.Count); + writer.WriteValueSafe(m_NetworkObjectsSync.Count); + // Serialize all NetworkObjects that are spawned for (var i = 0; i < m_NetworkObjectsSync.Count; ++i) { var noStart = writer.Position; var sceneObject = m_NetworkObjectsSync[i].GetMessageSceneObject(TargetClientId); - BytePacker.WriteValuePacked(writer, m_NetworkObjectsSync[i].GetSceneOriginHandle()); sceneObject.Serialize(writer); var noStop = writer.Position; totalBytes += (int)(noStop - noStart); @@ -462,8 +462,8 @@ internal void WriteSceneSynchronizationData(FastBufferWriter writer) for (var i = 0; i < m_DespawnedInSceneObjectsSync.Count; ++i) { var noStart = writer.Position; - BytePacker.WriteValuePacked(writer, m_DespawnedInSceneObjectsSync[i].GetSceneOriginHandle()); - BytePacker.WriteValuePacked(writer, m_DespawnedInSceneObjectsSync[i].GlobalObjectIdHash); + writer.WriteValueSafe(m_DespawnedInSceneObjectsSync[i].GetSceneOriginHandle()); + writer.WriteValueSafe(m_DespawnedInSceneObjectsSync[i].GlobalObjectIdHash); var noStop = writer.Position; totalBytes += (int)(noStop - noStart); } @@ -497,8 +497,6 @@ internal void SerializeScenePlacedObjects(FastBufferWriter writer) { if (keyValuePairBySceneHandle.Value.Observers.Contains(TargetClientId)) { - // Write our server relative scene handle for the NetworkObject being serialized - writer.WriteValueSafe(keyValuePairBySceneHandle.Key); // Serialize the NetworkObject var sceneObject = keyValuePairBySceneHandle.Value.GetMessageSceneObject(TargetClientId); sceneObject.Serialize(writer); @@ -512,8 +510,8 @@ internal void SerializeScenePlacedObjects(FastBufferWriter writer) // Write the scene handle and GlobalObjectIdHash value for (var i = 0; i < m_DespawnedInSceneObjectsSync.Count; ++i) { - BytePacker.WriteValuePacked(writer, m_DespawnedInSceneObjectsSync[i].GetSceneOriginHandle()); - BytePacker.WriteValuePacked(writer, m_DespawnedInSceneObjectsSync[i].GlobalObjectIdHash); + writer.WriteValueSafe(m_DespawnedInSceneObjectsSync[i].GetSceneOriginHandle()); + writer.WriteValueSafe(m_DespawnedInSceneObjectsSync[i].GlobalObjectIdHash); } var tailPosition = writer.Position; @@ -625,13 +623,15 @@ internal void DeserializeScenePlacedObjects() for (ushort i = 0; i < newObjectsCount; i++) { - InternalBuffer.ReadValueSafe(out int sceneHandle); - // Set our relative scene to the NetworkObject - m_NetworkManager.SceneManager.SetTheSceneBeingSynchronized(sceneHandle); - - // Deserialize the NetworkObject var sceneObject = new NetworkObject.SceneObject(); sceneObject.Deserialize(InternalBuffer); + + if (sceneObject.IsSceneObject) + { + // Set our relative scene to the NetworkObject + m_NetworkManager.SceneManager.SetTheSceneBeingSynchronized(sceneObject.NetworkSceneHandle); + } + NetworkObject.AddSceneObject(sceneObject, InternalBuffer, m_NetworkManager); } // Now deserialize the despawned in-scene placed NetworkObjects list (if any) @@ -772,8 +772,8 @@ private void DeserializeDespawnedInScenePlacedNetworkObjects() for (int i = 0; i < despawnedObjectsCount; i++) { // We just need to get the scene - ByteUnpacker.ReadValuePacked(InternalBuffer, out int networkSceneHandle); - ByteUnpacker.ReadValuePacked(InternalBuffer, out uint globalObjectIdHash); + InternalBuffer.ReadValueSafe(out int networkSceneHandle); + InternalBuffer.ReadValueSafe(out uint globalObjectIdHash); var sceneRelativeNetworkObjects = new Dictionary(); if (!sceneCache.ContainsKey(networkSceneHandle)) { @@ -848,24 +848,26 @@ internal void SynchronizeSceneNetworkObjects(NetworkManager networkManager) try { // Process all spawned NetworkObjects for this network session - ByteUnpacker.ReadValuePacked(InternalBuffer, out int newObjectsCount); - - + InternalBuffer.ReadValueSafe(out int newObjectsCount); for (int i = 0; i < newObjectsCount; i++) { - // We want to make sure for each NetworkObject we have the appropriate scene selected as the scene that is - // currently being synchronized. This assures in-scene placed NetworkObjects will use the right NetworkObject - // from the list of populated - ByteUnpacker.ReadValuePacked(InternalBuffer, out int handle); - m_NetworkManager.SceneManager.SetTheSceneBeingSynchronized(handle); - var sceneObject = new NetworkObject.SceneObject(); sceneObject.Deserialize(InternalBuffer); + // If the sceneObject is in-scene placed, then set the scene being synchronized + if (sceneObject.IsSceneObject) + { + m_NetworkManager.SceneManager.SetTheSceneBeingSynchronized(sceneObject.NetworkSceneHandle); + } var spawnedNetworkObject = NetworkObject.AddSceneObject(sceneObject, InternalBuffer, networkManager); - if (!m_NetworkObjectsSync.Contains(spawnedNetworkObject)) + + // If we failed to deserialize the NetowrkObject then don't add null to the list + if (spawnedNetworkObject != null) { - m_NetworkObjectsSync.Add(spawnedNetworkObject); + if (!m_NetworkObjectsSync.Contains(spawnedNetworkObject)) + { + m_NetworkObjectsSync.Add(spawnedNetworkObject); + } } } diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs index 945d6247e1..470317ee60 100644 --- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs @@ -454,8 +454,14 @@ internal NetworkObject CreateLocalNetworkObject(NetworkObject.SceneObject sceneO if (sceneObject.HasParent) { - // Go ahead and set network parenting properties - networkObject.SetNetworkParenting(parentNetworkId, worldPositionStays); + // Go ahead and set network parenting properties, if the latest parent is not set then pass in null + // (we always want to set worldPositionStays) + ulong? parentId = null; + if (sceneObject.IsLatestParentSet) + { + parentId = parentNetworkId; + } + networkObject.SetNetworkParenting(parentId, worldPositionStays); } @@ -495,8 +501,7 @@ internal void SpawnNetworkObjectLocally(NetworkObject networkObject, ulong netwo } // Ran on both server and client - internal void SpawnNetworkObjectLocally(NetworkObject networkObject, in NetworkObject.SceneObject sceneObject, - FastBufferReader variableData, bool destroyWithScene) + internal void SpawnNetworkObjectLocally(NetworkObject networkObject, in NetworkObject.SceneObject sceneObject, bool destroyWithScene) { if (networkObject == null) { @@ -508,8 +513,6 @@ internal void SpawnNetworkObjectLocally(NetworkObject networkObject, in NetworkO throw new SpawnStateException("Object is already spawned"); } - networkObject.SetNetworkVariableData(variableData); - SpawnNetworkObjectLocallyCommon(networkObject, sceneObject.NetworkObjectId, sceneObject.IsSceneObject, sceneObject.IsPlayerObject, sceneObject.OwnerClientId, destroyWithScene); } diff --git a/com.unity.netcode.gameobjects/TestHelpers/Runtime/NetcodeIntegrationTest.cs b/com.unity.netcode.gameobjects/TestHelpers/Runtime/NetcodeIntegrationTest.cs index e91eb28a1e..c924678fc4 100644 --- a/com.unity.netcode.gameobjects/TestHelpers/Runtime/NetcodeIntegrationTest.cs +++ b/com.unity.netcode.gameobjects/TestHelpers/Runtime/NetcodeIntegrationTest.cs @@ -431,8 +431,14 @@ private void ClientNetworkManagerPostStart(NetworkManager networkManager) networkManager.name = $"NetworkManager - Client - {networkManager.LocalClientId}"; Assert.NotNull(networkManager.LocalClient.PlayerObject, $"{nameof(StartServerAndClients)} detected that client {networkManager.LocalClientId} does not have an assigned player NetworkObject!"); + // Go ahead and create an entry for this new client + if (!m_PlayerNetworkObjects.ContainsKey(networkManager.LocalClientId)) + { + m_PlayerNetworkObjects.Add(networkManager.LocalClientId, new Dictionary()); + } + // Get all player instances for the current client NetworkManager instance - var clientPlayerClones = Object.FindObjectsOfType().Where((c) => c.IsPlayerObject && c.OwnerClientId == networkManager.LocalClientId); + var clientPlayerClones = Object.FindObjectsOfType().Where((c) => c.IsPlayerObject && c.OwnerClientId == networkManager.LocalClientId).ToList(); // Add this player instance to each client player entry foreach (var playerNetworkObject in clientPlayerClones) { @@ -446,6 +452,16 @@ private void ClientNetworkManagerPostStart(NetworkManager networkManager) m_PlayerNetworkObjects[playerNetworkObject.NetworkManager.LocalClientId].Add(networkManager.LocalClientId, playerNetworkObject); } } + + // For late joining clients, add the remaining (if any) cloned versions of each client's player + clientPlayerClones = Object.FindObjectsOfType().Where((c) => c.IsPlayerObject && c.NetworkManager == networkManager).ToList(); + foreach (var playerNetworkObject in clientPlayerClones) + { + if (!m_PlayerNetworkObjects[networkManager.LocalClientId].ContainsKey(playerNetworkObject.OwnerClientId)) + { + m_PlayerNetworkObjects[networkManager.LocalClientId].Add(playerNetworkObject.OwnerClientId, playerNetworkObject); + } + } } protected void ClientNetworkManagerPostStartInit() diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs deleted file mode 100644 index 8781faf2bd..0000000000 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs +++ /dev/null @@ -1,233 +0,0 @@ -using System.Collections.Generic; -using UnityEngine; -using UnityEngine.SceneManagement; -using NUnit.Framework; -using Unity.Collections; -using Unity.Netcode.TestHelpers.Runtime; - -namespace Unity.Netcode.RuntimeTests -{ - public class NetworkObjectSceneSerializationTests - { - - /// - /// The purpose behind this test is to assure that in-scene NetworkObjects - /// that are serialized into a single stream (approval or switch scene this happens) - /// will continue to be processed even if one of the NetworkObjects is invalid. - /// - [Test] - public void NetworkObjectSceneSerializationFailure() - { - var networkObjectsToTest = new List(); - - var writer = new FastBufferWriter(1300, Allocator.Temp, 4096000); - var invalidNetworkObjectOffsets = new List(); - var invalidNetworkObjectIdCount = new List(); - var invalidNetworkObjects = new List(); - var invalidNetworkObjectFrequency = 3; - using (writer) - { - // Construct 50 NetworkObjects - for (int i = 0; i < 50; i++) - { - // Inject an invalid NetworkObject every [invalidNetworkObjectFrequency] entry - if ((i % invalidNetworkObjectFrequency) == 0) - { - // Create the invalid NetworkObject - var gameObject = new GameObject($"InvalidTestObject{i}"); - - Assert.IsNotNull(gameObject); - - var networkObject = gameObject.AddComponent(); - - Assert.IsNotNull(networkObject); - - var networkVariableComponent = gameObject.AddComponent(); - Assert.IsNotNull(networkVariableComponent); - - // Add invalid NetworkObject's starting position before serialization to handle trapping for the Debug.LogError message - // that we know will be thrown - invalidNetworkObjectOffsets.Add(writer.Position); - - networkObject.GlobalObjectIdHash = (uint)(i); - invalidNetworkObjectIdCount.Add(i); - - invalidNetworkObjects.Add(gameObject); - - writer.WriteValueSafe((int)networkObject.GetSceneOriginHandle()); - // Serialize the invalid NetworkObject - var sceneObject = networkObject.GetMessageSceneObject(0); - var prePosition = writer.Position; - sceneObject.Serialize(writer); - - Debug.Log( - $"Invalid {nameof(NetworkObject)} Size {writer.Position - prePosition}"); - - // Now adjust how frequent we will inject invalid NetworkObjects - invalidNetworkObjectFrequency = Random.Range(2, 5); - - } - else - { - // Create a valid NetworkObject - var gameObject = new GameObject($"TestObject{i}"); - - Assert.IsNotNull(gameObject); - - var networkObject = gameObject.AddComponent(); - - var networkVariableComponent = gameObject.AddComponent(); - Assert.IsNotNull(networkVariableComponent); - - Assert.IsNotNull(networkObject); - - networkObject.GlobalObjectIdHash = (uint)(i + 4096); - - networkObjectsToTest.Add(gameObject); - - writer.WriteValueSafe(networkObject.GetSceneOriginHandle()); - - // Handle populating the scenes loaded list - var scene = networkObject.gameObject.scene; - - if (!NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenesLoaded.ContainsKey( - scene.handle)) - { - NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenesLoaded - .Add(scene.handle, scene); - } - var handle = networkObject.GetSceneOriginHandle(); - // Since this is a unit test, we will fake the server to client handle lookup by just adding the same handle key and value - if (!NetworkManagerHelper.NetworkManagerObject.SceneManager.ServerSceneHandleToClientSceneHandle - .ContainsKey(handle)) - { - NetworkManagerHelper.NetworkManagerObject.SceneManager.ServerSceneHandleToClientSceneHandle - .Add(handle, handle); - } - - // Serialize the valid NetworkObject - var sceneObject = networkObject.GetMessageSceneObject(0); - sceneObject.Serialize(writer); - - if (!NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenePlacedObjects.ContainsKey( - networkObject.GlobalObjectIdHash)) - { - NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenePlacedObjects.Add( - networkObject.GlobalObjectIdHash, new Dictionary()); - } - - // Add this valid NetworkObject into the ScenePlacedObjects list - NetworkManagerHelper.NetworkManagerObject.SceneManager - .ScenePlacedObjects[networkObject.GlobalObjectIdHash] - .Add(SceneManager.GetActiveScene().handle, networkObject); - } - } - - var totalBufferSize = writer.Position; - - var reader = new FastBufferReader(writer, Allocator.Temp); - using (reader) - { - - var networkObjectsDeSerialized = new List(); - var currentLogLevel = NetworkManager.Singleton.LogLevel; - var invalidNetworkObjectCount = 0; - while (reader.Position != totalBufferSize) - { - // If we reach the point where we expect it to fail, then make sure we let TestRunner know it should expect this log error message - if (invalidNetworkObjectOffsets.Count > 0 && - reader.Position == invalidNetworkObjectOffsets[0]) - { - invalidNetworkObjectOffsets.RemoveAt(0); - - // Turn off Network Logging to avoid other errors that we know will happen after the below LogAssert.Expect message occurs. - NetworkManager.Singleton.LogLevel = LogLevel.Nothing; - - // Trap for this specific error message so we don't make Test Runner think we failed (it will fail on Debug.LogError) - UnityEngine.TestTools.LogAssert.Expect(LogType.Error, - $"Failed to spawn {nameof(NetworkObject)} for Hash {invalidNetworkObjectIdCount[invalidNetworkObjectCount]}."); - - invalidNetworkObjectCount++; - } - - - reader.ReadValueSafe(out int handle); - NetworkManagerHelper.NetworkManagerObject.SceneManager.SetTheSceneBeingSynchronized(handle); - var sceneObject = new NetworkObject.SceneObject(); - sceneObject.Deserialize(reader); - - var deserializedNetworkObject = NetworkObject.AddSceneObject(sceneObject, reader, - NetworkManagerHelper.NetworkManagerObject); - if (deserializedNetworkObject != null) - { - networkObjectsDeSerialized.Add(deserializedNetworkObject); - } - else - { - // Under this condition, we are expecting null (i.e. no NetworkObject instantiated) - // and will set our log level back to the original value to assure the valid NetworkObjects - // aren't causing any log Errors to occur - NetworkManager.Singleton.LogLevel = currentLogLevel; - } - } - - // Now validate all NetworkObjects returned against the original NetworkObjects we created - // after they validate, destroy the objects - foreach (var entry in networkObjectsToTest) - { - var entryNetworkObject = entry.GetComponent(); - Assert.IsTrue(networkObjectsDeSerialized.Contains(entryNetworkObject)); - Object.Destroy(entry); - } - } - } - - // Destroy the invalid network objects - foreach (var entry in invalidNetworkObjects) - { - Object.Destroy(entry); - } - } - - [SetUp] - public void Setup() - { - // Create, instantiate, and host - NetworkManagerHelper.StartNetworkManager(out NetworkManager networkManager, NetworkManagerHelper.NetworkManagerOperatingMode.None); - networkManager.NetworkConfig.EnableSceneManagement = true; - networkManager.StartHost(); - - } - - [TearDown] - public void TearDown() - { - // Stop, shutdown, and destroy - NetworkManagerHelper.ShutdownNetworkManager(); - } - } - - /// - /// A simple test class that will provide varying NetworkBuffer stream sizes - /// when the NetworkVariable is serialized - /// - public class NetworkBehaviourWithNetworkVariables : NetworkBehaviour - { - private const uint k_MinDataBlocks = 1; - private const uint k_MaxDataBlocks = 64; - - public NetworkList NetworkVariableData; - - private void Awake() - { - var dataBlocksAssigned = new List(); - var numberDataBlocks = Random.Range(k_MinDataBlocks, k_MaxDataBlocks); - for (var i = 0; i < numberDataBlocks; i++) - { - dataBlocksAssigned.Add((ulong)Random.Range(0.0f, float.MaxValue)); - } - - NetworkVariableData = new NetworkList(dataBlocksAssigned); - } - } -} diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSynchronizationTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSynchronizationTests.cs new file mode 100644 index 0000000000..7f58283c27 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSynchronizationTests.cs @@ -0,0 +1,624 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.TestTools; +using NUnit.Framework; +using Unity.Netcode.TestHelpers.Runtime; +using Random = UnityEngine.Random; + +namespace Unity.Netcode.RuntimeTests +{ + [TestFixture(VariableLengthSafety.DisableNetVarSafety, HostOrServer.Host)] + [TestFixture(VariableLengthSafety.EnabledNetVarSafety, HostOrServer.Host)] + [TestFixture(VariableLengthSafety.DisableNetVarSafety, HostOrServer.Server)] + [TestFixture(VariableLengthSafety.EnabledNetVarSafety, HostOrServer.Server)] + public class NetworkObjectSynchronizationTests : NetcodeIntegrationTest + { + private const int k_NumberToSpawn = 30; + protected override int NumberOfClients => 0; + + private GameObject m_NetworkPrefab; + private GameObject m_InValidNetworkPrefab; + private GameObject m_SynchronizationPrefab; + private GameObject m_OnSynchronizePrefab; + private VariableLengthSafety m_VariableLengthSafety; + + private LogLevel m_CurrentLogLevel; + + public enum VariableLengthSafety + { + DisableNetVarSafety, + EnabledNetVarSafety, + } + + public NetworkObjectSynchronizationTests(VariableLengthSafety variableLengthSafety, HostOrServer hostOrServer) + { + m_VariableLengthSafety = variableLengthSafety; + m_UseHost = hostOrServer == HostOrServer.Host; + } + + protected override void OnCreatePlayerPrefab() + { + m_PlayerPrefab.AddComponent(); + base.OnCreatePlayerPrefab(); + } + + protected override void OnServerAndClientsCreated() + { + + // Set the NetworkVariable Safety Check setting + m_ServerNetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety = m_VariableLengthSafety == VariableLengthSafety.EnabledNetVarSafety; + + // Ignore the errors generated during this test (they are expected) + m_ServerNetworkManager.LogLevel = LogLevel.Nothing; + + // Disable forcing the same prefabs to avoid failed connections + m_ServerNetworkManager.NetworkConfig.ForceSamePrefabs = false; + + // Create the valid network prefab + m_NetworkPrefab = CreateNetworkObjectPrefab("ValidObject"); + m_NetworkPrefab.AddComponent(); + + // Create the invalid network prefab (that will fail on client side) + m_InValidNetworkPrefab = CreateNetworkObjectPrefab("InvalidObject"); + m_InValidNetworkPrefab.AddComponent(); + + // Create the synchronization network prefab (some pass and some fail) + m_SynchronizationPrefab = CreateNetworkObjectPrefab("SyncObject"); + m_SynchronizationPrefab.AddComponent(); + m_SynchronizationPrefab.AddComponent(); + + m_OnSynchronizePrefab = CreateNetworkObjectPrefab("OnSyncObject"); + m_OnSynchronizePrefab.AddComponent(); + + base.OnServerAndClientsCreated(); + } + + protected override void OnNewClientCreated(NetworkManager networkManager) + { + networkManager.NetworkConfig.PlayerPrefab = m_PlayerPrefab; + networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety = m_VariableLengthSafety == VariableLengthSafety.EnabledNetVarSafety; + foreach (var networkPrefab in m_ServerNetworkManager.NetworkConfig.NetworkPrefabs) + { + // To simulate a failure, we exclude the m_InValidNetworkPrefab from the connecting + // client's side. + if (networkPrefab.Prefab.name != m_InValidNetworkPrefab.name) + { + networkManager.NetworkConfig.NetworkPrefabs.Add(networkPrefab); + } + } + // Disable forcing the same prefabs to avoid failed connections + networkManager.NetworkConfig.ForceSamePrefabs = false; + networkManager.LogLevel = m_CurrentLogLevel; + base.OnNewClientCreated(networkManager); + } + + [UnityTest] + public IEnumerator NetworkObjectDeserializationFailure() + { + m_CurrentLogLevel = LogLevel.Nothing; + var validSpawnedNetworkObjects = new List(); + NetworkBehaviourWithNetworkVariables.ResetSpawnCount(); + + // Spawn NetworkObjects on the server side with half of them being the + // invalid network prefabs to simulate NetworkObject synchronization failure + for (int i = 0; i < k_NumberToSpawn; i++) + { + if (i % 2 == 0) + { + SpawnObject(m_InValidNetworkPrefab, m_ServerNetworkManager); + } + else + { + // Keep track of the prefabs that should successfully spawn on the client side + validSpawnedNetworkObjects.Add(SpawnObject(m_NetworkPrefab, m_ServerNetworkManager)); + } + } + + // Assure the server-side spawned all NetworkObjects + yield return WaitForConditionOrTimeOut(() => NetworkBehaviourWithNetworkVariables.ServerSpawnCount == k_NumberToSpawn); + + // Now spawn and connect a client that will fail to spawn half of the NetworkObjects spawned + yield return CreateAndStartNewClient(); + + if (m_UseHost) + { + var serverSideClientPlayerComponent = m_PlayerNetworkObjects[m_ServerNetworkManager.LocalClientId][m_ClientNetworkManagers[0].LocalClientId].GetComponent(); + var serverSideHostPlayerComponent = m_ServerNetworkManager.LocalClient.PlayerObject.GetComponent(); + var clientSidePlayerComponent = m_ClientNetworkManagers[0].LocalClient.PlayerObject.GetComponent(); + var clientSideHostPlayerComponent = m_PlayerNetworkObjects[m_ClientNetworkManagers[0].LocalClientId][m_ServerNetworkManager.LocalClientId].GetComponent(); + + // Validate that the client side player values match the server side value of the client's player + Assert.IsTrue(serverSideClientPlayerComponent.NetworkVariableData1.Value == clientSidePlayerComponent.NetworkVariableData1.Value, + $"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData1)}][Client Player] Client side value ({serverSideClientPlayerComponent.NetworkVariableData1.Value})" + + $" does not equal the server side value ({serverSideClientPlayerComponent.NetworkVariableData1.Value})!"); + Assert.IsTrue(serverSideClientPlayerComponent.NetworkVariableData2.Value == clientSidePlayerComponent.NetworkVariableData2.Value, + $"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData2)}][Client Player] Client side value ({serverSideClientPlayerComponent.NetworkVariableData2.Value})" + + $" does not equal the server side value ({serverSideClientPlayerComponent.NetworkVariableData2.Value})!"); + Assert.IsTrue(serverSideClientPlayerComponent.NetworkVariableData3.Value == clientSidePlayerComponent.NetworkVariableData3.Value, + $"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData3)}][Client Player] Client side value ({serverSideClientPlayerComponent.NetworkVariableData3.Value})" + + $" does not equal the server side value ({serverSideClientPlayerComponent.NetworkVariableData3.Value})!"); + Assert.IsTrue(serverSideClientPlayerComponent.NetworkVariableData4.Value == clientSidePlayerComponent.NetworkVariableData4.Value, + $"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData4)}][Client Player] Client side value ({serverSideClientPlayerComponent.NetworkVariableData4.Value})" + + $" does not equal the server side value ({serverSideClientPlayerComponent.NetworkVariableData4.Value})!"); + + + // Validate that only the 2nd and 4th NetworkVariable on the client side instance of the host's player is the same and the other two do not match + // (i.e. NetworkVariables owned by the server should not get synchronized on client) + Assert.IsTrue(serverSideHostPlayerComponent.NetworkVariableData1.Value != clientSideHostPlayerComponent.NetworkVariableData1.Value, + $"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData1)}][Host Player] Client side value ({serverSideHostPlayerComponent.NetworkVariableData1.Value})" + + $" should not be equal to the server side value ({clientSideHostPlayerComponent.NetworkVariableData1.Value})!"); + Assert.IsTrue(serverSideHostPlayerComponent.NetworkVariableData2.Value == clientSideHostPlayerComponent.NetworkVariableData2.Value, + $"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData2)}][Host Player] Client side value ({serverSideHostPlayerComponent.NetworkVariableData2.Value})" + + $" does not equal the server side value ({clientSideHostPlayerComponent.NetworkVariableData2.Value})!"); + Assert.IsTrue(serverSideHostPlayerComponent.NetworkVariableData3.Value != clientSideHostPlayerComponent.NetworkVariableData3.Value, + $"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData3)}][Host Player] Client side value ({serverSideHostPlayerComponent.NetworkVariableData3.Value})" + + $" should not be equal to the server side value ({clientSideHostPlayerComponent.NetworkVariableData3.Value})!"); + Assert.IsTrue(serverSideHostPlayerComponent.NetworkVariableData4.Value == clientSideHostPlayerComponent.NetworkVariableData4.Value, + $"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData4)}][Host Player] Client side value ({serverSideHostPlayerComponent.NetworkVariableData4.Value})" + + $" does not equal the server side value ({clientSideHostPlayerComponent.NetworkVariableData4.Value})!"); + } + else + { + // Spawn and connect another client when running as a server + yield return CreateAndStartNewClient(); + yield return WaitForConditionOrTimeOut(() => m_PlayerNetworkObjects[2].Count > 1); + AssertOnTimeout($"Timed out waiting for second client to have access to the first client's cloned player object!"); + + var clientSide1PlayerComponent = m_ClientNetworkManagers[0].LocalClient.PlayerObject.GetComponent(); + var clientSide2Player1Clone = m_PlayerNetworkObjects[2][clientSide1PlayerComponent.OwnerClientId].GetComponent(); + var clientOneId = clientSide1PlayerComponent.OwnerClientId; + + var clientSide2PlayerComponent = m_ClientNetworkManagers[1].LocalClient.PlayerObject.GetComponent(); + var clientSide1Player2Clone = m_PlayerNetworkObjects[1][clientSide2PlayerComponent.OwnerClientId].GetComponent(); + var clientTwoId = clientSide2PlayerComponent.OwnerClientId; + + // Validate that client one's 2nd and 4th NetworkVariables for the local and clone instances match and the other two do not + Assert.IsTrue(clientSide1PlayerComponent.NetworkVariableData1.Value != clientSide2Player1Clone.NetworkVariableData1.Value, + $"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData1)}][Player-{clientOneId}] Client-{clientOneId} value ({clientSide1PlayerComponent.NetworkVariableData1.Value})" + + $" should not be equal to Client-{clientTwoId}'s clone side value ({clientSide2Player1Clone.NetworkVariableData1.Value})!"); + + Assert.IsTrue(clientSide1PlayerComponent.NetworkVariableData2.Value == clientSide2Player1Clone.NetworkVariableData2.Value, + $"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData2)}][Player-{clientOneId}] Client-{clientOneId} value ({clientSide1PlayerComponent.NetworkVariableData2.Value})" + + $" does not equal Client-{clientTwoId}'s clone side value ({clientSide2Player1Clone.NetworkVariableData2.Value})!"); + + Assert.IsTrue(clientSide1PlayerComponent.NetworkVariableData3.Value != clientSide2Player1Clone.NetworkVariableData3.Value, + $"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData3)}][Player-{clientOneId}] Client-{clientOneId} value ({clientSide1PlayerComponent.NetworkVariableData3.Value})" + + $" should not be equal to Client-{clientTwoId}'s clone side value ({clientSide2Player1Clone.NetworkVariableData3.Value})!"); + + Assert.IsTrue(clientSide1PlayerComponent.NetworkVariableData4.Value == clientSide2Player1Clone.NetworkVariableData4.Value, + $"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData4)}][Player-{clientOneId}] Client-{clientOneId} value ({clientSide1PlayerComponent.NetworkVariableData4.Value})" + + $" does not equal Client-{clientTwoId}'s clone side value ({clientSide2Player1Clone.NetworkVariableData4.Value})!"); + + + // Validate that client two's 2nd and 4th NetworkVariables for the local and clone instances match and the other two do not + Assert.IsTrue(clientSide2PlayerComponent.NetworkVariableData1.Value != clientSide1Player2Clone.NetworkVariableData1.Value, + $"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData1)}][Player-{clientTwoId}] Client-{clientTwoId} value ({clientSide2PlayerComponent.NetworkVariableData1.Value})" + + $" should not be equal to Client-{clientOneId}'s clone side value ({clientSide1Player2Clone.NetworkVariableData1.Value})!"); + + Assert.IsTrue(clientSide2PlayerComponent.NetworkVariableData2.Value == clientSide1Player2Clone.NetworkVariableData2.Value, + $"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData2)}][Player-{clientTwoId}] Client-{clientTwoId} value ({clientSide2PlayerComponent.NetworkVariableData2.Value})" + + $" does not equal Client-{clientOneId}'s clone side value ({clientSide1Player2Clone.NetworkVariableData2.Value})!"); + + Assert.IsTrue(clientSide2PlayerComponent.NetworkVariableData3.Value != clientSide1Player2Clone.NetworkVariableData3.Value, + $"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData3)}][Player-{clientTwoId}] Client-{clientTwoId} value ({clientSide2PlayerComponent.NetworkVariableData3.Value})" + + $" should not be equal to Client-{clientOneId}'s clone side value ({clientSide1Player2Clone.NetworkVariableData3.Value})!"); + + Assert.IsTrue(clientSide2PlayerComponent.NetworkVariableData4.Value == clientSide1Player2Clone.NetworkVariableData4.Value, + $"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData4)}][Player-{clientTwoId}] Client-{clientTwoId} value ({clientSide2PlayerComponent.NetworkVariableData4.Value})" + + $" does not equal Client-{clientOneId}'s clone side value ({clientSide1Player2Clone.NetworkVariableData4.Value})!"); + } + + // Now validate all of the NetworkVariable values match to assure everything synchronized properly + foreach (var spawnedObject in validSpawnedNetworkObjects) + { + foreach (var clientNetworkManager in m_ClientNetworkManagers) + { + //Validate that the connected client has spawned all of the instances that shouldn't have failed. + var clientSideNetworkObjects = s_GlobalNetworkObjects[clientNetworkManager.LocalClientId]; + + Assert.IsTrue(NetworkBehaviourWithNetworkVariables.ClientSpawnCount[clientNetworkManager.LocalClientId] == validSpawnedNetworkObjects.Count, $"Client-{clientNetworkManager.LocalClientId} spawned " + + $"({NetworkBehaviourWithNetworkVariables.ClientSpawnCount}) {nameof(NetworkObject)}s but the expected number of {nameof(NetworkObject)}s should have been ({validSpawnedNetworkObjects.Count})!"); + + var spawnedNetworkObject = spawnedObject.GetComponent(); + Assert.IsTrue(clientSideNetworkObjects.ContainsKey(spawnedNetworkObject.NetworkObjectId), $"Failed to find valid spawned {nameof(NetworkObject)} on the client-side with a " + + $"{nameof(NetworkObject.NetworkObjectId)} of {spawnedNetworkObject.NetworkObjectId}"); + + var clientSideObject = clientSideNetworkObjects[spawnedNetworkObject.NetworkObjectId]; + Assert.IsTrue(clientSideObject.NetworkManager == clientNetworkManager, $"Client-side object {clientSideObject}'s {nameof(NetworkManager)} is not valid!"); + + ValidateNetworkBehaviourWithNetworkVariables(spawnedNetworkObject, clientSideObject); + } + } + } + + private void ValidateNetworkBehaviourWithNetworkVariables(NetworkObject serverSideNetworkObject, NetworkObject clientSideNetworkObject) + { + var serverSideComponent = serverSideNetworkObject.GetComponent(); + var clientSideComponent = clientSideNetworkObject.GetComponent(); + + string netVarName1 = nameof(NetworkBehaviourWithNetworkVariables.NetworkVariableData1); + string netVarName2 = nameof(NetworkBehaviourWithNetworkVariables.NetworkVariableData1); + string netVarName3 = nameof(NetworkBehaviourWithNetworkVariables.NetworkVariableData1); + string netVarName4 = nameof(NetworkBehaviourWithNetworkVariables.NetworkVariableData1); + + Assert.IsTrue(serverSideComponent.NetworkVariableData1.Count == clientSideComponent.NetworkVariableData1.Count, $"[{serverSideComponent.name}:{netVarName1}] Server side {nameof(NetworkList)} " + + $"count ({serverSideComponent.NetworkVariableData1.Count}) does not match the client side {nameof(NetworkList)} count ({clientSideComponent.NetworkVariableData1.Count})!"); + + for (int i = 0; i < serverSideComponent.NetworkVariableData1.Count; i++) + { + Assert.IsTrue(serverSideComponent.NetworkVariableData1[i] == clientSideComponent.NetworkVariableData1[i], $"[{serverSideComponent.name}:{netVarName1}][Index:{i}] Server side instance value " + + $"({serverSideComponent.NetworkVariableData1[i]}) does not match the client side instance value ({clientSideComponent.NetworkVariableData1[i]})!"); + } + + Assert.IsTrue(serverSideComponent.NetworkVariableData2.Value == clientSideComponent.NetworkVariableData2.Value, $"[{serverSideComponent.name}:{netVarName2}] Server side instance value ({serverSideComponent.NetworkVariableData2.Value}) " + + $"does not match the client side instance value ({clientSideComponent.NetworkVariableData2.Value})!"); + Assert.IsTrue(serverSideComponent.NetworkVariableData3.Value == clientSideComponent.NetworkVariableData3.Value, $"[{serverSideComponent.name}:{netVarName3}] Server side instance value ({serverSideComponent.NetworkVariableData3.Value}) " + + $"does not match the client side instance value ({clientSideComponent.NetworkVariableData3.Value})!"); + Assert.IsTrue(serverSideComponent.NetworkVariableData4.Value == clientSideComponent.NetworkVariableData4.Value, $"[{serverSideComponent.name}:{netVarName4}] Server side instance value ({serverSideComponent.NetworkVariableData4.Value}) " + + $"does not match the client side instance value ({clientSideComponent.NetworkVariableData4.Value})!"); + } + + /// + /// This validates that when a NetworkBehaviour fails serialization or deserialization during synchronizations that other NetworkBehaviours + /// will still be initialized properly + /// + [UnityTest] + public IEnumerator NetworkBehaviourSynchronization() + { + m_ServerNetworkManager.LogLevel = LogLevel.Normal; + m_CurrentLogLevel = LogLevel.Normal; + NetworkBehaviourSynchronizeFailureComponent.ResetBehaviour(); + + var spawnedObjectList = new List(); + var numberOfObjectsToSpawn = NetworkBehaviourSynchronizeFailureComponent.NumberOfFailureTypes * 4; + // Spawn 11 more NetworkObjects where there should be 4 of each failure type + for (int i = 0; i < numberOfObjectsToSpawn; i++) + { + var synchronizationObject = SpawnObject(m_SynchronizationPrefab, m_ServerNetworkManager); + var synchronizationBehaviour = synchronizationObject.GetComponent(); + synchronizationBehaviour.AssignNextFailureType(); + spawnedObjectList.Add(synchronizationObject); + } + + // Now spawn and connect a client that will fail to spawn half of the NetworkObjects spawned + yield return CreateAndStartNewClient(); + + // Validate that when a NetworkBehaviour fails to synchronize and is skipped over it does not + // impact the rest of the NetworkBehaviours. + var clientSideNetworkObjects = s_GlobalNetworkObjects[m_ClientNetworkManagers[0].LocalClientId]; + foreach (var spawnedObject in spawnedObjectList) + { + var serverSideSpawnedNetworkObject = spawnedObject.GetComponent(); + var clientSideObject = clientSideNetworkObjects[serverSideSpawnedNetworkObject.NetworkObjectId]; + var clientSideSpawnedNetworkObject = clientSideObject.GetComponent(); + + ValidateNetworkBehaviourWithNetworkVariables(serverSideSpawnedNetworkObject, clientSideSpawnedNetworkObject); + } + } + + /// + /// A basic validation for the NetworkBehaviour.OnSynchronize method + /// + [UnityTest] + public IEnumerator NetworkBehaviourOnSynchronize() + { + var serverSideInstance = SpawnObject(m_OnSynchronizePrefab, m_ServerNetworkManager).GetComponent(); + + // Now spawn and connect a client that will have custom serialized data applied during the client synchronization process. + yield return CreateAndStartNewClient(); + + var clientSideNetworkObjects = s_GlobalNetworkObjects[m_ClientNetworkManagers[0].LocalClientId]; + var clientSideInstance = clientSideNetworkObjects[serverSideInstance.NetworkObjectId].GetComponent(); + + // Validate the values match + Assert.IsTrue(serverSideInstance.CustomSerializationData.Value1 == clientSideInstance.CustomSerializationData.Value1, $"Client-side instance Value1 ({serverSideInstance.CustomSerializationData.Value1}) does not equal server-side instance Value1 ({clientSideInstance.CustomSerializationData.Value1})"); + Assert.IsTrue(serverSideInstance.CustomSerializationData.Value2 == clientSideInstance.CustomSerializationData.Value2, $"Client-side instance Value1 ({serverSideInstance.CustomSerializationData.Value2}) does not equal server-side instance Value1 ({clientSideInstance.CustomSerializationData.Value2})"); + Assert.IsTrue(serverSideInstance.CustomSerializationData.Value3 == clientSideInstance.CustomSerializationData.Value3, $"Client-side instance Value1 ({serverSideInstance.CustomSerializationData.Value3}) does not equal server-side instance Value1 ({clientSideInstance.CustomSerializationData.Value3})"); + Assert.IsTrue(serverSideInstance.CustomSerializationData.Value4 == clientSideInstance.CustomSerializationData.Value4, $"Client-side instance Value1 ({serverSideInstance.CustomSerializationData.Value4}) does not equal server-side instance Value1 ({clientSideInstance.CustomSerializationData.Value4})"); + } + } + + /// + /// A test NetworkBeahviour that provides a varying NetworkList size as well as + /// additional NetworkVariables to assure if a NetworkObject fails to be created + /// the synchronization process will continue (i.e. it will skip over that block + /// of the reader buffer). + /// + public class NetworkBehaviourWithNetworkVariables : NetworkBehaviour + { + public static int ServerSpawnCount { get; internal set; } + public static readonly Dictionary ClientSpawnCount = new Dictionary(); + + public static void ResetSpawnCount() + { + ServerSpawnCount = 0; + ClientSpawnCount.Clear(); + } + + private const uint k_MinDataBlocks = 1; + private const uint k_MaxDataBlocks = 64; + + // Add various types of NetworkVariables + public NetworkList NetworkVariableData1; + public NetworkVariable NetworkVariableData2; + public NetworkVariable NetworkVariableData3; + public NetworkVariable NetworkVariableData4; + + private void Awake() + { + var dataBlocksAssigned = new List(); + var numberDataBlocks = Random.Range(k_MinDataBlocks, k_MaxDataBlocks); + for (var i = 0; i < numberDataBlocks; i++) + { + dataBlocksAssigned.Add((ulong)Random.Range(0.0f, float.MaxValue)); + } + + NetworkVariableData1 = new NetworkList(dataBlocksAssigned); + NetworkVariableData2 = new NetworkVariable(Random.Range(1, 1000)); + NetworkVariableData3 = new NetworkVariable(Random.Range(1, 1000)); + NetworkVariableData4 = new NetworkVariable((byte)Random.Range(1, 255)); + + } + + public override void OnNetworkSpawn() + { + if (IsServer) + { + ServerSpawnCount++; + } + else + { + if (!ClientSpawnCount.ContainsKey(NetworkManager.LocalClientId)) + { + ClientSpawnCount.Add(NetworkManager.LocalClientId, 0); + } + ClientSpawnCount[NetworkManager.LocalClientId]++; + } + + base.OnNetworkSpawn(); + } + } + + /// + /// A test NetworkBeahviour that has varying permissions in order to validate that + /// when variable length safety checks are off NetworkVariables still are updated + /// properly. + /// + public class NetworkBehaviourWithOwnerNetworkVariables : NetworkBehaviour + { + + // Should not synchronize on non-owners + public NetworkVariable NetworkVariableData1 = new NetworkVariable(default, NetworkVariableReadPermission.Owner, NetworkVariableWritePermission.Server); + // Should synchronize with everyone + public NetworkVariable NetworkVariableData2 = new NetworkVariable(); + // Should not synchronize on non-owners + public NetworkVariable NetworkVariableData3 = new NetworkVariable(default, NetworkVariableReadPermission.Owner, NetworkVariableWritePermission.Server); + // Should synchronize with everyone + public NetworkVariable NetworkVariableData4 = new NetworkVariable(); + + public override void OnNetworkSpawn() + { + if (IsServer) + { + NetworkVariableData1.Value = Random.Range(1, 1000); + NetworkVariableData2.Value = Random.Range(1, 1000); + NetworkVariableData3.Value = (byte)Random.Range(1, 255); + NetworkVariableData4.Value = (ushort)Random.Range(1, ushort.MaxValue); + } + } + } + + /// + /// A test NetworkBeahviour that simulates various types of synchronization failures + /// and provides a synchronization success version to validate that synchronization + /// will continue if user synchronization code fails. + /// + public class NetworkBehaviourSynchronizeFailureComponent : NetworkBehaviour + { + public static int NumberOfFailureTypes { get; internal set; } + public static int ServerSpawnCount { get; internal set; } + public static int ClientSpawnCount { get; internal set; } + + private static FailureTypes s_FailureType = FailureTypes.None; + + public enum FailureTypes + { + None, + DuringWriting, + DuringReading, + DontReadAnything, + ThrowWriteSideException, + ThrowReadSideException + } + + public static void ResetBehaviour() + { + ServerSpawnCount = 0; + ClientSpawnCount = 0; + s_FailureType = FailureTypes.None; + NumberOfFailureTypes = System.Enum.GetValues(typeof(FailureTypes)).Length; + } + + private MyCustomData m_MyCustomData; + + private struct MyCustomData : INetworkSerializable + { + public FailureTypes FailureType; + private ushort m_DataSize; + private byte[] m_DataBlock; + + public void NetworkSerialize(BufferSerializer serializer) where T : IReaderWriter + { + if (serializer.IsWriter) + { + var writer = serializer.GetFastBufferWriter(); + switch (FailureType) + { + case FailureTypes.None: + // We want to write something for these two cases + case FailureTypes.DuringReading: + case FailureTypes.DontReadAnything: + { + writer.WriteValueSafe(m_DataSize); + for (int i = 0; i < m_DataSize; i++) + { + writer.WriteValueSafe(m_DataBlock[i]); + } + break; + } + case FailureTypes.DuringWriting: + { + writer.WriteValueSafe(m_DataSize); + // Try to write past the allocated size to generate an exception + // while also filling the buffer to verify that the buffer will be + // reset back to the original position. + for (int i = 0; i <= m_DataSize; i++) + { + writer.WriteValueSafe(m_DataBlock[i]); + } + break; + } + case FailureTypes.ThrowWriteSideException: + { + throw new System.Exception("Write side exception!"); + } + } + } + else + { + var reader = serializer.GetFastBufferReader(); + switch (FailureType) + { + case FailureTypes.None: + { + reader.ReadValueSafe(out m_DataSize); + m_DataBlock = new byte[m_DataSize]; + for (int i = 0; i < m_DataSize; i++) + { + reader.ReadValueSafe(out m_DataBlock[i]); + } + break; + } + case FailureTypes.DuringReading: + { + reader.ReadValueSafe(out m_DataSize); + // Allocate more space than needed + m_DataBlock = new byte[(int)(m_DataSize * 1.5f)]; + // Now read past the size of this message to verify + // that the reader will get rest back to the appropriate + // position and an error will be generated for this + for (int i = 0; i < m_DataBlock.Length; i++) + { + reader.ReadValueSafe(out m_DataBlock[i]); + } + break; + } + case FailureTypes.DontReadAnything: + { + // Don't read anything + break; + } + case FailureTypes.ThrowReadSideException: + { + throw new System.Exception("Read side exception!"); + } + + } + } + } + + public void GenerateData(ushort size) + { + m_DataSize = size; + m_DataBlock = new byte[size]; + for (int i = 0; i < m_DataSize; i++) + { + m_DataBlock[i] = (byte)Random.Range(0, 512); + } + } + } + + // This NetworkVariable is synchronized before OnSynchronize is invoked + // which enables us to perform the tests. + // Users could follow the same pattern for game assets and synchronize + // clients based on NetworkVariable settings. (i.e. a specific NPC type or the like) + private NetworkVariable m_FailureType; + + public void AssignNextFailureType() + { + var currentPosition = (int)s_FailureType; + currentPosition = (++currentPosition) % NumberOfFailureTypes; + s_FailureType = (FailureTypes)currentPosition; + m_FailureType.Value = s_FailureType; + } + + + private void Awake() + { + m_FailureType = new NetworkVariable(); + m_MyCustomData = new MyCustomData(); + } + + public override void OnNetworkSpawn() + { + if (IsServer) + { + ServerSpawnCount++; + m_MyCustomData.GenerateData((ushort)Random.Range(1, 512)); + } + else + { + ClientSpawnCount++; + } + + base.OnNetworkSpawn(); + } + + protected override void OnSynchronize(ref BufferSerializer serializer) + { + // Assign the failure type first + m_MyCustomData.FailureType = m_FailureType.Value; + // Now handle the serialization for this failure type + m_MyCustomData.NetworkSerialize(serializer); + } + } + + public class NetworkBehaviourOnSynchronizeComponent : NetworkBehaviour + { + public SomeCustomSerializationData CustomSerializationData = new SomeCustomSerializationData(); + + public struct SomeCustomSerializationData : INetworkSerializable + { + public uint Value1; + public bool Value2; + public long Value3; + public float Value4; + public void NetworkSerialize(BufferSerializer serializer) where T : IReaderWriter + { + serializer.SerializeValue(ref Value1); + serializer.SerializeValue(ref Value2); + serializer.SerializeValue(ref Value3); + serializer.SerializeValue(ref Value4); + } + } + + public override void OnNetworkSpawn() + { + if (IsServer) + { + CustomSerializationData.Value1 = (uint)Random.Range(0, 10000); + CustomSerializationData.Value2 = true; + CustomSerializationData.Value3 = Random.Range(0, 10000); + CustomSerializationData.Value4 = Random.Range(-1000.0f, 1000.0f); + } + base.OnNetworkSpawn(); + } + + protected override void OnSynchronize(ref BufferSerializer serializer) + { + serializer.SerializeNetworkSerializable(ref CustomSerializationData); + base.OnSynchronize(ref serializer); + } + } +} diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs.meta b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSynchronizationTests.cs.meta similarity index 100% rename from com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs.meta rename to com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSynchronizationTests.cs.meta diff --git a/testproject/Assets/Scripts/ServerHostClientText.cs b/testproject/Assets/Scripts/ServerHostClientText.cs index a394fbf03e..4c49ca8ca1 100644 --- a/testproject/Assets/Scripts/ServerHostClientText.cs +++ b/testproject/Assets/Scripts/ServerHostClientText.cs @@ -7,6 +7,12 @@ public class ServerHostClientText : NetworkBehaviour [SerializeField] private Text m_DisplayText; private Color m_Color; + private Vector3 m_LocalPosition; + + private void Awake() + { + m_LocalPosition = transform.localPosition; + } private void Start() { @@ -30,6 +36,7 @@ public override void OnNetworkSpawn() m_DisplayText.text = "Client"; } } + transform.localPosition = m_LocalPosition; } private void OnGUI() diff --git a/testproject/Assets/Tests/Manual/ManualTestsMenu.unity b/testproject/Assets/Tests/Manual/ManualTestsMenu.unity index 0781d337fa..8ca598ab66 100644 --- a/testproject/Assets/Tests/Manual/ManualTestsMenu.unity +++ b/testproject/Assets/Tests/Manual/ManualTestsMenu.unity @@ -698,6 +698,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3} m_Name: m_EditorClassIdentifier: + m_SendPointerHoverToParent: 1 m_HorizontalAxis: Horizontal m_VerticalAxis: Vertical m_SubmitButton: Submit @@ -923,6 +924,7 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: m_SceneMenus: + - {fileID: 11400000, guid: 2c0ff1138526d4041a875c84f7114513, type: 2} - {fileID: 11400000, guid: 83bc07221c884b24e968f464eaccce26, type: 2} - {fileID: 11400000, guid: 644bd35b81cd40c4d88002a24e223462, type: 2} - {fileID: 11400000, guid: 33e25ac2a2f551c4db75a9e960a6bc2f, type: 2} diff --git a/testproject/Assets/Tests/Manual/NestedNetworkTransforms.meta b/testproject/Assets/Tests/Manual/NestedNetworkTransforms.meta new file mode 100644 index 0000000000..9f0f5283cd --- /dev/null +++ b/testproject/Assets/Tests/Manual/NestedNetworkTransforms.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8a96c655551fade40adc673dd12bf67a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMover.cs b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMover.cs new file mode 100644 index 0000000000..38ad1881ee --- /dev/null +++ b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMover.cs @@ -0,0 +1,31 @@ +using UnityEngine; +using Unity.Netcode; + +namespace TestProject.ManualTests +{ + public class ChildMover : NetworkBehaviour + { + public static bool RandomizeScale; + + [Range(0.1f, 30.0f)] + public float RotationSpeed = 5.0f; + + public void PlayerIsMoving(float movementDirection) + { + if (IsSpawned && IsOwner) + { + var rotateDirection = movementDirection * RotationSpeed; + transform.RotateAround(transform.parent.position, Vector3.up, rotateDirection); + } + } + + public override void OnNetworkSpawn() + { + if (IsOwner && RandomizeScale) + { + transform.localScale = transform.localScale * Random.Range(0.5f, 2.0f); + } + base.OnNetworkSpawn(); + } + } +} diff --git a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMover.cs.meta b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMover.cs.meta new file mode 100644 index 0000000000..8d284ce0c3 --- /dev/null +++ b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMover.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0d8ad30fca3f9a240bdce16f0166033b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMoverManager.cs b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMoverManager.cs new file mode 100644 index 0000000000..6552943606 --- /dev/null +++ b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMoverManager.cs @@ -0,0 +1,107 @@ +using System.Collections.Generic; +using UnityEngine; +using Unity.Netcode; + +namespace TestProject.ManualTests +{ + public class ChildMoverManager : NetworkBehaviour + { + public List ChildMovers; + + [Range(0.001f, 5.0f)] + public float TriggerDistanceToMove = 0.2f; + + public Camera PlayerCamera; + private Vector3 m_LastPosition; + private Vector3 m_LastForward; + private Camera m_MainCamera; + + private void Awake() + { + if (PlayerCamera != null) + { + PlayerCamera.enabled = false; + } + } + + public override void OnNetworkSpawn() + { + if (IsOwner) + { + m_LastPosition = transform.position; + m_LastForward = transform.forward; + + for (int i = 0; i < Camera.allCamerasCount; i++) + { + var camera = Camera.allCameras[i]; + if (camera.name == "Main Camera") + { + m_MainCamera = Camera.allCameras[i]; + } + } + } + base.OnNetworkSpawn(); + } + + private float m_LastRotDirection = 1.0f; + private float m_LastMovementDirection = 1.0f; + private void Update() + { + if (IsOwner && IsSpawned) + { + var deltaPosition = (transform.position - m_LastPosition); + if (deltaPosition.sqrMagnitude >= (TriggerDistanceToMove * TriggerDistanceToMove)) + { + // Get our movement direction + var movementDirection = Vector3.Dot(deltaPosition.normalized, transform.forward); + if (movementDirection == 0) + { + movementDirection = m_LastMovementDirection; + } + else + { + m_LastMovementDirection = movementDirection; + } + var rotationDirection = Vector3.zero; + if (movementDirection > 0) + { + rotationDirection = Vector3.Cross(m_LastForward, transform.forward); + } + else if (movementDirection < 0) + { + rotationDirection = Vector3.Cross(transform.forward, m_LastForward); + } + else + { + rotationDirection.y = m_LastRotDirection; + } + + m_LastRotDirection = rotationDirection.y; + movementDirection *= rotationDirection.y < 0 ? -1 : 1; + + m_LastPosition = transform.position; + m_LastForward = transform.forward; + foreach (var childMover in ChildMovers) + { + childMover.PlayerIsMoving(Mathf.Sign(movementDirection)); + } + } + + if (Input.GetKeyDown(KeyCode.C) && PlayerCamera != null && m_MainCamera != null) + { + if (m_MainCamera.isActiveAndEnabled) + { + PlayerCamera.enabled = true; + m_MainCamera.enabled = false; + } + else + { + m_MainCamera.enabled = true; + PlayerCamera.enabled = false; + } + } + } + } + + } +} diff --git a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMoverManager.cs.meta b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMoverManager.cs.meta new file mode 100644 index 0000000000..b17549dd15 --- /dev/null +++ b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMoverManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 482bb1f796fe43348bcbfd8161ed3825 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/NestedNetworkTransforms.unity b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/NestedNetworkTransforms.unity new file mode 100644 index 0000000000..342f1a7b70 --- /dev/null +++ b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/NestedNetworkTransforms.unity @@ -0,0 +1,1031 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_LightingSettings: {fileID: 0} +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &635877837 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 635877840} + - component: {fileID: 635877839} + - component: {fileID: 635877838} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &635877838 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 635877837} + m_Enabled: 1 +--- !u!20 &635877839 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 635877837} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &635877840 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 635877837} + m_LocalRotation: {x: 0.60876137, y: 0, z: 0, w: 0.7933534} + m_LocalPosition: {x: 0, y: 60, z: -11} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 75, y: 0, z: 0} +--- !u!1 &691147452 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 691147454} + - component: {fileID: 691147453} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &691147453 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 691147452} + m_Enabled: 1 + serializedVersion: 10 + m_Type: 1 + m_Shape: 0 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_InnerSpotAngle: 21.80208 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_CullingMatrixOverride: + e00: 1 + e01: 0 + e02: 0 + e03: 0 + e10: 0 + e11: 1 + e12: 0 + e13: 0 + e20: 0 + e21: 0 + e22: 1 + e23: 0 + e30: 0 + e31: 0 + e32: 0 + e33: 1 + m_UseCullingMatrixOverride: 0 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingLayerMask: 1 + m_Lightmapping: 4 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} + m_UseBoundingSphereOverride: 0 + m_UseViewFrustumForShadowCasterCull: 1 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &691147454 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 691147452} + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: 0, y: 3, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!1001 &759287026 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 1107190083} + m_Modifications: + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_Pivot.x + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_Pivot.y + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_RootOrder + value: 2 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_AnchorMax.x + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_AnchorMax.y + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_AnchorMin.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_AnchorMin.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_SizeDelta.x + value: -952 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_SizeDelta.y + value: -344 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_AnchoredPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_AnchoredPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6963777608485144162, guid: d725b5588e1b956458798319e6541d84, + type: 3} + propertyPath: m_Name + value: ConnectionModeButtons + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: d725b5588e1b956458798319e6541d84, type: 3} +--- !u!224 &759287027 stripped +RectTransform: + m_CorrespondingSourceObject: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, + type: 3} + m_PrefabInstance: {fileID: 759287026} + m_PrefabAsset: {fileID: 0} +--- !u!1 &1107190079 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1107190083} + - component: {fileID: 1107190082} + - component: {fileID: 1107190081} + - component: {fileID: 1107190080} + m_Layer: 5 + m_Name: Canvas + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1107190080 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1107190079} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IgnoreReversedGraphics: 1 + m_BlockingObjects: 0 + m_BlockingMask: + serializedVersion: 2 + m_Bits: 4294967295 +--- !u!114 &1107190081 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1107190079} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} + m_Name: + m_EditorClassIdentifier: + m_UiScaleMode: 0 + m_ReferencePixelsPerUnit: 100 + m_ScaleFactor: 1 + m_ReferenceResolution: {x: 800, y: 600} + m_ScreenMatchMode: 0 + m_MatchWidthOrHeight: 0 + m_PhysicalUnit: 3 + m_FallbackScreenDPI: 96 + m_DefaultSpriteDPI: 96 + m_DynamicPixelsPerUnit: 1 + m_PresetInfoIsWorld: 0 +--- !u!223 &1107190082 +Canvas: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1107190079} + m_Enabled: 1 + serializedVersion: 3 + m_RenderMode: 0 + m_Camera: {fileID: 0} + m_PlaneDistance: 100 + m_PixelPerfect: 0 + m_ReceivesEvents: 1 + m_OverrideSorting: 0 + m_OverridePixelPerfect: 0 + m_SortingBucketNormalizedSize: 0 + m_AdditionalShaderChannelsFlag: 0 + m_SortingLayerID: 0 + m_SortingOrder: 0 + m_TargetDisplay: 0 +--- !u!224 &1107190083 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1107190079} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1136626022} + - {fileID: 1200545880} + - {fileID: 759287027} + - {fileID: 1622867933} + m_Father: {fileID: 0} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0} +--- !u!1 &1136626021 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1136626022} + - component: {fileID: 1136626026} + - component: {fileID: 1136626025} + - component: {fileID: 1136626024} + - component: {fileID: 1136626023} + m_Layer: 5 + m_Name: ServerHostClientDisplay + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1136626022 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1136626021} + 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: [] + m_Father: {fileID: 1107190083} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0} + m_AnchorMax: {x: 0.5, y: 0} + m_AnchoredPosition: {x: 0, y: 31} + m_SizeDelta: {x: 200, y: 30} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1136626023 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1136626021} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3} + m_Name: + m_EditorClassIdentifier: + GlobalObjectIdHash: 1789729126 + AlwaysReplicateAsRoot: 0 + DontDestroyWithOwner: 0 + AutoObjectParentSync: 0 +--- !u!114 &1136626024 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1136626021} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 7ea6e834b18e2c840a76ce574eb4b144, type: 3} + m_Name: + m_EditorClassIdentifier: + m_DisplayText: {fileID: 1136626025} +--- !u!114 &1136626025 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1136626021} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 0.5058824, b: 0.003921569, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 24 + m_FontStyle: 1 + m_BestFit: 0 + m_MinSize: 1 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 0 + m_Text: Server +--- !u!222 &1136626026 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1136626021} + m_CullTransparentMesh: 1 +--- !u!1001 &1200545879 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 1107190083} + m_Modifications: + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_Pivot.x + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_Pivot.y + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_RootOrder + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_AnchorMax.x + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_AnchorMax.y + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_AnchorMin.x + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_AnchorMin.y + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_SizeDelta.x + value: 20 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_SizeDelta.y + value: 25 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_LocalScale.x + value: 0.9806 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_LocalScale.y + value: 0.9806 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_LocalScale.z + value: 0.9806 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_LocalRotation.x + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_LocalRotation.y + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_LocalRotation.z + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_AnchoredPosition.x + value: -22.946533 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_AnchoredPosition.y + value: -23.338623 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2848221156307247795, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_Name + value: ExitButton + objectReference: {fileID: 0} + - target: {fileID: 5266522511616468950, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + propertyPath: m_SceneMenuToLoad + value: + objectReference: {fileID: 11400000, guid: 4a3cdce12e998384f8aca207b5a2c700, + type: 2} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 3200770c16e3b2b4ebe7f604154faac7, type: 3} +--- !u!224 &1200545880 stripped +RectTransform: + m_CorrespondingSourceObject: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7, + type: 3} + m_PrefabInstance: {fileID: 1200545879} + m_PrefabAsset: {fileID: 0} +--- !u!1001 &1525117742 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 0} + m_Modifications: + - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05, + type: 3} + propertyPath: m_RootOrder + value: 4 + objectReference: {fileID: 0} + - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05, + type: 3} + propertyPath: m_LocalScale.x + value: 1.5 + objectReference: {fileID: 0} + - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05, + type: 3} + propertyPath: m_LocalScale.z + value: 1.5 + objectReference: {fileID: 0} + - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05, + type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05, + type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05, + type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05, + type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05, + type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05, + type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4012615692778511854, guid: fe5fd652408224242a6fea8a6f8e6d05, + type: 3} + propertyPath: m_Name + value: SceneLevelGeometry + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: fe5fd652408224242a6fea8a6f8e6d05, type: 3} +--- !u!1 &1622867932 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1622867933} + - component: {fileID: 1622867935} + - component: {fileID: 1622867934} + m_Layer: 5 + m_Name: EventSystem + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1622867933 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1622867932} + 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: [] + m_Father: {fileID: 1107190083} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1622867934 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1622867932} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3} + m_Name: + m_EditorClassIdentifier: + m_SendPointerHoverToParent: 1 + m_HorizontalAxis: Horizontal + m_VerticalAxis: Vertical + m_SubmitButton: Submit + m_CancelButton: Cancel + m_InputActionsPerSecond: 10 + m_RepeatDelay: 0.5 + m_ForceModuleActive: 0 +--- !u!114 &1622867935 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1622867932} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3} + m_Name: + m_EditorClassIdentifier: + m_FirstSelected: {fileID: 0} + m_sendNavigationEvents: 1 + m_DragThreshold: 10 +--- !u!1 &1909864228 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1909864230} + - component: {fileID: 1909864229} + - component: {fileID: 1909864231} + m_Layer: 0 + m_Name: NetworkManager + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1909864229 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1909864228} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 593a2fe42fa9d37498c96f9a383b6521, type: 3} + m_Name: + m_EditorClassIdentifier: + RunInBackground: 1 + LogLevel: 1 + NetworkConfig: + ProtocolVersion: 0 + NetworkTransport: {fileID: 1909864231} + PlayerPrefab: {fileID: 296612175404815451, guid: 61e89146f864ef2478af3da9d7dda1e2, + type: 3} + NetworkPrefabs: [] + TickRate: 30 + ClientConnectionBufferTimeout: 10 + ConnectionApproval: 0 + ConnectionData: + EnableTimeResync: 0 + TimeResyncInterval: 30 + EnsureNetworkVariableLengthSafety: 0 + EnableSceneManagement: 1 + ForceSamePrefabs: 1 + RecycleNetworkIds: 1 + NetworkIdRecycleDelay: 120 + RpcHashSize: 0 + LoadSceneTimeOut: 120 + SpawnTimeout: 1 + EnableNetworkLogs: 1 +--- !u!4 &1909864230 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1909864228} + 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: [] + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1909864231 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1909864228} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 6960e84d07fb87f47956e7a81d71c4e6, type: 3} + m_Name: + m_EditorClassIdentifier: + m_ProtocolType: 0 + m_MaxPacketQueueSize: 128 + m_MaxPayloadSize: 6144 + m_HeartbeatTimeoutMS: 500 + m_ConnectTimeoutMS: 1000 + m_MaxConnectAttempts: 60 + m_DisconnectTimeoutMS: 30000 + ConnectionData: + Address: 127.0.0.1 + Port: 7777 + ServerListenAddress: + DebugSimulator: + PacketDelayMS: 0 + PacketJitterMS: 0 + PacketDropRate: 0 diff --git a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/NestedNetworkTransforms.unity.meta b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/NestedNetworkTransforms.unity.meta new file mode 100644 index 0000000000..1fe34e9afa --- /dev/null +++ b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/NestedNetworkTransforms.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 92b8cccf28cbaba40854a025b66e2ac3 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/NestedNetworkTransformsReference.asset b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/NestedNetworkTransformsReference.asset new file mode 100644 index 0000000000..3414672af6 --- /dev/null +++ b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/NestedNetworkTransformsReference.asset @@ -0,0 +1,19 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 39a16938ffb5cd846a9f6df7a686a9c4, type: 3} + m_Name: NestedNetworkTransformsReference + m_EditorClassIdentifier: + SceneToReference: {fileID: 102900000, guid: 92b8cccf28cbaba40854a025b66e2ac3, type: 3} + m_IncludedScenes: [] + m_DisplayName: Nested NetworkTransforms + m_ReferencedScenes: + - NestedNetworkTransforms diff --git a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/NestedNetworkTransformsReference.asset.meta b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/NestedNetworkTransformsReference.asset.meta new file mode 100644 index 0000000000..d242a32947 --- /dev/null +++ b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/NestedNetworkTransformsReference.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2c0ff1138526d4041a875c84f7114513 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/PlayerCube Variant.prefab b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/PlayerCube Variant.prefab new file mode 100644 index 0000000000..7dd9279aa0 --- /dev/null +++ b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/PlayerCube Variant.prefab @@ -0,0 +1,474 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &772585991204072682 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1522619104359096714} + - component: {fileID: 6327137497379236391} + - component: {fileID: 2204728973112518521} + - component: {fileID: 1866518356433686547} + - component: {fileID: 2645854474244200924} + m_Layer: 0 + m_Name: ChildOne + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1522619104359096714 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 772585991204072682} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 2, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 296612175404815447} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!23 &6327137497379236391 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 772585991204072682} + 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_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 62ff4693d71b55440a645bbccd83ac8a, 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: 0 + 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!33 &2204728973112518521 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 772585991204072682} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!114 &1866518356433686547 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 772585991204072682} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0d8ad30fca3f9a240bdce16f0166033b, type: 3} + m_Name: + m_EditorClassIdentifier: + RotationSpeed: 5 +--- !u!114 &2645854474244200924 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 772585991204072682} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: cf01cca54b77c0241ad6d9da5ef6a709, type: 3} + m_Name: + m_EditorClassIdentifier: + 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 + InLocalSpace: 0 + Interpolate: 1 +--- !u!1 &2771624607751045562 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5485086383386216104} + m_Layer: 0 + m_Name: FirstLevel + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &5485086383386216104 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2771624607751045562} + 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: 4974009855568796650} + m_Father: {fileID: 296612175404815447} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &4147667212972069939 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7199215624742357828} + - component: {fileID: 8372809022110481992} + m_Layer: 0 + m_Name: PlayerView + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &7199215624742357828 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4147667212972069939} + m_LocalRotation: {x: 0.17364816, y: -0, z: -0, w: 0.9848078} + m_LocalPosition: {x: 0, y: 8, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 296612175404815447} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 20, y: 0, z: 0} +--- !u!20 &8372809022110481992 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4147667212972069939} + m_Enabled: 0 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!1 &6292214655028195304 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4974009855568796650} + - component: {fileID: 3062926429781172158} + - component: {fileID: 1595868777393624541} + - component: {fileID: 2914594030718603294} + - component: {fileID: 2888046383252644210} + m_Layer: 0 + m_Name: ChildTwo + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4974009855568796650 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6292214655028195304} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -2, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 5485086383386216104} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!23 &3062926429781172158 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6292214655028195304} + 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_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 62ff4693d71b55440a645bbccd83ac8a, 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: 0 + 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!33 &1595868777393624541 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6292214655028195304} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!114 &2914594030718603294 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6292214655028195304} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0d8ad30fca3f9a240bdce16f0166033b, type: 3} + m_Name: + m_EditorClassIdentifier: + RotationSpeed: 5 +--- !u!114 &2888046383252644210 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6292214655028195304} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: cf01cca54b77c0241ad6d9da5ef6a709, type: 3} + m_Name: + m_EditorClassIdentifier: + 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 + InLocalSpace: 0 + Interpolate: 1 +--- !u!1001 &8977898853425847701 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 0} + m_Modifications: + - target: {fileID: 947981134, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, type: 3} + propertyPath: GlobalObjectIdHash + value: 951099334 + objectReference: {fileID: 0} + - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, + type: 3} + propertyPath: m_RootOrder + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, + type: 3} + propertyPath: m_LocalScale.x + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, + type: 3} + propertyPath: m_LocalScale.y + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, + type: 3} + propertyPath: m_LocalScale.z + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, + type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, + type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, + type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, + type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, + type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, + type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8685790303553767886, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, + type: 3} + propertyPath: m_Name + value: PlayerCube Variant + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, type: 3} +--- !u!4 &296612175404815447 stripped +Transform: + m_CorrespondingSourceObject: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, + type: 3} + m_PrefabInstance: {fileID: 8977898853425847701} + m_PrefabAsset: {fileID: 0} +--- !u!1 &296612175404815451 stripped +GameObject: + m_CorrespondingSourceObject: {fileID: 8685790303553767886, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, + type: 3} + m_PrefabInstance: {fileID: 8977898853425847701} + m_PrefabAsset: {fileID: 0} +--- !u!114 &4389916208190318681 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 296612175404815451} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 482bb1f796fe43348bcbfd8161ed3825, type: 3} + m_Name: + m_EditorClassIdentifier: + ChildMovers: + - {fileID: 1866518356433686547} + - {fileID: 2914594030718603294} + TriggerDistanceToMove: 0.25 + PlayerCamera: {fileID: 8372809022110481992} diff --git a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/PlayerCube Variant.prefab.meta b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/PlayerCube Variant.prefab.meta new file mode 100644 index 0000000000..c6d325e64c --- /dev/null +++ b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/PlayerCube Variant.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 61e89146f864ef2478af3da9d7dda1e2 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Tests/Runtime/ObjectParenting/AutomatedPlayerMover.cs b/testproject/Assets/Tests/Runtime/ObjectParenting/AutomatedPlayerMover.cs new file mode 100644 index 0000000000..bd97836562 --- /dev/null +++ b/testproject/Assets/Tests/Runtime/ObjectParenting/AutomatedPlayerMover.cs @@ -0,0 +1,93 @@ +using UnityEngine; +using Unity.Netcode.Components; + +namespace TestProject.RuntimeTests +{ + public class AutomatedPlayerMover : NetworkTransform + { + public static bool StopMovement; + private float m_Speed = 15.0f; + private float m_RotSpeed = 15.0f; + + private GameObject m_Destination; + private Vector3 m_TargetPosition; + + + + /// + /// Make this PlayerMovement-NetworkTransform component + /// Owner Authoritative + /// + protected override bool OnIsServerAuthoritative() + { + return false; + } + + private void UpdateDestination() + { + if (Navigationpoints.Instance != null) + { + var targetNavPointIndex = Random.Range(0, Navigationpoints.Instance.NavPoints.Count - 1); + m_Destination = Navigationpoints.Instance.NavPoints[targetNavPointIndex]; + + m_TargetPosition = m_Destination.transform.position; + m_TargetPosition.y = transform.position.y; + } + } + + public override void OnNetworkSpawn() + { + if (IsOwner) + { + UpdateDestination(); + var temp = transform.position; + temp.y = 0.5f; + transform.position = temp; + } + base.OnNetworkSpawn(); + } + + public override void OnNetworkDespawn() + { + base.OnNetworkDespawn(); + } + + private void LateUpdate() + { + if (!IsSpawned || !IsOwner || m_Destination == null || StopMovement) + { + return; + } + m_TargetPosition.y = transform.position.y; + var distance = Vector3.Distance(transform.position, m_TargetPosition); + if (distance < 0.25f) + { + var currentDestination = m_Destination; + while (m_Destination == currentDestination) + { + UpdateDestination(); + } + } + } + + private void FixedUpdate() + { + if (!IsSpawned || !IsOwner || m_Destination == null || StopMovement) + { + return; + } + else + { + transform.position = Vector3.MoveTowards(transform.position, m_Destination.transform.position, m_Speed * Time.fixedDeltaTime); + var normalizedDirection = (m_TargetPosition - transform.position).normalized; + if (normalizedDirection.magnitude != 0.0f) + { + var lookRotation = Quaternion.LookRotation(normalizedDirection, transform.up).eulerAngles; + var currentEuler = transform.eulerAngles; + currentEuler.y = Mathf.LerpAngle(currentEuler.y, lookRotation.y, Time.deltaTime * m_RotSpeed); + transform.eulerAngles = currentEuler; + } + } + } + } +} diff --git a/testproject/Assets/Tests/Runtime/ObjectParenting/AutomatedPlayerMover.cs.meta b/testproject/Assets/Tests/Runtime/ObjectParenting/AutomatedPlayerMover.cs.meta new file mode 100644 index 0000000000..3ccd51fd66 --- /dev/null +++ b/testproject/Assets/Tests/Runtime/ObjectParenting/AutomatedPlayerMover.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dfb1af1a9249278438d2daa2877ee2ad +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Tests/Runtime/ObjectParenting/Navigationpoints.cs b/testproject/Assets/Tests/Runtime/ObjectParenting/Navigationpoints.cs new file mode 100644 index 0000000000..e2a6aecfcf --- /dev/null +++ b/testproject/Assets/Tests/Runtime/ObjectParenting/Navigationpoints.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace TestProject.RuntimeTests +{ + public class Navigationpoints : MonoBehaviour + { + public static Navigationpoints Instance; + + public List NavPoints; + + private void Awake() + { + if (Instance == null) + { + Instance = this; + } + } + } +} diff --git a/testproject/Assets/Tests/Runtime/ObjectParenting/Navigationpoints.cs.meta b/testproject/Assets/Tests/Runtime/ObjectParenting/Navigationpoints.cs.meta new file mode 100644 index 0000000000..82f15cd59e --- /dev/null +++ b/testproject/Assets/Tests/Runtime/ObjectParenting/Navigationpoints.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fb5950beeb672f54fa8b2666aaefa223 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Tests/Runtime/ObjectParenting/NestedNetworkTransformTestScene.unity b/testproject/Assets/Tests/Runtime/ObjectParenting/NestedNetworkTransformTestScene.unity new file mode 100644 index 0000000000..e7971f34dd --- /dev/null +++ b/testproject/Assets/Tests/Runtime/ObjectParenting/NestedNetworkTransformTestScene.unity @@ -0,0 +1,1077 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_LightingSettings: {fileID: 0} +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &75767844 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 75767845} + m_Layer: 0 + m_Name: NavigationPoint1 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &75767845 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 75767844} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0.5, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 582924497} + m_Father: {fileID: 0} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &147173471 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 147173472} + - component: {fileID: 147173476} + - component: {fileID: 147173475} + m_Layer: 9 + m_Name: ObjectLabel + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &147173472 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 147173471} + 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: [] + m_Father: {fileID: 165918550} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!102 &147173475 +TextMesh: + serializedVersion: 3 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 147173471} + m_Text: 3 + m_OffsetZ: 0 + m_CharacterSize: 1 + m_LineSpacing: 1 + m_Anchor: 7 + m_Alignment: 0 + m_TabSize: 4 + m_FontSize: 32 + m_FontStyle: 0 + m_RichText: 1 + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_Color: + serializedVersion: 2 + rgba: 4294967295 +--- !u!23 &147173476 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 147173471} + 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_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10100, guid: 0000000000000000e000000000000000, type: 0} + 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: 0 + 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 &165918549 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 165918550} + m_Layer: 0 + m_Name: NavigationPoint3 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &165918550 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 165918549} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 50, y: 0.5, z: 50} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 147173472} + m_Father: {fileID: 0} + m_RootOrder: 5 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &170238949 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 170238951} + - component: {fileID: 170238950} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &170238950 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 170238949} + m_Enabled: 1 + serializedVersion: 10 + m_Type: 1 + m_Shape: 0 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_InnerSpotAngle: 21.80208 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_CullingMatrixOverride: + e00: 1 + e01: 0 + e02: 0 + e03: 0 + e10: 0 + e11: 1 + e12: 0 + e13: 0 + e20: 0 + e21: 0 + e22: 1 + e23: 0 + e30: 0 + e31: 0 + e32: 0 + e33: 1 + m_UseCullingMatrixOverride: 0 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingLayerMask: 1 + m_Lightmapping: 4 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} + m_UseBoundingSphereOverride: 0 + m_UseViewFrustumForShadowCasterCull: 1 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &170238951 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 170238949} + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: 0, y: 3, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!1 &582924496 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 582924497} + - component: {fileID: 582924501} + - component: {fileID: 582924500} + m_Layer: 9 + m_Name: ObjectLabel + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &582924497 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 582924496} + 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: [] + m_Father: {fileID: 75767845} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!102 &582924500 +TextMesh: + serializedVersion: 3 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 582924496} + m_Text: 1 + m_OffsetZ: 0 + m_CharacterSize: 1 + m_LineSpacing: 1 + m_Anchor: 7 + m_Alignment: 0 + m_TabSize: 4 + m_FontSize: 32 + m_FontStyle: 0 + m_RichText: 1 + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_Color: + serializedVersion: 2 + rgba: 4294967295 +--- !u!23 &582924501 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 582924496} + 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_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10100, guid: 0000000000000000e000000000000000, type: 0} + 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: 0 + 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 &1218177454 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1218177455} + - component: {fileID: 1218177459} + - component: {fileID: 1218177458} + m_Layer: 9 + m_Name: ObjectLabel + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1218177455 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1218177454} + 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: [] + m_Father: {fileID: 1476695697} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!102 &1218177458 +TextMesh: + serializedVersion: 3 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1218177454} + m_Text: "5\t" + m_OffsetZ: 0 + m_CharacterSize: 1 + m_LineSpacing: 1 + m_Anchor: 7 + m_Alignment: 0 + m_TabSize: 4 + m_FontSize: 32 + m_FontStyle: 0 + m_RichText: 1 + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_Color: + serializedVersion: 2 + rgba: 4294967295 +--- !u!23 &1218177459 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1218177454} + 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_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10100, guid: 0000000000000000e000000000000000, type: 0} + 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: 0 + 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 &1222925673 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1222925674} + - component: {fileID: 1222925678} + - component: {fileID: 1222925677} + m_Layer: 9 + m_Name: ObjectLabel + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1222925674 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1222925673} + 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: [] + m_Father: {fileID: 2026412476} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!102 &1222925677 +TextMesh: + serializedVersion: 3 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1222925673} + m_Text: "2\t" + m_OffsetZ: 0 + m_CharacterSize: 1 + m_LineSpacing: 1 + m_Anchor: 7 + m_Alignment: 0 + m_TabSize: 4 + m_FontSize: 32 + m_FontStyle: 0 + m_RichText: 1 + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_Color: + serializedVersion: 2 + rgba: 4294967295 +--- !u!23 &1222925678 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1222925673} + 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_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10100, guid: 0000000000000000e000000000000000, type: 0} + 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: 0 + 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 &1323194500 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1323194502} + - component: {fileID: 1323194501} + m_Layer: 0 + m_Name: NavigationPoints + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1323194501 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1323194500} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fb5950beeb672f54fa8b2666aaefa223, type: 3} + m_Name: + m_EditorClassIdentifier: + NavPoints: + - {fileID: 75767844} + - {fileID: 2026412475} + - {fileID: 165918549} + - {fileID: 2050720992} + - {fileID: 1476695696} +--- !u!4 &1323194502 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1323194500} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 53.61696, z: -48.528473} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 8 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1001 &1337449911 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 0} + m_Modifications: + - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05, + type: 3} + propertyPath: m_RootOrder + value: 2 + objectReference: {fileID: 0} + - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05, + type: 3} + propertyPath: m_LocalScale.x + value: 2 + objectReference: {fileID: 0} + - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05, + type: 3} + propertyPath: m_LocalScale.z + value: 2 + objectReference: {fileID: 0} + - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05, + type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05, + type: 3} + propertyPath: m_LocalPosition.y + value: 0.000000059604645 + objectReference: {fileID: 0} + - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05, + type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05, + type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05, + type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05, + type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4012615692778511854, guid: fe5fd652408224242a6fea8a6f8e6d05, + type: 3} + propertyPath: m_Name + value: SceneLevelGeometry + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: fe5fd652408224242a6fea8a6f8e6d05, type: 3} +--- !u!1 &1476695696 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1476695697} + m_Layer: 0 + m_Name: NavigationPoint5 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1476695697 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1476695696} + m_LocalRotation: {x: 0.2164396, y: 0, z: 0, w: 0.97629607} + m_LocalPosition: {x: 50, y: 0.5, z: -50} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1218177455} + m_Father: {fileID: 0} + m_RootOrder: 7 + m_LocalEulerAnglesHint: {x: 25, y: 0, z: 0} +--- !u!1 &1561339197 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1561339198} + - component: {fileID: 1561339202} + - component: {fileID: 1561339201} + m_Layer: 9 + m_Name: ObjectLabel + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1561339198 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1561339197} + 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: [] + m_Father: {fileID: 2050720993} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!102 &1561339201 +TextMesh: + serializedVersion: 3 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1561339197} + m_Text: "4\t" + m_OffsetZ: 0 + m_CharacterSize: 1 + m_LineSpacing: 1 + m_Anchor: 7 + m_Alignment: 0 + m_TabSize: 4 + m_FontSize: 32 + m_FontStyle: 0 + m_RichText: 1 + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_Color: + serializedVersion: 2 + rgba: 4294967295 +--- !u!23 &1561339202 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1561339197} + 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_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10100, guid: 0000000000000000e000000000000000, type: 0} + 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: 0 + 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 &1922374989 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1922374992} + - component: {fileID: 1922374991} + - component: {fileID: 1922374990} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &1922374990 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1922374989} + m_Enabled: 1 +--- !u!20 &1922374991 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1922374989} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &1922374992 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1922374989} + m_LocalRotation: {x: 0.46174863, y: 0, z: 0, w: 0.8870109} + m_LocalPosition: {x: 0, y: 70, z: -60} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 55, y: 0, z: 0} +--- !u!1 &2026412475 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2026412476} + m_Layer: 0 + m_Name: NavigationPoint2 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2026412476 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2026412475} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -50, y: 0.5, z: 50} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1222925674} + m_Father: {fileID: 0} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &2050720992 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2050720993} + m_Layer: 0 + m_Name: NavigationPoint4 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2050720993 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2050720992} + m_LocalRotation: {x: 0.2164396, y: 0, z: 0, w: 0.97629607} + m_LocalPosition: {x: -50, y: 0.5, z: -50} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1561339198} + m_Father: {fileID: 0} + m_RootOrder: 6 + m_LocalEulerAnglesHint: {x: 25, y: 0, z: 0} diff --git a/testproject/Assets/Tests/Runtime/ObjectParenting/NestedNetworkTransformTestScene.unity.meta b/testproject/Assets/Tests/Runtime/ObjectParenting/NestedNetworkTransformTestScene.unity.meta new file mode 100644 index 0000000000..3623bc6e70 --- /dev/null +++ b/testproject/Assets/Tests/Runtime/ObjectParenting/NestedNetworkTransformTestScene.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 14235ed18eff0964cbb3ff7ae2ed1933 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Tests/Runtime/ObjectParenting/NestedNetworkTransformTests.cs b/testproject/Assets/Tests/Runtime/ObjectParenting/NestedNetworkTransformTests.cs new file mode 100644 index 0000000000..d1f145e8f1 --- /dev/null +++ b/testproject/Assets/Tests/Runtime/ObjectParenting/NestedNetworkTransformTests.cs @@ -0,0 +1,183 @@ +using System.Text; +using System.Collections; +using Unity.Netcode.Components; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.SceneManagement; +using UnityEngine.TestTools; +using TestProject.ManualTests; + +namespace TestProject.RuntimeTests +{ + public class NestedNetworkTransformTests : IntegrationTestWithApproximation + { + private const string k_TestScene = "NestedNetworkTransformTestScene"; + private const string k_PlayerToLoad = "PlayerNestedTransforms"; + + protected override int NumberOfClients => 0; + + private Scene m_BaseSceneLoaded; + private Scene m_OriginalActiveScene; + + private Object m_PlayerPrefabResource; + + protected override void OnOneTimeSetup() + { + ChildMover.RandomizeScale = true; + // Preserve the test runner scene that is currently the active scene + m_OriginalActiveScene = SceneManager.GetActiveScene(); + // Load our test's scene used by all client players (it gets set as the currently active scene when loaded) + m_PlayerPrefabResource = Resources.Load(k_PlayerToLoad); + Assert.NotNull(m_PlayerPrefabResource, $"Failed to load resource {k_PlayerToLoad}"); + + // Migrate the resource into the DDOL to prevent the server from thinking it is in-scene placed. + Object.DontDestroyOnLoad(m_PlayerPrefabResource); + SceneManager.sceneLoaded += SceneManager_sceneLoaded; + SceneManager.LoadScene(k_TestScene, LoadSceneMode.Additive); + base.OnOneTimeSetup(); + } + + protected override void OnOneTimeTearDown() + { + ChildMover.RandomizeScale = false; + // Set test runner's scene back to the currently active scene + if (m_OriginalActiveScene.IsValid() && m_OriginalActiveScene.isLoaded) + { + SceneManager.SetActiveScene(m_OriginalActiveScene); + } + // Unload our base scene if it is still loaded + if (m_BaseSceneLoaded.IsValid() && m_BaseSceneLoaded.isLoaded) + { + SceneManager.UnloadSceneAsync(m_BaseSceneLoaded); + } + base.OnOneTimeTearDown(); + } + + protected override IEnumerator OnSetup() + { + yield return WaitForConditionOrTimeOut(() => m_BaseSceneLoaded.IsValid() && m_BaseSceneLoaded.isLoaded); + AssertOnTimeout($"Timed out waiting for scene {k_TestScene} to load!"); + yield return base.OnSetup(); + } + + private void SceneManager_sceneLoaded(Scene sceneLoaded, LoadSceneMode loadSceneMode) + { + if (loadSceneMode == LoadSceneMode.Additive && sceneLoaded.name == k_TestScene) + { + m_BaseSceneLoaded = sceneLoaded; + SceneManager.sceneLoaded -= SceneManager_sceneLoaded; + SceneManager.SetActiveScene(sceneLoaded); + } + } + + protected override IEnumerator OnTearDown() + { + // This prevents us from trying to destroy the resource loaded + m_PlayerPrefab = null; + return base.OnTearDown(); + } + + + protected override void OnCreatePlayerPrefab() + { + // Destroy the default player prefab + Object.DestroyImmediate(m_PlayerPrefab); + // Assign the network prefab resource loaded + m_PlayerPrefab = m_PlayerPrefabResource as GameObject; + base.OnCreatePlayerPrefab(); + } + + /// + /// Prevent the server from telling the clients to load our test scene + /// + private bool VerifySceneServer(int sceneIndex, string sceneName, LoadSceneMode loadSceneMode) + { + if (sceneName == k_TestScene) + { + return false; + } + return true; + } + + /// + /// Increase the threshold as we are just testing that the + /// NetworkTransform is synchronizing properly and there are + /// known issues with Euler rotation that will be fixed when + /// we start synchronizing Quaternions. + /// + protected override float GetDeltaVarianceThreshold() + { + return 0.1f; + } + + private StringBuilder m_ValidationErrors; + /// + /// Validates that all player transforms are approximately the + /// same when a new client joins. + /// + /// + private bool ValidateNetworkTransforms() + { + m_ValidationErrors.Clear(); + foreach (var connectedClient in m_ServerNetworkManager.ConnectedClientsIds) + { + var playerToValidate = m_PlayerNetworkObjects[connectedClient][connectedClient]; + var playerNetworkTransforms = playerToValidate.GetComponentsInChildren(); + foreach (var playerRelative in m_PlayerNetworkObjects) + { + if (playerRelative.Key == connectedClient) + { + continue; + } + var relativeClonedTransforms = playerRelative.Value[connectedClient].GetComponentsInChildren(); + for (int i = 0; i < playerNetworkTransforms.Length; i++) + { + if (!Approximately(playerNetworkTransforms[i].transform.position, relativeClonedTransforms[i].transform.position)) + { + m_ValidationErrors.Append($"[Position][Client-{connectedClient} {playerNetworkTransforms[i].transform.position}][Failing on Client-{playerRelative.Key} for Clone-{relativeClonedTransforms[i].OwnerClientId} {relativeClonedTransforms[i].transform.position}]"); + } + if (!Approximately(playerNetworkTransforms[i].transform.eulerAngles, relativeClonedTransforms[i].transform.eulerAngles)) + { + m_ValidationErrors.Append($"[Rotation][Client-{connectedClient} {playerNetworkTransforms[i].transform.eulerAngles}][Failing on Client-{playerRelative.Key} for Clone-{relativeClonedTransforms[i].OwnerClientId} {relativeClonedTransforms[i].transform.eulerAngles}]"); + } + if (!Approximately(playerNetworkTransforms[i].transform.localScale, relativeClonedTransforms[i].transform.localScale)) + { + m_ValidationErrors.Append($"[Scale][Client-{connectedClient} {playerNetworkTransforms[i].transform.localScale}][Failing on Client-{playerRelative.Key} for Clone-{relativeClonedTransforms[i].OwnerClientId} {relativeClonedTransforms[i].transform.localScale}]"); + } + } + } + } + return m_ValidationErrors.Length == 0; + } + + [UnityTest] + public IEnumerator NestedNetworkTransformSynchronization() + { + m_ValidationErrors = new StringBuilder(); + var waitPeriod = new WaitForSeconds(1.0f); + m_ServerNetworkManager.SceneManager.VerifySceneBeforeLoading = VerifySceneServer; + yield return waitPeriod; + + // Spawn 5 more clients over time + for (int i = 0; i < 5; i++) + { + yield return CreateAndStartNewClient(); + // Stop all movement for all players + AutomatedPlayerMover.StopMovement = true; + // Validate that the transforms are approximately the same + yield return WaitForConditionOrTimeOut(ValidateNetworkTransforms); + AssertOnTimeout($"Timed out waiting for all nested NetworkTransform cloned instances to match:\n {m_ValidationErrors}"); + // Continue player movement + AutomatedPlayerMover.StopMovement = false; + // Allow the players to move a bit. + yield return waitPeriod; + } + + // Just a final sanity check to make sure position and rotation match + AutomatedPlayerMover.StopMovement = true; + yield return WaitForConditionOrTimeOut(ValidateNetworkTransforms); + } + + } +} + diff --git a/testproject/Assets/Tests/Runtime/ObjectParenting/NestedNetworkTransformTests.cs.meta b/testproject/Assets/Tests/Runtime/ObjectParenting/NestedNetworkTransformTests.cs.meta new file mode 100644 index 0000000000..cebb30746c --- /dev/null +++ b/testproject/Assets/Tests/Runtime/ObjectParenting/NestedNetworkTransformTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1623c8082eac2494badd6912c5ca2f7f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Tests/Runtime/ObjectParenting/ParentingInSceneObjectsTests.cs b/testproject/Assets/Tests/Runtime/ObjectParenting/ParentingInSceneObjectsTests.cs index 8accb2aba8..c1853c0d0b 100644 --- a/testproject/Assets/Tests/Runtime/ObjectParenting/ParentingInSceneObjectsTests.cs +++ b/testproject/Assets/Tests/Runtime/ObjectParenting/ParentingInSceneObjectsTests.cs @@ -12,7 +12,6 @@ public class ParentingInSceneObjectsTests : IntegrationTestWithApproximation { private const string k_BaseSceneToLoad = "UnitTestBaseScene"; private const string k_TestSceneToLoad = "ParentingInSceneObjects"; - private const string k_NestedUndeGameObjectName = "RootParent_GameObject"; private const int k_NumIterationsDeparentReparent = 100; private const float k_AproximateThresholdValue = 0.001f; diff --git a/testproject/Assets/Tests/Runtime/ObjectParenting/Resources.meta b/testproject/Assets/Tests/Runtime/ObjectParenting/Resources.meta new file mode 100644 index 0000000000..dbf62d950b --- /dev/null +++ b/testproject/Assets/Tests/Runtime/ObjectParenting/Resources.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6d5e70d978cb18b4cbca95b8aa1aff40 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/Assets/Tests/Runtime/ObjectParenting/Resources/PlayerNestedTransforms.prefab b/testproject/Assets/Tests/Runtime/ObjectParenting/Resources/PlayerNestedTransforms.prefab new file mode 100644 index 0000000000..9eb666bcdf --- /dev/null +++ b/testproject/Assets/Tests/Runtime/ObjectParenting/Resources/PlayerNestedTransforms.prefab @@ -0,0 +1,515 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &772585991204072682 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1522619104359096714} + - component: {fileID: 6327137497379236391} + - component: {fileID: 2204728973112518521} + - component: {fileID: 1866518356433686547} + - component: {fileID: 2645854474244200924} + m_Layer: 0 + m_Name: ChildOne + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1522619104359096714 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 772585991204072682} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 2, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 296612175404815447} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!23 &6327137497379236391 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 772585991204072682} + 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_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 62ff4693d71b55440a645bbccd83ac8a, 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: 0 + 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!33 &2204728973112518521 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 772585991204072682} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!114 &1866518356433686547 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 772585991204072682} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0d8ad30fca3f9a240bdce16f0166033b, type: 3} + m_Name: + m_EditorClassIdentifier: + RotationSpeed: 5 +--- !u!114 &2645854474244200924 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 772585991204072682} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: cf01cca54b77c0241ad6d9da5ef6a709, type: 3} + m_Name: + m_EditorClassIdentifier: + 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 + InLocalSpace: 0 + Interpolate: 1 +--- !u!1 &2771624607751045562 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5485086383386216104} + m_Layer: 0 + m_Name: FirstLevel + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &5485086383386216104 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2771624607751045562} + 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: 4974009855568796650} + m_Father: {fileID: 296612175404815447} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &4147667212972069939 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7199215624742357828} + - component: {fileID: 8372809022110481992} + m_Layer: 0 + m_Name: PlayerView + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &7199215624742357828 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4147667212972069939} + m_LocalRotation: {x: 0.17364816, y: -0, z: -0, w: 0.9848078} + m_LocalPosition: {x: 0, y: 8, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 296612175404815447} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 20, y: 0, z: 0} +--- !u!20 &8372809022110481992 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4147667212972069939} + m_Enabled: 0 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!1 &6292214655028195304 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4974009855568796650} + - component: {fileID: 3062926429781172158} + - component: {fileID: 1595868777393624541} + - component: {fileID: 2914594030718603294} + - component: {fileID: 2888046383252644210} + m_Layer: 0 + m_Name: ChildTwo + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4974009855568796650 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6292214655028195304} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -2, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 5485086383386216104} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!23 &3062926429781172158 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6292214655028195304} + 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_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 62ff4693d71b55440a645bbccd83ac8a, 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: 0 + 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!33 &1595868777393624541 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6292214655028195304} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!114 &2914594030718603294 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6292214655028195304} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0d8ad30fca3f9a240bdce16f0166033b, type: 3} + m_Name: + m_EditorClassIdentifier: + RotationSpeed: 5 +--- !u!114 &2888046383252644210 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6292214655028195304} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: cf01cca54b77c0241ad6d9da5ef6a709, type: 3} + m_Name: + m_EditorClassIdentifier: + 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 + InLocalSpace: 0 + Interpolate: 1 +--- !u!1001 &8977898853425847701 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 0} + m_Modifications: + - target: {fileID: 947981134, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, type: 3} + propertyPath: GlobalObjectIdHash + value: 951099334 + objectReference: {fileID: 0} + - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, + type: 3} + propertyPath: m_RootOrder + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, + type: 3} + propertyPath: m_LocalScale.x + value: 2 + objectReference: {fileID: 0} + - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, + type: 3} + propertyPath: m_LocalScale.y + value: 2 + objectReference: {fileID: 0} + - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, + type: 3} + propertyPath: m_LocalScale.z + value: 2 + objectReference: {fileID: 0} + - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, + type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, + type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, + type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, + type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, + type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, + type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8685790303553767877, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, + type: 3} + propertyPath: m_Interpolate + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8685790303553767877, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, + type: 3} + propertyPath: m_IsKinematic + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8685790303553767886, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, + type: 3} + propertyPath: m_Name + value: PlayerNestedTransforms + objectReference: {fileID: 0} + m_RemovedComponents: + - {fileID: 8685790303553767876, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, type: 3} + - {fileID: 7138389085065872747, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, type: 3} + - {fileID: 2744080254494315543, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, type: 3} + - {fileID: 8685790303553767873, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, type: 3} + - {fileID: 8685790303553767877, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, type: 3} + m_SourcePrefab: {fileID: 100100000, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, type: 3} +--- !u!4 &296612175404815447 stripped +Transform: + m_CorrespondingSourceObject: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, + type: 3} + m_PrefabInstance: {fileID: 8977898853425847701} + m_PrefabAsset: {fileID: 0} +--- !u!1 &296612175404815451 stripped +GameObject: + m_CorrespondingSourceObject: {fileID: 8685790303553767886, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, + type: 3} + m_PrefabInstance: {fileID: 8977898853425847701} + m_PrefabAsset: {fileID: 0} +--- !u!114 &4389916208190318681 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 296612175404815451} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 482bb1f796fe43348bcbfd8161ed3825, type: 3} + m_Name: + m_EditorClassIdentifier: + ChildMovers: + - {fileID: 1866518356433686547} + - {fileID: 2914594030718603294} + TriggerDistanceToMove: 0.25 + PlayerCamera: {fileID: 8372809022110481992} +--- !u!114 &5376990732947334198 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 296612175404815451} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: dfb1af1a9249278438d2daa2877ee2ad, type: 3} + m_Name: + m_EditorClassIdentifier: + SyncPositionX: 1 + SyncPositionY: 1 + SyncPositionZ: 1 + SyncRotAngleX: 0 + SyncRotAngleY: 1 + SyncRotAngleZ: 0 + SyncScaleX: 1 + SyncScaleY: 1 + SyncScaleZ: 1 + PositionThreshold: 0.001 + RotAngleThreshold: 0.01 + ScaleThreshold: 0.01 + InLocalSpace: 0 + Interpolate: 1 diff --git a/testproject/Assets/Tests/Runtime/ObjectParenting/Resources/PlayerNestedTransforms.prefab.meta b/testproject/Assets/Tests/Runtime/ObjectParenting/Resources/PlayerNestedTransforms.prefab.meta new file mode 100644 index 0000000000..39a6843e92 --- /dev/null +++ b/testproject/Assets/Tests/Runtime/ObjectParenting/Resources/PlayerNestedTransforms.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 0a46b240b37c5b74786b1938e8fd2eae +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/testproject/ProjectSettings/EditorBuildSettings.asset b/testproject/ProjectSettings/EditorBuildSettings.asset index a1496f313c..4b0fb15a49 100644 --- a/testproject/ProjectSettings/EditorBuildSettings.asset +++ b/testproject/ProjectSettings/EditorBuildSettings.asset @@ -113,6 +113,12 @@ EditorBuildSettings: - enabled: 1 path: Assets/Tests/Runtime/ObjectParenting/ParentingInSceneObjects.unity guid: 49fd14bff1eceda4f9299721a9029750 + - enabled: 1 + path: Assets/Tests/Manual/NestedNetworkTransforms/NestedNetworkTransforms.unity + guid: 92b8cccf28cbaba40854a025b66e2ac3 + - enabled: 1 + path: Assets/Tests/Runtime/ObjectParenting/NestedNetworkTransformTestScene.unity + guid: 14235ed18eff0964cbb3ff7ae2ed1933 m_configObjects: com.unity.addressableassets: {fileID: 11400000, guid: 5a3d5c53c25349c48912726ae850f3b0, type: 2}