Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

Question Netcode For Game Objects - Editing Sprite Information

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

  1. Jozzuph

    Jozzuph

    Joined:
    Sep 12, 2019
    Posts:
    33
    Hey, I'm trying to edit sprite information for my game so all clients can see the edit.

    My Player Prefab is currently set up with Blink and Player scripts. When an entity takes damage, it should blink red on all clients. But it only blinks red on its own.

    The Player Script initializes the Blink script here:

    Code (CSharp):
    1. #region Script Variables
    2.     public MoveVelocity entityMovement;
    3.     public Coroutine routineKnockback;
    4.     public Rigidbody2D rigidBody;
    5.     public Blink blinker;
    6.     #endregion
    7.  
    8.     public void Awake()
    9.     {
    10.         InitializeStats();
    11.  
    12.         entityMovement = GetComponent<MoveVelocity>();
    13.         rigidBody = GetComponent<Rigidbody2D>();
    14.         blinker = GetComponent<Blink>();
    15.     }
    The Player Script handles attacking here:
    Code (CSharp):
    1. public virtual void OnTriggerEnter2D(Collider2D other)
    2.     {
    3.         //Check if this objects hurt box collided with another objects hitbox, if it does get the objects stats and deal damage.
    4.         if (other.gameObject.CompareTag("Hitbox"))
    5.         {
    6.             Debug.Log("Hit enemy " + other.name + " for " + _attackDamage.Value + " damage.");
    7.             EntityStats hitEntityStats = other.transform.root.GetComponent<EntityStats>();
    8.  
    9.             //Force the other entity to Calculate its Damage if it has stats
    10.             if (hitEntityStats != null)
    11.             {
    12.                 hitEntityStats.TakeDamage(_attackDamage.Value, _knockbackPower.Value, transform.position);
    13.             }
    14.         }
    15.     }
    The Player Script handles taking damage and calling the Blink here (Inside the TakeDamage function):

    Code (CSharp):
    1. //Deal damage
    2.         currentHealth -= damage;
    3.         _currentHealth.Value = currentHealth;
    4.         blinker.StartBlink();
    This is the Blink Script:
    Code (CSharp):
    1. using System.Collections;
    2. using Unity.Netcode;
    3. using UnityEngine;
    4.  
    5. public class Blink : NetworkBehaviour
    6. {
    7.     private SpriteRenderer spriteBod;
    8.     private Coroutine damagedBlink;
    9.     public float blinkTime = 0.2f;
    10.  
    11.     public Color originalColor;
    12.     public Color blinkColor = 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 = new NetworkVariable<Color>(Color.white, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
    14.     [HideInInspector] public NetworkVariable<Color> _damagedColor = new NetworkVariable<Color>(Color.white, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
    15.  
    16.     public void Awake()
    17.     {
    18.         spriteBod = transform.Find("Body").GetComponent<SpriteRenderer>();
    19.         originalColor = spriteBod.color;
    20.  
    21.         _originalColor.Value = originalColor;
    22.         _damagedColor.Value = blinkColor;
    23.     }
    24.     public virtual void StartBlink()
    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(Blinking());
    33.     }
    34.  
    35.     private IEnumerator Blinking()
    36.     {
    37.         spriteBod.color = _damagedColor.Value;
    38.         yield return new WaitForSeconds(blinkTime); //the actual wait time
    39.         spriteBod.color = _originalColor.Value;
    40.     }
    41. }
    42.  
    This is an error produced when an entity takes damage:

    EntityStats.TakeDamage (System.Single damage) (at Assets/Scripts/Stats/EntityStats.cs:195)
    EntityStats.TakeDamage (System.Single damage, System.Single attackerKnockback, UnityEngine.Vector3 attackerPos) (at Assets/Scripts/Stats/EntityStats.cs:217)
    EntityStats.OnTriggerEnter2D (UnityEngine.Collider2D other) (at Assets/Scripts/Stats/EntityStats.cs:166)
     
  2. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    674
    I don't see where you're changing the network variable value, but I can see some other potential issues as well so it might be best to wind things back a bit and just get the network variables set up correctly. This is a simple example of changing a value on one client and having it appear on the other.
    Code (CSharp):
    1. public class Blink : NetworkBehaviour
    2. {
    3.     public NetworkVariable<Color> bodyColor = new NetworkVariable<Color>(Color.white, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
    4.  
    5.     public override void OnNetworkSpawn()
    6.     {
    7.         base.OnNetworkSpawn();
    8.  
    9.         if (!IsLocalPlayer)
    10.         {
    11.             bodyColor.OnValueChanged += OnChangeBodyColor;
    12.         }
    13.     }
    14.  
    15.     private void OnChangeBodyColor(Color previousValue, Color newValue)
    16.     {
    17.         Debug.Log("OnChangeBodyColor: " + newValue);
    18.     }
    19.  
    20.     public virtual void ChangeColor()
    21.     {
    22.         bodyColor.Value = new Color(Random.Range(0,255), Random.Range(0, 255), Random.Range(0, 255));
    23.  
    24.         Debug.Log("ChangeColor: " + bodyColor.Value);
    25.     }
    26. }
    Keep in mind with a client and host there are two Player objects on each so you'll need to make sure you find the correct player to make changes upon. You can get the local player with NetworkManager.Singleton.LocalClient.PlayerObject.
     
  3. Jozzuph

    Jozzuph

    Joined:
    Sep 12, 2019
    Posts:
    33
    Looking at your code here, I think I've been looking at the wrong problem. I corrected the code to this:


    Code (CSharp):
    1. using System.Collections;
    2. using Unity.Netcode;
    3. using UnityEngine;
    4.  
    5. public class Blink : NetworkBehaviour
    6. {
    7.     private SpriteRenderer spriteBod;
    8.     private Coroutine blink;
    9.     private float blinkTime = 0.2f;
    10.  
    11.     private Color originalColor;
    12.     private Color blinkColor = new((255 / 255), (89 / 255), (94 / 255)); //colors are normalized from 0 - 1 so this needs to be done;
    13.     [HideInInspector] public NetworkVariable<Color> _bodyColor = new NetworkVariable<Color>(Color.white, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
    14.  
    15.  
    16.     public override void OnNetworkSpawn()
    17.     {
    18.         if (!IsOwner) return;
    19.  
    20.         _bodyColor.OnValueChanged += (Color prevColor, Color newColor) =>
    21.         {
    22.             spriteBod.color = _bodyColor.Value;
    23.             Debug.Log(spriteBod.color);
    24.         };
    25.     }
    26.     public void Awake()
    27.     {
    28.         spriteBod = transform.Find("Body").GetComponent<SpriteRenderer>();
    29.         originalColor = spriteBod.color;
    30.     }
    31.     public virtual void StartBlink()
    32.     {
    33.         //Resets color so it doesn't keep tacking if currently blinking
    34.         if (blink != null)
    35.         {
    36.             _bodyColor.Value = originalColor;
    37.             StopCoroutine(blink);
    38.         }
    39.         blink = StartCoroutine(Blinking());
    40.     }
    41.  
    42.     private IEnumerator Blinking()
    43.     {
    44.         _bodyColor.Value = blinkColor;
    45.         yield return new WaitForSeconds(blinkTime); //the actual wait time
    46.         _bodyColor.Value = originalColor;
    47.     }
    48. }
    49.  
    However, the error points to specific places in the script where the client does not have permission that I don't know how to fix and didn't catch because the functionality was working correctly.

    The problems it specifies in order are as follows:

    EntityStats.TakeDamage (System.Single damage) (at Assets/Scripts/Stats/EntityStats.cs:193)
    EntityStats.TakeDamage (System.Single damage, System.Single attackerKnockback, UnityEngine.Vector3 attackerPos) (at Assets/Scripts/Stats/EntityStats.cs:215)
    EntityStats.OnTriggerEnter2D (UnityEngine.Collider2D other) (at Assets/Scripts/Stats/EntityStats.cs:165)

    Code (CSharp):
    1. //Deal damage
    2.         currentHealth -= damage;
    3.         _currentHealth.Value = currentHealth; //LINE 193
    4.         blinker.StartBlink();
    5.  
    Code (CSharp):
    1. public virtual void TakeDamage(float damage, float attackerKnockback, Vector3 attackerPos)
    2.     {
    3.         #region Check for Parry
    4.         //If parrying while attacked return without calculating damage
    5.         if (abilityParry != null && abilityParry.isActive) { Debug.Log("ID: " + OwnerClientId + "; Parried"); return; }
    6.         #endregion
    7.  
    8.         TakeDamage(damage); //LINE 215
    9.  
    10.         //if still alive try knockback, and if knockback has a push or pull value, and if it has a rigid body
    11.         if (isAlive && attackerKnockback != 0 && rigidBody != null)
    12.         {
    13.             Vector3 knockDirection = (this.transform.position - attackerPos).normalized;
    14.             //Debug.LogError("knockDirection: " + knockDirection);
    15.             //Debug.DrawLine(transform.position, (transform.position + knockDirection * 10), Color.red, Mathf.Infinity); //show line for knocback
    16.             //Get the correct amount of knockback
    17.             Vector2 knockVelocity = (knockDirection * attackerKnockback);
    18.             knockVelocity -= knockVelocity * (_knockbackResist.Value / 100);
    19.             //Debug.LogError("knockVelocity: " + knockVelocity);
    20.  
    21.             //if the object already has knockback applied this will stop that knockback
    22.             if (routineKnockback != null)
    23.             {
    24.                 StopCoroutine(routineKnockback);
    25.             }
    26.             //apply newest knockback and set it to routinKnockback
    27.             routineKnockback = StartCoroutine(Knockback(knockVelocity));
    28.         }
    29.     }
    Code (CSharp):
    1.  public virtual void OnTriggerEnter2D(Collider2D other)
    2.     {
    3.         //Check if this objects hurt box collided with another objects hitbox, if it does get the objects stats and deal damage.
    4.         if (other.gameObject.CompareTag("Hitbox"))
    5.         {
    6.             Debug.Log("Hit enemy " + other.name + " for " + _attackDamage.Value + " damage.");
    7.             EntityStats hitEntityStats = other.transform.root.GetComponent<EntityStats>();
    8.  
    9.             //Force the other entity to Calculate its Damage if it has stats, and if it is not parrying
    10.             if (hitEntityStats != null)
    11.             {
    12.                 hitEntityStats.TakeDamage(_attackDamage.Value, _knockbackPower.Value, transform.position); //LINE 165
    13.             }
    14.         }
    15.     }
     
    Last edited: Feb 15, 2023
  4. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    674
    Check you have permission to write to _currentHealth and that you're on the correct player.
     
  5. Jozzuph

    Jozzuph

    Joined:
    Sep 12, 2019
    Posts:
    33
    Yeah there is something going wrong here for sure, I’ll try to get more info and send it here in a bit, busy with a few other assignments today though. Thanks!
     
  6. Jozzuph

    Jozzuph

    Joined:
    Sep 12, 2019
    Posts:
    33
    Hey! thanks for all the help; with your example code and advice, I was able to go through and fix everything!

    This was the final blink code for anyone in the future:

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