Best way to sync client UI?

So I have network objects called Minis, some of which are controlled by clients. They have a component attached to them called Sheet. This Sheet (as it is now) is just a monobehavior. Each Mini has a Sheet and each sheet has a Mini.

image

I want each client’s UI’s elements to reflect the Mini’s Sheet that they control. So, if a Mini’s Sheet’s hitpoints change, I want that change to change the UI healthbar.

Note: the mini is not the player prefab. Which is why I’m not sure what the best way is to go about this.

Mini.cs

public class Mini : NetworkBehaviour
{
    [SerializeField] private Sheet sheet;
    private ulong ownerClientId; //Is set when the server spawns the Mini.

    public ulong GetClientId() => ownerClientId;

    //...stuff.
}

Sheet.cs

public class Sheet : Monobehaviour
{
    [SerializeField] private Mini mini;

    public readonly int HitpointsFull = 20;
    public int HitpointsRemaining { get; private set; }

    private void Start()
    {
        HitpointsRemaining = HitpointsFull;
    }

    public void TakeDamage(int damage)
    {
        HitpointsRemaining = Mathf.Clamp(HitpointsRemaining - damage, 0, HitpointsFull);

        //What to put here?
    }
}

UIPlayerController.cs

public class UIPlayerController : MonoBehaviour
{
    [SerializeField] private GameObject healthbar;

    public void SetHealth(float health) => healthbar.transform.Find("HealthbarRemaining").GetComponent<Image>().fillAmount = health;
}

Thank you all in advance for your help!

Well, you need the usual:

  • when an IsOwner player spawns and despawns, it sends corresponding events
  • Mini listens to those events, and thus get the player reference
  • from then on it’s just either polling on the Minis side or subscribe to further player events such as “whatever value” changed.

Don’t unnecessarily overcomplicate the simple things. :wink:

    [SerializeField] private Image healthbarImage;

    public void SetHealth(float health) => healthbarImage.fillAmount = health;

And in general: do not “find” by string. It’s prone to break in numerous ways.

2 Likes

I figured events were the best way. I just haven’t ever used them. Would you recommend Unity’s event system or the regular ones?

public event Action is all you need.

1 Like

Okay, got it. So I made these changes. Unfortunately it only works when player 2 (red client) damages player 1 (green host). I made a video to show you. It’s after the code.

Again, the minis are not player prefabs (in case that’s relevant). The player prefab makes requests to the server to do things with the mini(s) that are theirs.

Sheet.cs

public class Sheet : NetworkBehaviour
{
    [SerializeField] private Mini mini;

    public readonly int HitpointsFull = 20;
    public static event Action<float> HitpointChangedEvent;
    public int HitpointsRemaining { get; private set; }
    public int AC { get; private set; }

    private void Start()
    {
        HitpointsRemaining = HitpointsFull;
        AC = 10;
        Weapon = new Weapon(5);
    }

    public void TakeDamage(int damage)
    {
        HitpointsRemaining = Mathf.Clamp(HitpointsRemaining - damage, 0, HitpointsFull);

        if (HitpointsRemaining == 0) Die();

        if (!IsOwner) return;

        if (mini.GetOwnerType() == Owner.player)
            HitpointChangedEvent?.Invoke((float)HitpointsRemaining / (float)HitpointsFull);
    }

    public void DealDamage(Mini target, int damage)
    {
        Sheet targetSheet = target.GetComponent<Sheet>();

        if (11 > targetSheet.AC)
        {
            targetSheet.TakeDamage(damage);
            Debug.Log($"{name} deals {damage} damage to {target.name}!");
        }
        else
            Debug.Log("Missed!");
    }

UIPlayerController.cs

public class UIPlayerController : MonoBehaviour
{
    [SerializeField] private Image healthbarRemaining;

    private void SetHealthbarRemaining(float health)
    {
        healthbarRemaining.fillAmount = health;
    }

    private void OnEnable()
    {
        Sheet.HitpointChangedEvent += SetHealthbarRemaining;
    }

    private void OnDisable()
    {
        Sheet.HitpointChangedEvent -= SetHealthbarRemaining;
    }
}

Here are the logs since you can’t really see them.