From f429bfdcf320ae341f92109de07d62f4433ae844 Mon Sep 17 00:00:00 2001 From: Anton Franzluebbers Date: Tue, 11 Jan 2022 22:42:57 -0500 Subject: [PATCH] added mouse dragging script for testing, move networkdestroy and takeownership to static methods in the manager so not everybody needs references to the player, added a custom inspector to NetworkObject for showing owner and a button for automatically finding and assigning networkcomponents, better interpolation for synctransform based on serialization rate, synctransform can send local transforms, find new scene objects on scene load --- .../VelNet/1.0.4/Example/MouseDragger.cs | 44 +++++++++++ .../VelNet/1.0.4/Example/MouseDragger.cs.meta | 11 +++ .../VelNet/1.0.4/Example/PlayerController.cs | 4 +- .../VelNet/1.0.4/Example/PlayerPrefab.prefab | 4 +- .../Samples/VelNet/1.0.4/Example/test.unity | 39 +++++++--- .../VelNetUnity/Runtime/NetworkObject.cs | 48 +++++++++++- .../Runtime/Util/NetworkSerializedObject.cs | 8 +- .../VelNetUnity/Runtime/Util/SyncTransform.cs | 77 +++++++++++++++++-- .../VelNetUnity/Runtime/VelNetManager.cs | 50 +++++++++--- .../VelNetUnity/Runtime/VelNetPlayer.cs | 26 +++---- 10 files changed, 260 insertions(+), 51 deletions(-) create mode 100644 TestVelGameServer/Assets/Samples/VelNet/1.0.4/Example/MouseDragger.cs create mode 100644 TestVelGameServer/Assets/Samples/VelNet/1.0.4/Example/MouseDragger.cs.meta diff --git a/TestVelGameServer/Assets/Samples/VelNet/1.0.4/Example/MouseDragger.cs b/TestVelGameServer/Assets/Samples/VelNet/1.0.4/Example/MouseDragger.cs new file mode 100644 index 0000000..6802b65 --- /dev/null +++ b/TestVelGameServer/Assets/Samples/VelNet/1.0.4/Example/MouseDragger.cs @@ -0,0 +1,44 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using VelNet; + +public class MouseDragger : MonoBehaviour +{ + private Camera cam; + public string[] draggableTags = { "draggable" }; + private NetworkObject draggingObject; + + private void Start() + { + cam = Camera.main; + } + + private void Update() + { + if (Input.GetMouseButtonDown(0)) + { + if (Physics.Raycast(cam.ScreenPointToRay(Input.mousePosition), out RaycastHit hit)) + { + foreach (string draggableTag in draggableTags) + { + if (!hit.transform.CompareTag(draggableTag) && !hit.transform.parent?.CompareTag(draggableTag) == true) continue; + NetworkObject netObj = hit.transform.GetComponent(); + netObj ??= hit.transform.GetComponentInParent(); + if (netObj == null) break; + VelNetManager.TakeOwnership(netObj.networkId); + draggingObject = netObj; + break; + } + } + } + else if (Input.GetMouseButtonUp(0)) + { + draggingObject = null; + } + else if (Input.GetMouseButton(0) && draggingObject != null) + { + draggingObject.transform.position = cam.ScreenPointToRay(Input.mousePosition).direction * Vector3.Distance(draggingObject.transform.position, cam.transform.position) + cam.transform.position; + } + } +} \ No newline at end of file diff --git a/TestVelGameServer/Assets/Samples/VelNet/1.0.4/Example/MouseDragger.cs.meta b/TestVelGameServer/Assets/Samples/VelNet/1.0.4/Example/MouseDragger.cs.meta new file mode 100644 index 0000000..dcc6a81 --- /dev/null +++ b/TestVelGameServer/Assets/Samples/VelNet/1.0.4/Example/MouseDragger.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c9d312e1088769143a72b0c13d5aee32 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/TestVelGameServer/Assets/Samples/VelNet/1.0.4/Example/PlayerController.cs b/TestVelGameServer/Assets/Samples/VelNet/1.0.4/Example/PlayerController.cs index b33a15f..3420680 100644 --- a/TestVelGameServer/Assets/Samples/VelNet/1.0.4/Example/PlayerController.cs +++ b/TestVelGameServer/Assets/Samples/VelNet/1.0.4/Example/PlayerController.cs @@ -36,7 +36,7 @@ namespace VelNet { foreach (KeyValuePair kvp in VelNetManager.instance.objects) { - Owner.TakeOwnership(kvp.Key); + VelNetManager.TakeOwnership(kvp.Key); } } @@ -47,7 +47,7 @@ namespace VelNet // don't destroy player objects if (!kvp.Value.ownershipLocked) { - Owner.NetworkDestroy(kvp.Key); + VelNetManager.NetworkDestroy(kvp.Key); } } } diff --git a/TestVelGameServer/Assets/Samples/VelNet/1.0.4/Example/PlayerPrefab.prefab b/TestVelGameServer/Assets/Samples/VelNet/1.0.4/Example/PlayerPrefab.prefab index a82c290..8c7fb3c 100644 --- a/TestVelGameServer/Assets/Samples/VelNet/1.0.4/Example/PlayerPrefab.prefab +++ b/TestVelGameServer/Assets/Samples/VelNet/1.0.4/Example/PlayerPrefab.prefab @@ -32,7 +32,7 @@ Transform: m_GameObject: {fileID: 6139051692386484099} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 0.1, y: 0.1, z: 0.1} + m_LocalScale: {x: 0.5, y: 0.5, z: 0.5} m_Children: [] m_Father: {fileID: 0} m_RootOrder: 0 @@ -117,8 +117,8 @@ MonoBehaviour: isSceneObject: 0 syncedComponents: - {fileID: -4404668399269848200} - - {fileID: 1181612843795795320} - {fileID: 7564913803199044469} + - {fileID: 1181612843795795320} --- !u!114 &-4404668399269848200 MonoBehaviour: m_ObjectHideFlags: 0 diff --git a/TestVelGameServer/Assets/Samples/VelNet/1.0.4/Example/test.unity b/TestVelGameServer/Assets/Samples/VelNet/1.0.4/Example/test.unity index 52c9e3e..5c5a1e3 100644 --- a/TestVelGameServer/Assets/Samples/VelNet/1.0.4/Example/test.unity +++ b/TestVelGameServer/Assets/Samples/VelNet/1.0.4/Example/test.unity @@ -38,7 +38,7 @@ RenderSettings: m_ReflectionIntensity: 1 m_CustomReflection: {fileID: 0} m_Sun: {fileID: 0} - m_IndirectSpecularColor: {r: 0.44657874, g: 0.49641275, b: 0.5748172, a: 1} + m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1} m_UseRadianceAmbientProbe: 0 --- !u!157 &3 LightmapSettings: @@ -148,7 +148,7 @@ MonoBehaviour: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2034436} - m_Enabled: 1 + m_Enabled: 0 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} m_Name: @@ -469,7 +469,7 @@ MonoBehaviour: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 162005663} - m_Enabled: 1 + m_Enabled: 0 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} m_Name: @@ -851,7 +851,7 @@ MonoBehaviour: m_CaretColor: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} m_CustomCaretColor: 0 m_SelectionColor: {r: 0.65882355, g: 0.80784315, b: 1, a: 0.7529412} - m_Text: + m_Text: TEST USER m_CaretBlinkRate: 0.85 m_CaretWidth: 1 m_ReadOnly: 0 @@ -1108,7 +1108,7 @@ MonoBehaviour: m_CaretColor: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} m_CustomCaretColor: 0 m_SelectionColor: {r: 0.65882355, g: 0.80784315, b: 1, a: 0.7529412} - m_Text: + m_Text: 0 m_CaretBlinkRate: 0.85 m_CaretWidth: 1 m_ReadOnly: 0 @@ -1241,6 +1241,7 @@ GameObject: - component: {fileID: 903768657} - component: {fileID: 903768656} - component: {fileID: 903768655} + - component: {fileID: 903768654} m_Layer: 0 m_Name: Main Camera m_TagString: MainCamera @@ -1248,6 +1249,20 @@ GameObject: m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 +--- !u!114 &903768654 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 903768653} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: c9d312e1088769143a72b0c13d5aee32, type: 3} + m_Name: + m_EditorClassIdentifier: + draggableTags: + - TestSphere --- !u!81 &903768655 AudioListener: m_ObjectHideFlags: 0 @@ -1834,7 +1849,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 03a4d4e1a7fd74c7ab2eccca4ce168db, type: 3} m_Name: m_EditorClassIdentifier: - host: 129.159.107.234 + host: velnet.ugavel.com port: 3290 udpConnected: 0 userid: -1 @@ -2323,8 +2338,8 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: _lastPrefabError: - _isMuted: 0 - _isDeafened: 0 + _isMuted: 1 + _isDeafened: 1 _oneMinusBaseRemoteVoiceVolume: 0 _playbackPrefab: {fileID: 0} _playbackPrefab2: {fileID: 1041743830464418, guid: 7b8de751a39894b0c8e1cfc9ef961a5b, type: 3} @@ -2454,7 +2469,7 @@ MonoBehaviour: m_HorizontalOverflow: 1 m_VerticalOverflow: 0 m_LineSpacing: 1 - m_Text: + m_Text: 0 --- !u!222 &1484033258 CanvasRenderer: m_ObjectHideFlags: 0 @@ -2616,7 +2631,7 @@ MonoBehaviour: m_HorizontalOverflow: 1 m_VerticalOverflow: 0 m_LineSpacing: 1 - m_Text: + m_Text: TEST USER --- !u!1 &1679565283 GameObject: m_ObjectHideFlags: 0 @@ -3392,6 +3407,10 @@ PrefabInstance: m_Modification: m_TransformParent: {fileID: 0} m_Modifications: + - target: {fileID: 3951900052977689805, guid: 6e4a023f70e01405e8b249a4488fe319, type: 3} + propertyPath: isSceneObject + value: 1 + objectReference: {fileID: 0} - target: {fileID: 8565720275311462453, guid: 6e4a023f70e01405e8b249a4488fe319, type: 3} propertyPath: m_Name value: TestNetworkedGameObject diff --git a/TestVelGameServer/Packages/VelNetUnity/Runtime/NetworkObject.cs b/TestVelGameServer/Packages/VelNetUnity/Runtime/NetworkObject.cs index 15c4de1..aa39b78 100644 --- a/TestVelGameServer/Packages/VelNetUnity/Runtime/NetworkObject.cs +++ b/TestVelGameServer/Packages/VelNetUnity/Runtime/NetworkObject.cs @@ -1,5 +1,8 @@ using System.Collections.Generic; using System.Linq; +#if UNITY_EDITOR +using UnityEditor; +#endif using UnityEngine; namespace VelNet @@ -9,10 +12,11 @@ namespace VelNet /// public class NetworkObject : MonoBehaviour { - [Header("NetworkObject properties")] - public VelNetPlayer owner; + [Header("NetworkObject properties")] public VelNetPlayer owner; + [Tooltip("Whether this object's ownership is transferrable. Should be true for player objects.")] public bool ownershipLocked; + public bool IsMine => owner != null && owner.isLocal; /// @@ -61,4 +65,44 @@ namespace VelNet syncedComponents[int.Parse(identifier)].ReceiveBytes(message); } } + +#if UNITY_EDITOR + /// + /// Sets up the interface for the CopyTransform script. + /// + [CustomEditor(typeof(NetworkObject))] + public class NetworkObjectEditor : Editor + { + public override void OnInspectorGUI() + { + NetworkObject t = target as NetworkObject; + + EditorGUILayout.Space(); + + if (t == null) return; + + EditorGUILayout.HelpBox("Network Object. One per prefab pls.\nAssign components to the list to be synced.", MessageType.Info); + + EditorGUI.BeginDisabledGroup(true); + EditorGUILayout.Toggle("IsMine", t.IsMine); + EditorGUILayout.TextField("Owner ID", t.owner?.userid.ToString() ?? "No owner"); + EditorGUI.EndDisabledGroup(); + EditorGUILayout.Space(); + + if (GUILayout.Button("Find Network Components and add backreferences.")) + { + NetworkComponent[] comps = t.GetComponents(); + t.syncedComponents = comps.ToList(); + foreach (NetworkComponent c in comps) + { + c.networkObject = t; + } + } + + EditorGUILayout.Space(); + + DrawDefaultInspector(); + } + } +#endif } \ No newline at end of file diff --git a/TestVelGameServer/Packages/VelNetUnity/Runtime/Util/NetworkSerializedObject.cs b/TestVelGameServer/Packages/VelNetUnity/Runtime/Util/NetworkSerializedObject.cs index ef1aa14..13037d1 100644 --- a/TestVelGameServer/Packages/VelNetUnity/Runtime/Util/NetworkSerializedObject.cs +++ b/TestVelGameServer/Packages/VelNetUnity/Runtime/Util/NetworkSerializedObject.cs @@ -6,10 +6,10 @@ namespace VelNet { public abstract class NetworkSerializedObject : NetworkComponent { - [FormerlySerializedAs("updateRateHz")] [Tooltip("Send rate of this object")] + [Tooltip("Send rate of this object. This caps out at the framerate of the game.")] public float serializationRateHz = 30; - private void Start() + private void Awake() { StartCoroutine(SendMessageUpdate()); } @@ -23,11 +23,11 @@ namespace VelNet SendBytes(SendState()); } - yield return new WaitForSeconds(1 / serializationRateHz); + yield return new WaitForSeconds(1f / serializationRateHz); } // ReSharper disable once IteratorNeverReturns } - + public override void ReceiveBytes(byte[] message) { ReceiveState(message); diff --git a/TestVelGameServer/Packages/VelNetUnity/Runtime/Util/SyncTransform.cs b/TestVelGameServer/Packages/VelNetUnity/Runtime/Util/SyncTransform.cs index 231a0eb..162a9c9 100644 --- a/TestVelGameServer/Packages/VelNetUnity/Runtime/Util/SyncTransform.cs +++ b/TestVelGameServer/Packages/VelNetUnity/Runtime/Util/SyncTransform.cs @@ -10,21 +10,47 @@ namespace VelNet [AddComponentMenu("VelNet/VelNet Sync Transform")] public class SyncTransform : NetworkSerializedObject { - public Vector3 targetPosition; - public Quaternion targetRotation; - public float smoothness = .1f; + public bool useLocalTransform; + private Vector3 targetPosition; + private Quaternion targetRotation; + private float distanceAtReceiveTime; + private float angleAtReceiveTime; + + private void Start() + { + if (useLocalTransform) + { + targetPosition = transform.localPosition; + targetRotation = transform.localRotation; + } + else + { + targetPosition = transform.position; + targetRotation = transform.rotation; + } + } + + /// + /// This gets called at serializationRateHz when the object is locally owned + /// + /// The state of this object to send across the network protected override byte[] SendState() { using MemoryStream mem = new MemoryStream(); using BinaryWriter writer = new BinaryWriter(mem); - writer.Write(transform.position); - writer.Write(transform.rotation); + writer.Write(transform.localPosition); + writer.Write(transform.localRotation); return mem.ToArray(); } + /// + /// This gets called whenever a message about the state of this object is received. + /// Usually at serializationRateHz. + /// + /// The network state of this object protected override void ReceiveState(byte[] message) { using MemoryStream mem = new MemoryStream(message); @@ -32,13 +58,50 @@ namespace VelNet targetPosition = reader.ReadVector3(); targetRotation = reader.ReadQuaternion(); + + // record the distance from the target for interpolation + if (useLocalTransform) + { + distanceAtReceiveTime = Vector3.Distance(targetPosition, transform.localPosition); + angleAtReceiveTime = Quaternion.Angle(targetRotation, transform.localRotation); + } + else + { + distanceAtReceiveTime = Vector3.Distance(targetPosition, transform.position); + angleAtReceiveTime = Quaternion.Angle(targetRotation, transform.rotation); + } } private void Update() { if (IsMine) return; - transform.position = Vector3.Lerp(transform.position, targetPosition, 1 / smoothness / serializationRateHz); - transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, 1 / smoothness / serializationRateHz); + + if (useLocalTransform) + { + transform.localPosition = Vector3.MoveTowards( + transform.localPosition, + targetPosition, + Time.deltaTime * distanceAtReceiveTime * serializationRateHz + ); + transform.localRotation = Quaternion.RotateTowards( + transform.localRotation, + targetRotation, + Time.deltaTime * angleAtReceiveTime * serializationRateHz + ); + } + else + { + transform.position = Vector3.MoveTowards( + transform.position, + targetPosition, + Time.deltaTime * distanceAtReceiveTime * serializationRateHz + ); + transform.rotation = Quaternion.RotateTowards( + transform.rotation, + targetRotation, + Time.deltaTime * angleAtReceiveTime * serializationRateHz + ); + } } } } \ No newline at end of file diff --git a/TestVelGameServer/Packages/VelNetUnity/Runtime/VelNetManager.cs b/TestVelGameServer/Packages/VelNetUnity/Runtime/VelNetManager.cs index c8b2a41..7fe36d9 100644 --- a/TestVelGameServer/Packages/VelNetUnity/Runtime/VelNetManager.cs +++ b/TestVelGameServer/Packages/VelNetUnity/Runtime/VelNetManager.cs @@ -7,6 +7,7 @@ using System.Text; using System.Threading; using UnityEngine; using System.Net; +using UnityEngine.SceneManagement; namespace VelNet { @@ -104,12 +105,17 @@ namespace VelNet } instance = this; + + SceneManager.sceneLoaded += (scene, mode) => + { + // add all local network objects + sceneObjects = FindObjectsOfType().Where(o=>o.isSceneObject).ToArray(); + }; } private IEnumerator Start() { ConnectToTcpServer(); - sceneObjects = FindObjectsOfType(); //add all local network objects yield return null; try @@ -169,6 +175,7 @@ namespace VelNet // we clear the list, but will recreate as we get messages from people in our room players.Clear(); + masterPlayer = null; if (m.text != "") { @@ -200,7 +207,7 @@ namespace VelNet objects .Where(kvp => kvp.Value == null || !kvp.Value.isSceneObject) .Select(o => o.Key) - .ToList().ForEach(DeleteNetworkObject); + .ToList().ForEach(NetworkDestroy); // empty all the groups foreach (string group in instance.groups.Keys) @@ -246,7 +253,7 @@ namespace VelNet // I'm the local master player, so can take ownership immediately else if (me.isLocal && me == masterPlayer) { - me.TakeOwnership(kvp.Key); + TakeOwnership(kvp.Key); } // the master player left, so everyone should set the owner null (we should get a new master shortly) else if (players[m.sender] == masterPlayer) @@ -257,7 +264,7 @@ namespace VelNet } // TODO this may check for ownership in the future. We don't need ownership here - deleteObjects.ForEach(DeleteNetworkObject); + deleteObjects.ForEach(NetworkDestroy); players.Remove(m.sender); } @@ -683,18 +690,43 @@ namespace VelNet instance.objects.Add(newObject.networkId, newObject); } - public void DeleteNetworkObject(string networkId) + public static void NetworkDestroy(string networkId) { - if (!objects.ContainsKey(networkId)) return; - NetworkObject obj = objects[networkId]; + if (!instance.objects.ContainsKey(networkId)) return; + NetworkObject obj = instance.objects[networkId]; if (obj == null) return; if (obj.isSceneObject) { - deletedSceneObjects.Add(networkId); + instance.deletedSceneObjects.Add(networkId); } Destroy(obj.gameObject); - objects.Remove(networkId); + instance.objects.Remove(networkId); + } + + /// + /// Takes local ownership of an object by id. + /// + /// Network ID of the object to transfer + /// True if successfully transferred, False if transfer message not sent + public static bool TakeOwnership(string networkId) + { + // local player must exist + if (LocalPlayer == null) return false; + + // obj must exist + if (!instance.objects.ContainsKey(networkId)) return false; + + // if the ownership is locked, fail + if (instance.objects[networkId].ownershipLocked) return false; + + // immediately successful + instance.objects[networkId].owner = LocalPlayer; + + // must be ordered, so that ownership transfers are not confused. Also sent to all players, so that multiple simultaneous requests will result in the same outcome. + SendTo(MessageType.ALL_ORDERED, "6," + networkId); + + return true; } } } \ No newline at end of file diff --git a/TestVelGameServer/Packages/VelNetUnity/Runtime/VelNetPlayer.cs b/TestVelGameServer/Packages/VelNetUnity/Runtime/VelNetPlayer.cs index 0fb7216..fae5623 100644 --- a/TestVelGameServer/Packages/VelNetUnity/Runtime/VelNetPlayer.cs +++ b/TestVelGameServer/Packages/VelNetUnity/Runtime/VelNetPlayer.cs @@ -113,14 +113,14 @@ namespace VelNet { string networkId = sections[1]; - manager.DeleteNetworkObject(networkId); + VelNetManager.NetworkDestroy(networkId); break; } case "9": //deleted scene objects { for (int k = 1; k < sections.Length; k++) { - manager.DeleteNetworkObject(sections[k]); + VelNetManager.NetworkDestroy(sections[k]); } break; @@ -146,9 +146,12 @@ namespace VelNet VelNetManager.SendTo(VelNetManager.MessageType.OTHERS, "5," + obj.networkId + "," + identifier + "," + Convert.ToBase64String(data), reliable); } - /// - /// TODO could move this to a static method in VelNetManager - /// + public void SendSceneUpdate() + { + VelNetManager.SendTo(VelNetManager.MessageType.OTHERS, "9," + string.Join(",", manager.deletedSceneObjects)); + } + + [Obsolete("Use VelNetManager.NetworkDestroy() instead.")] public void NetworkDestroy(string networkId) { // must be the local owner of the object to destroy it @@ -158,10 +161,8 @@ namespace VelNet VelNetManager.SendTo(VelNetManager.MessageType.ALL_ORDERED, "8," + networkId); } - /// - /// TODO could move this to a static method in VelNetManager - /// /// True if successful, False if failed to transfer ownership + [Obsolete("Use VelNetManager.TakeOwnership() instead.")] public bool TakeOwnership(string networkId) { // must exist and be the the local player @@ -169,19 +170,14 @@ namespace VelNet // if the ownership is locked, fail if (manager.objects[networkId].ownershipLocked) return false; - + // immediately successful manager.objects[networkId].owner = this; // must be ordered, so that ownership transfers are not confused. Also sent to all players, so that multiple simultaneous requests will result in the same outcome. VelNetManager.SendTo(VelNetManager.MessageType.ALL_ORDERED, "6," + networkId); - - return true; - } - public void SendSceneUpdate() - { - VelNetManager.SendTo(VelNetManager.MessageType.OTHERS, "9," + string.Join(",", manager.deletedSceneObjects)); + return true; } } } \ No newline at end of file