Search Unity

[SOLVED] Trying to Synchronise the Random Sprite of a Static Network Object

Discussion in 'Multiplayer' started by alexander_q, Sep 10, 2014.

  1. alexander_q

    alexander_q

    Joined:
    Feb 7, 2014
    Posts:
    32
    I am placing a present object during runtime, and trying to have it synchronised on server and client. I instantiate it like so:

    Code (CSharp):
    1. void Update(){
    2.     if(Input.GetKeyDown(KeyCode.Alpha7)){
    3.         Network.Instantiate(networkPrefab,transform.position,Quaternion.identity,0);
    4.     }
    5. }
    This code runs on a manager object. I've tried it both with and without a Networkview component, without any change in effect.

    At start, the present does:

    Code (CSharp):
    1. void Start(){
    2.     LootManager = GameObject.FindGameObjectWithTag("LootManager").GetComponent<lootController>();
    3.     presentType = Random.Range(0,18);
    4.  
    5.     if(networkManager.connected == true && networkView.isMine){
    6.         networkView.RPC("SetSprite",RPCMode.AllBuffered,presentType);
    7.     }  
    8. }
    When the RPC is received, the present calls a coroutine:

    Code (CSharp):
    1. [RPC]
    2. void SetSprite(int presType){
    3.     StartCoroutine("SetSpriteDelay",presType);
    4. }
    Code (CSharp):
    1. IEnumerator SetSpriteDelay(int presType){
    2.     yield return new WaitForSeconds(1.0f);
    3.     gameObject.GetComponent<SpriteRenderer>().sprite = LootManager.presentSprites[presType];
    4. }
    I had been changing the sprite during "SetSprite()", but it seemed like 90% of the time, the present had not been instantiated by the Network.Instantiate before the RPC call to SetSprite had arrived, as if the calls were arriving out of order. Thus, I created the delay to compensate.

    However, this delay will most often be too much, and sometimes be not enough to ensure synchronization. What is a better way to do this? Is there a way to force the RPCs to be in order?
     
  2. appels

    appels

    Joined:
    Jun 25, 2010
    Posts:
    2,687
    You can send data right after the instantiate:
    Code (CSharp):
    1. GameObject player = (GameObject)Network.Instantiate(networkPrefab,transform.position,Quaternion.identity,0);
    2. player.networkView.RPC("SetSprite", RPCMode.AllBuffered, Random.Range(0,18));
     
  3. alexander_q

    alexander_q

    Joined:
    Feb 7, 2014
    Posts:
    32
    Sure, but shouldn't the RPC in the start function of the instantiated object go after, anyway?

    In any case, I get...

    ... at the line...

    Code (CSharp):
    1. gameObject.GetComponent<SpriteRenderer>().sprite = LootManager.presentSprites[presType];
    It's as if the object is instantiated (since it can run the code) but the sprite renderer isn't ready yet. This is as it was before. A delay fixes it, but is not optimal.
     
    Last edited: Sep 10, 2014
  4. appels

    appels

    Joined:
    Jun 25, 2010
    Posts:
    2,687
    You need to debug that null ref. Try to find out what's actualy happening.
    I don't see why a delay would be needed to make it working.
     
  5. CloudSpark

    CloudSpark

    Joined:
    Aug 17, 2014
    Posts:
    6
    Keep in mind that a function can be called AFTER the object itself is initialized, but BEFORE all the other components attached to the GameObject are initialized. With that in mind:

    Code (csharp):
    1.  
    2. void Start(){
    3.     LootManager = GameObject.FindGameObjectWithTag("LootManager").GetComponent<lootController>();
    4.     presentType = Random.Range(0,18);
    5.    if(networkManager.connected == true && networkView.isMine){
    6.         networkView.RPC("SetSprite",RPCMode.AllBuffered,presentType);
    7.    }
    8. }
    9.  
    Code (csharp):
    1.  
    2. [RPC]
    3. void SetSprite(int presType){
    4.    StartCoroutine(SetSprite(presType));
    5. }
    6.  
    7. IEnumerator SetSprite(int presType){
    8.     while(GetComponent<SpriteRenderer>() == null) yield return null;  //just wait for the sprite renderer to be initialized
    9.     gameObject.GetComponent<SpriteRenderer>().sprite = LootManager.presentSprites[presType];
    10. }
    11.  
    12.  
    Note: this assumes that the SpriteRenderer is actually the null item - if for example, the LootManager or the presentSprites is null, this won't help.
     
  6. alexander_q

    alexander_q

    Joined:
    Feb 7, 2014
    Posts:
    32
    Turns out it wasn't the SpriteRenderer after all - it WAS the loot manager. My interpretation of this is that the RPC for the present is received and processed either before or during the Start() event, and references the loot manager before the reference is ready. Does this sound about right?