Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Question How much are ScriptableObjects supported by Netcode/Multiplayer services?

Discussion in 'Multiplayer' started by WakingDragon, Oct 25, 2022.

  1. WakingDragon

    WakingDragon

    Joined:
    Mar 18, 2018
    Posts:
    34
    Hi,
    I use state machine based on ScriptableObjects (SOs), as well as in various other parts of my game. The issue is that the Client does an action, which can be sent to the Server, and the Server updates the GameState. This updated GameState then needs to be sent back to each Client so they all move on to the next GameState together.
    I guess I could compile a dynamic table of GameStates with an int reference to each state and then transmit the new int, forcing the Clients' state machines to ExitCurrentState and EnterNewState(GameState referenced by int).

    Is this the way I need to be thinking about it, or should is there some greater support for SOs that I haven't managed to find?

    Thanks
    Ben
     
  2. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    4,195
    There's no specific "support" for ScriptableObjects the same way there isn't "support" for MonoBehaviours. These are scripts, any data or logic could be defined in these classes. So you have to write what you need in terms of transferring that state.

    The mechanisms to share that data and perform actions are NetworkVariable<> and RPCs.

    Since you're using Statemachines, the server should be the only one updating the statemachine and changing states. The clients simply do whatever they need when they're in a given state. If the client need to know about the exact moment the state changes, they would either receive a ClientRPC or they act on NetworkVariable.OnValueChanged and do things they have to do locally, like show/hide GUI or animate something.
     
  3. WakingDragon

    WakingDragon

    Joined:
    Mar 18, 2018
    Posts:
    34
    Yes thanks. Although Monobehaviours are kinda "supported" because NetworkBehaviour is derived from them and that seems a very explicit choice to support GameObject-oriented deisgn. I was hoping there was some kind of "NetworkScriptableObject" option that I was missing that would allow passing of the asset references (although not the assets themselves) to clients.
     
  4. Dzzyxx

    Dzzyxx

    Joined:
    Jun 7, 2021
    Posts:
    2
    I played around with this idea in my game what I did was make a new class called guidScriptableObject which is derived from Scriptable object, it pretty much just assigns a unique guid to each scriptable objects that inherit the class, and i made an network variable class which just contains that guid to sync across server. I had a spawnmanager in the server which contained my certain Scriptableobject types for example avatars. When an object is spawned in the game i assign the scriptableobject guid to its network variable and when clients join i just send the guid in a clientrpc and perform the code i need.
    This requires the server/client to have the same game builds unless you assign the guids yourself in an editor script.

    there is probably a way better approach but i thought this was simple enough so i didnt have to fill up my networkmanager prefab list to different objecttypes.


    Code (CSharp):
    1. using System;
    2. using Unity.Collections;
    3. using Unity.Netcode;
    4. using UnityEditor;
    5. using UnityEngine;
    6.  
    7. public struct ScriptableNetworkObject : INetworkSerializable
    8.     {
    9.         public FixedString64Bytes _guid;
    10.         public void NetworkSerialize<T>(BufferSerializer<T> serializer)
    11.             where T : IReaderWriter
    12.         {
    13.             serializer.SerializeValue(ref _guid);
    14.          
    15.         }
    16.  
    17.  
    18. }
    19. public class ScriptableObjectIdAttribute : PropertyAttribute { }
    20.  
    21. #if UNITY_EDITOR
    22. [CustomPropertyDrawer(typeof(ScriptableObjectIdAttribute))]
    23. public class ScriptableObjectIdDrawer : PropertyDrawer {
    24.     public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
    25.         GUI.enabled = false;
    26.         if (string.IsNullOrEmpty(property.stringValue)) {
    27.             property.stringValue = Guid.NewGuid().ToString();
    28.         }
    29.         EditorGUI.PropertyField(position, property, label, true);
    30.         GUI.enabled = true;
    31.     }
    32. }
    33. #endif
    34. /// <summary>
    35.     /// ScriptableObject that stores a GUID for unique identification. The population of this field is implemented
    36.     /// inside an Editor script.
    37.     /// </summary>
    38.     [System.Serializable]
    39.     public abstract class GuidScriptableObject : ScriptableObject
    40.     {
    41.  
    42.  
    43.         [ScriptableObjectId]
    44.         public string Id;
    45.  
    46.      
    47.         void OnValidate()
    48.         {
    49.             if (String.IsNullOrEmpty(Id))
    50.             {
    51.                 Id = Guid.NewGuid().ToString();
    52.             }
    53.         }
    54.      
    55.     }
     
    mishakozlov74 likes this.
  5. mishakozlov74

    mishakozlov74

    Joined:
    Aug 8, 2018
    Posts:
    135
    I faced the same problem and I found that it's much easier to use Addressables for that. It essentially supports referencing things before (and without) loading them, so it's very handy to send a string reference over the network.

    The only problem is that BufferSerializer<T> serializer can't serialize it out of the box, so:

    Code (CSharp):
    1. if (serializer.IsReader) {
    2.     serializer.GetFastBufferReader().ReadValueSafe(out string heistReferenceGuid);
    3.     _heistReference = new AssetReference(heistReferenceGuid);
    4. }
    5. else {
    6.     serializer.GetFastBufferWriter().WriteValueSafe(_heistReference.AssetGUID);
    7. }
    Going even beyond that, I added an extension that can do that for me
    Code (CSharp):
    1. namespace CustomExtensions {
    2.  
    3.     public static class NetworkExtensions {
    4.         public static void SerializeValue<T>(this BufferSerializer<T> reader, ref AssetReference assetReference) where T: IReaderWriter {
    5.             if (reader.IsReader) {
    6.                 reader.GetFastBufferReader().ReadValueSafe(out string guid);
    7.                 assetReference = new AssetReference(guid);
    8.             }
    9.             else {
    10.                 reader.GetFastBufferWriter().WriteValueSafe(assetReference.AssetGUID);
    11.             }
    12.         }
    13.     }
    14. }
    And now it just
    Code (CSharp):
    1. public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter {
    2.     serializer.SerializeValue(ref cuts);
    3.     serializer.SerializeValue(ref _heistReference);
    4. }