Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Multiplayer Recoloring Sprites

Discussion in 'Multiplayer' started by Jozzuph, Feb 13, 2023.

  1. Jozzuph

    Jozzuph

    Joined:
    Sep 12, 2019
    Posts:
    33
    Hey, so essentially, what I want to happen is whenever an entity gets damaged, they blink red, which I have working fine in single player. (Please be beginner friendly, I'm pretty new to networking stuff).

    I'm using Netcode for Game Objects.

    I'm having a problem where the client isn't allowed to write to a variable even though they should have permission. This makes it, so the blink color only applies for the specific client when they get hurt, instead of all clients.

    InvalidOperationException: Client is not allowed to write to this NetworkVariable
    Unity.Netcode.NetworkVariable`1[T].set_Value (T value) (at Library/PackageCache/com.unity.netcode.gameobjects@1.1.0/Runtime/NetworkVariable/NetworkVariable.cs:64)

    This is the script I have for the blinking, and it's called when an entity takes damage.

    Code (CSharp):
    1. using System.Collections;
    2. using Unity.Netcode;
    3. using UnityEngine;
    4.  
    5. public class DamagedBlink : NetworkBehaviour
    6. {
    7.     public SpriteRenderer spriteBod;
    8.     public Coroutine damagedBlink;
    9.     public float blinkTime = 0.2f;
    10.  
    11.     public Color originalColor;
    12.     public Color damagedColor = new((255 / 255), (89 / 255), (94 / 255)); //colors are normalized from 0 - 1 so this needs to be done;
    13.     [HideInInspector] public NetworkVariable<Color> _originalColor;
    14.     [HideInInspector] public NetworkVariable<Color> _damagedColor;
    15.  
    16.     public override void OnNetworkSpawn()
    17.     {
    18.         spriteBod = transform.Find("Body").GetComponent<SpriteRenderer>();
    19.         originalColor = spriteBod.color;
    20.  
    21.         _originalColor = new NetworkVariable<Color>(originalColor, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
    22.         _damagedColor = new NetworkVariable<Color>(damagedColor, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
    23.     }
    24.     public virtual void BlinkHurt()
    25.     {
    26.         //Resets color so it doesn't keep tacking if currently blinking
    27.         if (damagedBlink != null)
    28.         {
    29.             spriteBod.color = _originalColor.Value;
    30.             StopCoroutine(damagedBlink);
    31.         }
    32.         damagedBlink = StartCoroutine(Blink());
    33.     }
    34.  
    35.     public IEnumerator Blink()
    36.     {
    37.         spriteBod.color = _damagedColor.Value;
    38.         yield return new WaitForSeconds(blinkTime); //the actual wait time
    39.         spriteBod.color = _originalColor.Value;
    40.     }
    41. }
     
  2. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    656
    You'll need to spawn DamagedBlink with client ownership, use networkObject.SpawnWithOwnership(clientId) instead of networkObject.Spawn().
     
  3. Jozzuph

    Jozzuph

    Joined:
    Sep 12, 2019
    Posts:
    33
    Can you provide an example of where / how to do this? Thanks for the help; I am very new to this...

    I tried this but its not the Network Object type

    Code (CSharp):
    1. blinker = GetComponent<DamagedBlink>().SpawnWithOwnership(NetworkManager.Singleton.LocalClientId);
    DamgedBlink is just a script connected to the player Prefab.
     
    Last edited: Feb 13, 2023
  4. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    656
    How is DamagedBlink's network object being spawned currently, is the server/host spawning it at runtime or is it already in the scene?

    If it's at runtime instantiate its network prefab and call .NetworkObject.SpawnWithOwnership(clientId) or .GetComponent<NetworkObject>().SpawnWithOwnership(clientId);

    If it's already in the scene I'm not sure as I don't use them but in theory the server/host can call .NetworkObject.ChangeOwnership(clientId) on its network object.

    If DamagedBlink is a component of the Player prefab the client will get ownership of it by default.
     
  5. Jozzuph

    Jozzuph

    Joined:
    Sep 12, 2019
    Posts:
    33
    Yeah DamageBlink is a script on the Player Prefab so I would think the client would automatically get ownership but it’s still having this problem. Essentially how it works is there is a Player script attached to the Player Prefab and Damage Blink script attached to the same hierarchy of the prefab. And the player script uses get component for the damage blink script but still doesn’t have permission for some reason?
     
  6. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    656
    Looking at the code try instantiating the network variables in Awake rather than OnNetworkSpawn in case it's too late there for the permissions to be picked up.
     
  7. Jozzuph

    Jozzuph

    Joined:
    Sep 12, 2019
    Posts:
    33
    Will do, is there a time where I would 100% want to instantiate OnNetworkSpawn rather than Awake or Start?
     
  8. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    656
    I tend to always instantiate network variables when they're declared, network lists can't be instantiated straight away so I do it in Awake. The only network related thing I do in OnNetworkSpawn is add the OnValueChanged and OnListChanged callbacks.
     
  9. Jozzuph

    Jozzuph

    Joined:
    Sep 12, 2019
    Posts:
    33
    With awake, the client can only see itself blink but not anyone else. Same error message... Do you know if there would be a way to do this with Server or Client RPC's or does it still look like something if off? Also should I be checking if it IsOwner, IsClient, or IsLocalPlayer? When would be times for me to use those? Thanks again for the help!

    This is the necessary Player Script code

    Code (CSharp):
    1.     public MoveVelocity entityMovement;
    2.     public Coroutine routineKnockback;
    3.     public Rigidbody2D rigidBody;
    4.     public DamagedBlink blinker;
    5.  
    6.     //Applies function when object spawns
    7.     public override void OnNetworkSpawn()
    8.     {
    9.         InitializeStats();
    10.  
    11.         if (GetComponent<AbilityParry>() != null)
    12.         {
    13.             abilityParry = GetComponent<AbilityParry>();
    14.         }
    15.  
    16.         entityMovement = GetComponent<MoveVelocity>();
    17.         rigidBody = GetComponent<Rigidbody2D>();
    18.     }
    19.  
    20.     public void Awake()
    21.     {
    22.         blinker = GetComponent<DamagedBlink>();
    23.     }
     
    Last edited: Feb 14, 2023
  10. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    656
    With server authority it can decide if a player is hit and change a network variable health value which can be picked up by the client. There's an inherent delay with this and it's a bit of a rabbit hole to dig into which I've not gone into it as I only focus on turn-based gaming. Hopefully someone else can give you some advice on the subject.
     
  11. Jozzuph

    Jozzuph

    Joined:
    Sep 12, 2019
    Posts:
    33
    Thanks for the help, I just straight up can't get this to work right... Hopefully, someone can!