1

I'm aware you can't pass gameObject references through RPC calls since every client uses it's own unique references for every instanciation. Is there a way to refer to a specific gameObject on ALL clients via an RPC call?

Here's a sample of what i'm trying to accomplish

    if(Input.GetKeyDown("r")){
PhotonView.RPC("ChangeReady", RPCTarget.AllBuffered, gameObject)
}
 [PunRPC]
    void ChangeReady(GameObject PLAYER) {
        PlayerScript PSCRIPT = PLAYER.GetComponent<PlayerScript>();
        if (PSCRIPT.READY)
        {
            PSCRIPT.GetComponent<PlayerScript>().READY = false;
        }
        else {
            PSCRIPT.READY = true;
        }
        
    }

I am trying to make a ready check system for players so they can toggle ready on and off and the information is transmitted to the other clients. Not sure how to refer to the specific gameObject since as previously mentioned it's impossible to do in RPC.

How would I go about doing this?

derHugo
  • 83,094
  • 9
  • 75
  • 115
TheWIzard
  • 29
  • 3

2 Answers2

0

There are a few ways of doing this, if all you want to do is set a ready state:

a) Use CustomProperties, which allows you to store player specific data in a key-value pairs. and then, you can locally check for these values on your player(s) GameObjects and change their behaviours with your RPC. here is an Example:

bool ready = (bool)PhotonNetwork.player.customProperties["Ready"];
Hashtable hash = new Hashtable();
hash.Add("Ready", true);
PhotonNetwork.player.SetCustomProperties(hash);

Follow this link for info on CustomProperties

b) Use OnPhotonInstantiate by implementing IPunMagicInstantiateCallback On your Monobehaviour

void OnPhotonInstantiate(PhotonMessageInfo info)
{
// e.g. store this gameobject as this player's charater in Player.TagObject
info.sender.TagObject = this.GameObject;
}

the TagObject helps you associate a GameObject with a particular player. So once they are instantiated, This is called automatically, and Your GameObject can be tied to the player and retrieved using Player.TagObject and Execute your ready state logic (Via RPC or any other way)

Follow this link for info about OnPhotonInstantiate

varunkaustubh
  • 301
  • 1
  • 7
0

See Photon RPC and RaiseEvent => "You can add multiple parameters provided PUN can serialize them" -> See Photon - Supported Types for Serialization

=> No it is not directly possible.


Solution

You will always need a way to tell other devices which object you are referring to since they don't have the same refernences as you.

If you are referring to objetcs of your Photon Players, though you can instead send over the

PhotonNetwork.LocalPlayer.ActorNumber

and on the other side get its according GameObject via the ActorNumber.

There are multiple ways to do so.

I once did this by having a central class like

public class PhotonUserManager : IInRoomCallbacks, IMatchmakingCallbacks
{
    private static PhotonUserManager _instance;

    private readonly Dictionary<int, GameObject> _playerObjects = new Dictionary<int, GameObject>();

    public static bool TryGetPlayerObject(int actorNumber, out GameObject gameObject)
    {
        return _instance._playerObjects.TryGetValue(actorNumber, out gameObject);
    }

    [RuntimeInitializeOnLoadMethod]
    private static void Init()
    {
        _instance = new PhotonUserManager();
        PhotonNetwork.AddCallbackTarget(_instance);
    }

    public void OnCreateRoomFailed(short returnCode, string message){ }

    public void OnJoinRoomFailed(short returnCode, string message){ }

    public void OnCreatedRoom() { }

    public void OnJoinedRoom()
    {
        Refresh();
    }

    public void OnLeftRoom()
    {
        _playerObjects.Clear();
    }

    public void OnJoinRandomFailed(short returnCode, string message){ }

    public void OnFriendListUpdate(List<FriendInfo> friendList) { }

    public void OnPlayerEnteredRoom(Player newPlayer) 
    { 
        Refresh();
    }

    public void OnPlayerLeftRoom(Player otherPlayer)
    {
        if(_playerObjects.ContainsKey(otherPlayer.ActorNumber))
        {
            _playerObjects.Remove(otherPlayer.ActorNumber);
        }
    }

    public void OnRoomPropertiesUpdate(Hashtable propertiesThatChanged){ }

    public void OnPlayerPropertiesUpdate(Player targetPlayer, Hashtable changedProps){ }

    public void OnMasterClientSwitched(Player newMasterClient){ }

    private void Refresh()
    {
        var photonViews = UnityEngine.Object.FindObjectsOfType<PhotonView>();
        foreach (var view in photonViews)
        {
            var player = view.owner;
            //Objects in the scene don't have an owner, its means view.owner will be null => skip
            if(player == null) continue;

            // we already know this player's object -> nothing to do
            if(_playerObjects.ContainsKey(player.ActorNumber)) continue;

            var playerObject = view.gameObject;
            _playerObjects[player.ActorNumber] = playerObject;
        }
    }
}

This automatically collects the GameObject owned by the joined players. There is of course one limitation: If the players spawn anything themselves and own it .. it might fail since then there are multiple PhotonViews per player => in that case use the one with the lowest NetworkIdendity. In Photon the viewIDs are composed of first digit = ActorNumber + 3 digits ids ;)

and later do

if(!PhotonUserManager.TryGetPlayerObject(out var obj))
{
    Debug.LogError($"Couldn't get object for actor number {receivedActorNumber}");
}

As another option you can use OnPhotonInstantiate

public class SomeComponentOnYourPlayerPrefab : PunBehaviour
{
    public override void OnPhotonInstantiate(PhotonMessageInfo info)
    {
        // only set if not already set in order to not
        // overwrite the tag for a player that spawns something later
        if(info.sender.TagObject == null)
        {
            info.sender.TagObject = gameObject;
        }
    }
}

and then later do

var obj = PhotonNetwork.LocalPlayer.Get(receivedActorNumber).TagObject;
derHugo
  • 83,094
  • 9
  • 75
  • 115