RPC & null reference

Options
M4TT
M4TT
Hello

I will try to explain my issue that I'm facing with RPC.

I'm currently working on a 2D game, and of course I'm implementing the network system.
Basically there are vehicule that have trail renderer which gonna be enabled and disabled continuously ( such as Tron ), and at this point the trail renderer is properly showing for each client so it's fine.

Now if player 2 join after player 1, he would not see the whole trail from player 1, so I was like
" no problem let's add RPC event to send the trail to everyone even if they join later "

At this point, I'm not sure if it's the good answer to this issue, but I guess it is.
And I'm not able to do it properly because I have a null reference on the RPC

There is a part of my code :

private IEnumerator EnableTrail() //<-- used to enabled and disable the trail continuously
{
    while (true)
    {
        yield return new WaitForSeconds(timer);         // we wait a bit
        pv.RPC("TrailUp", RpcTarget.AllBuffered);       // then we said we want to call our mehod for everyone,
                                                        // even if they join later ( pv = GetComponent<PhotonView>() ) 

        yield return new WaitForSeconds(timer);
        pv.RPC("TrailDown", RpcTarget.AllBuffered);

        yield return null;
    }
}

[PunRPC]
void TrailUp()
{
    tr.emitting = true; // <-- null reference appears if there is 2 or more players
                        // ( tr = GetComponentInChildren<TrailRenderer>() )
}

[PunRPC]
void TrailDown()
{
    tr.emitting = false; // <-- null reference appears if there is 2 or more players
}


I'm probably missing something, and I'm stuck since few few hours on this problem that's why I would appreciate any kind of help, thanks!














Comments

  • M4TT
    Options
    Bump!
    Thinking about it, I think it's not the good way to sync the trail with the player who joins later by sending a boolean that macthes with the trail state ( enabled or not )
    Do you guys have any insight for me to achieve that ?
  • M4TT
    M4TT
    edited December 2019
    Options
    Bump!
    I found a solution to my issue, I will store locally in a list the position of each point where the player has been then I will instantiate few gameobject on these position for the player who joins later to make the trail
    But I don't know the best way to perform this ? Using RPC, raisevent, maybe using other way ?

    By the way I already tried to achieve this with sending a "simple" RPC to everyone who joins later like this way bellow, but this doesn't work at all, RPC is sended to people already in the scene but not when someone joins later
    pv.RPC("InstantiateRPC", RpcTarget.AllBuffered);//<-- this line is called every 70ms
    
    
    [PunRPC]
    private void InstantiateRPC()
    {
       StartCoroutine(InstGo());
    }
    
    
    private IEnumerator InstGo()
    {
       GameObject inst = Instantiate(col, spawnPos.transform.position, rb.transform.rotation);
       yield return new WaitForSeconds(.15f);
       inst.GetComponent<Collider2D>().enabled = true;
    }
    


  • M4TT
    M4TT
    edited December 2019
    Options
    Well I finally achieve to instantiate locally the trail of other player when they join the game later.

    Using PUN Doc. and by searching on internet my current system is the next one, when someone joins the game others players send him a RPC with 2 serialized list of transform & rotation, then he can deszerializes the data and locally instantiate GO at these specific position & rotation to create their trail.

    For anyone interested in the code, there is everything you need bellow
    public override void OnPlayerEnteredRoom(Player player)
    {
        StartCoroutine(SendRPC(1, player));
    }
    
    public IEnumerator SendRPC(int requestID, Player p)
    {
        yield return new WaitForSeconds(1); 
    //wait one seconde before sending the rpc, we don't want to send
    // the data before he's instantiated
        if(requestID == 1)
        {
            PlayerController.LocalPlayerInstance.GetComponent<PhotonView>().RPC("SetTrailOnJoin", p,
                                                                                     CustomSerialize(0), CustomSerialize(1));
        }
    }
    
    [PunRPC]
    public void SetTrailOnJoin(Player player, string dataP, string dataR)
    {
    //There are 2 list of data sent through RPC, one for the position (x,y) the other one for the rotation (z). 
    //By the way Data is just a <float, float> list
    //using Data = System.Collections.Generic.KeyValuePair<float, float>;
    
        List<Data> listOf_pos = CustomDeSerializePosition(dataP);
        List<float> listOf_rot = CustomDeSerializeRotation(dataR);
    
        int c_userid = PlayerController.LocalPlayerInstance.GetComponent<PhotonView>().OwnerActorNr;
       //ref to the local ownerActorNumber
    
        if (c_userid == player.ActorNumber) // Just in case but this condition is not necessary due to Player target on the RPC
        {
            for(int i = 0; i < listOf_pos.Count; i++)
            {
                Instantiate(GameObject.FindGameObjectWithTag("wall"), 
                           new Vector3(listOf_pos[i].Key,listOf_pos[i].Value, 0), 
                           new Quaternion(0, 0, listOf_rot[i],0));
            }
        }
    }
    
    //These are my serialization method, I've seen that encoding with ascii should be 
    //way better for network efficiency, I might take a look on that
    // ( bf ref to a  BinaryFormatter )
    public String CustomSerialize(int a = 0)
    {
        string data;
        if (a == 0)
        {
            MemoryStream ms = new MemoryStream();
            bf.Serialize(ms, PlayerController.LocalPlayerInstance.GetComponent<TrailManager>().listOf_pos);
            data = Convert.ToBase64String(ms.GetBuffer());
            Debug.Log("data");
        }
        else if (a == 1)
        {
            MemoryStream ms = new MemoryStream();
            bf.Serialize(ms, PlayerController.LocalPlayerInstance.GetComponent<TrailManager>().listOf_rot);
            data = Convert.ToBase64String(ms.GetBuffer());
            Debug.Log("data1");
        }
        else
            data = "";
    
    
        return data;
    }
    
    public List<Data> CustomDeSerializePosition(string dataP)
    {
        MemoryStream mp = new MemoryStream(Convert.FromBase64String(dataP));
        return (List<Data>)bf.Deserialize(mp);
    }
    
    public List<float> CustomDeSerializeRotation(string dataR)
    {
        MemoryStream mr = new MemoryStream(Convert.FromBase64String(dataR));
        return (List<float>)bf.Deserialize(mr);
    }
    
  • M4TT
    M4TT
    edited December 2019
    Options
    As I mentionned in my code this method wasn't network friendly, using RPC with and sending a BIG string seems to make crashes sometime, no doubt it was because of the RPC size

    I rencently discovered the cached event feature and used them to instantiate something when someone joins the game, I also managed to use another method to compress the position of the instantiation and it's more network friendly ^^ ( float to byte[] then compress the byte array )
    Well just in case if someone reading that, I was wondering how many byte at most there can be in the cache of the room using PUN2 ( free ) ? I mean the global cache