Search Unity

  1. Check out the Unite LA keynote for updates on the Visual Effect Editor, the FPS Sample, ECS, Unity for Film and more! Watch it now!
    Dismiss Notice
  2. The Unity Pro & Visual Studio Professional Bundle gives you the tools you need to develop faster & collaborate more efficiently. Learn more.
    Dismiss Notice
  3. Improved Prefab workflow (includes Nested Prefabs!), 2D isometric Tilemap and more! Get the 2018.3 Beta now.
    Dismiss Notice
  4. Want more efficiency in your development work? Sign up to receive weekly tech and creative know-how from Unity experts.
    Dismiss Notice
  5. Improve your Unity skills with a certified instructor in a private, interactive classroom. Watch the overview now.
    Dismiss Notice
  6. Want to see the most recent patch releases? Take a peek at the patch release page.
    Dismiss Notice

SyncVar not updating on clients when used with hook

Discussion in 'Connected Games' started by Donlod, Jul 15, 2017.

  1. Donlod

    Donlod

    Joined:
    Mar 14, 2016
    Posts:
    2
    Im working through this tutorial https://unity3d.com/de/learn/tutorials/topics/multiplayer-networking
    SyncVar is used to synchronize the current health values applied on the server with the clients. Because the healthbars are not synchronized hook= is used to update them on the clients in chapter 14. As soon as hook= is used the currentHealth values are no longer updated in (the inspector of) the clients but only in the servers. The healthbars work correctly.

    This is the code so far:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5. using UnityEngine.Networking;
    6.  
    7. public class Health : NetworkBehaviour {
    8.  
    9.     public const int maxHealth = 100;
    10.  
    11.     [SyncVar(hook = "OnChangeHealth")]
    12.     public int currentHealth = maxHealth;
    13.     public RectTransform healthBar;
    14.  
    15.     public void TakeDamage(int amount)
    16.     {
    17.         if (!isServer)
    18.         {
    19.             return;
    20.         }
    21.  
    22.         currentHealth -= amount;
    23.         if(currentHealth <= 0)
    24.         {
    25.             currentHealth = 0;
    26.             RpcRepawn();
    27.         }
    28.  
    29.     }
    30.  
    31.     [ClientRpc]
    32.     void RpcRepawn()
    33.     {
    34.         if(isLocalPlayer)
    35.         {
    36.             transform.position = Vector3.zero;
    37.         }
    38.     }
    39.  
    40.     void OnChangeHealth(int currentHealth)
    41.     {
    42.         healthBar.sizeDelta = new Vector2(currentHealth, healthBar.sizeDelta.y);
    43.     }
    44. }
    45.  
    I dont understand why the values in the inspector wont update anymore. As far as I undestood this, SyncVars are synchronized with the clients and then when they change the function is called.
    If I include a print if the currentHealth is below a threshold in TakeDamage() it wont happen, which means that the value indeed does not update.
    If I reconnect the client the players health values are correct. If the health values change again they wont update until the client reconnects.
     
  2. wobes

    wobes

    Joined:
    Mar 9, 2013
    Posts:
    472

    You should manually assign the new value of health.
    There is an error on official unity post.

    Code (CSharp):
    1. void OnChangeHealth(int newHealth)
    2.     {
    3.         currentHealth = newHealth; //fix
    4.         healthBar.sizeDelta = new Vector2(currentHealth, healthBar.sizeDelta.y);
    5.     }
     
    Donlod likes this.
  3. marcV2g

    marcV2g

    Joined:
    Jan 11, 2016
    Posts:
    115
    monotoan likes this.
  4. Donlod

    Donlod

    Joined:
    Mar 14, 2016
    Posts:
    2
    Thanks for the reply. What do you mean by "error"? Unity Engine bug or error in the tutorial? If this is intended then im confused why because its still a SyncVar isnt it?

    https://docs.unity3d.com/ScriptReference/Networking.SyncVarAttribute-hook.html

    It also seems that the docs and the tutorial are not consistent. In the docs there is written "... a function to be called when the sync var changes value on the client". So for me this means that the server recognizes a change and then synchronizes the new value with the clients. Then the assigned function is called only on the clients. While in the tutorial there is written " These functions are invoked on the Server and all Clients when the value of the SyncVar changes".

    Also if the doc is right, how comes that the function is called on the client even though the value did not change on the client?
     
  5. wobes

    wobes

    Joined:
    Mar 9, 2013
    Posts:
    472
    The value doesn't assign when hook is called. I gave you a fix and an explanation already.
     
    Last edited: Jul 15, 2017
  6. monotoan

    monotoan

    Joined:
    Feb 13, 2015
    Posts:
    11
    Yeah, it really seems like SyncVar hooks are broken. The ideal behavior -- and what the documentation implies -- is that a hook should act like what we'd call a "callback" or "delegate" in many other coding situations. That is, when the value of a SyncVar changes, the hook function fires to execute follow-up actions in response to that change.

    But if a SyncVar Hook blocks the updating of the value or fires before it's actually updated (which seems to be the case), then it defeats the whole purpose of the hook in my view. Manually updating the SyncVar value within the hook may work as a fix, but it's a hacky one and doesn't really support good Networking practice. If the whole purpose of SyncVars is that they're consistently and automatically synced across all clients, developers shouldn't be able to accidentally or intentionally block that syncing just by adding a hook.
     
  7. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    3,832
    A SyncVar hook doesn't update the SyncVar itself to allow you the opportunity to still use the old value in your hook function. For example if a SyncVar represented a player's score, and the server increased the score by +2, the client would get the SyncVar hook function called and still has the old score in the SyncVar. So the client can then subtract the old score from the new one, and can throw some cool "+2 Points!" thing on the screen, then update the client version of the SyncVar with the new value.

    Otherwise either the client would need to store another copy of the SyncVar value, or the server would need to call a separate CllientRpc, to get something as simple as telling the client how much a SyncVar has changed instead of just its current value.

    I believe this is as designed behavior, but I don't believe it is in any of the documentation (UNET HLAPI docs are not very thorough).
     
    Last edited: Jan 29, 2018
  8. monotoan

    monotoan

    Joined:
    Feb 13, 2015
    Posts:
    11
    Thanks for the explanation -- I can see the value in that. Personally, I think it's less valuable than having variables with reliable consistent values across all network instances, especially when there are other ways to track variable changes. But the main problem remains that this behavior is not what Unity Documentation or Tutorials would lead anyone to expect. I've logged feedback on the SyncVar hook Script Reference documentation page to highlight this.

    Also, further complicating this is that if a game instance is acting as a Host (i.e. both Server and Client), I believe the SyncVar value for that instance will already be updated when the hook fires for that Client -- but not for any other Guests/Clients. This leads to even more divergent and confusing behavior across the Network. Running one instance as a Host is the most common setup for people who are just learning UNET, so this seems like a pretty big stumbling block.
     
    Last edited: Jan 29, 2018
  9. curiousbrandon

    curiousbrandon

    Joined:
    Sep 22, 2017
    Posts:
    6
    Thanks for the thread. I spent a few hours trying to figure out why SyncVar wasn't working until I found this thread. This really should be added to the documentation for SyncVar and SyncVarAttribute.hook, especially when the attribute changes the way SyncVar works and creates inconsistency.
     
    Joe-Censored likes this.
  10. Tiberius1701

    Tiberius1701

    Joined:
    Sep 16, 2014
    Posts:
    51
    Thank you soooo much for this explanation and fix - luckily I started looking online as soon as I started to get the weird behavior. Hopefully a Unity Mod sees this and updates the tutorial - it is a great tutorial until it gets to this point.
     
  11. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    3,832
    Unet has been deprecated, so don't expect any documentation or tutorial updates for it.
     
  12. Zante

    Zante

    Joined:
    Mar 29, 2008
    Posts:
    334
    This is absolutely backwards and unintuitive!

    It needs to be documented somewhere, for the love of everything holy.

    Thank you for this thread and the investigative work.
     
    Last edited: Sep 25, 2018