Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Find localPlayer when all players have same name

Unity makes me confused when it comes to referencing players to another object's script. I have used several ways to reference a localPlayer object:

  1. Comparing Tags(not so good in some situations)
  2. gameObject.Name(never used it but it is also not reliable)
  3. Assigning public GameObject Player; in the inspector Drag and Drop (face problems when the player is a prefab)
  4. Making public static [PlayerControllerClass] instance; and setting instance = thisin void Start() function.In order to access playerSpeed from another script I do PlayerControllerClass.instance.playerSpeed = 10;

Is there other solutions for referencing players in a proper way.

Note: I know my question might not be relating to a specific problem but I can explain more if somebody will help.

Edit1: for example I want to find localPlayer in a NetworkGame where all players have same name, same tag but only one player is a localPlayer. Can I access the localPlayer from another script?

Edit2 : The answer I accepted is what I felt the best.I will use the answer provided in previewing score text of the localPlayerby accessing it from a set of players [localPlayer]

like image 878
Bak Stak Avatar asked Mar 06 '23 18:03

Bak Stak


2 Answers

Your question wasn't clear until your edit then I realized that this is a networking question.

I need to find localPlayer in a NetworkGame where all players have same name, same tag but only one player is a localPlayer can I access the localPlayer from another script?

You can find players with NetworkConnection.playerControllers which retruens List of PlayerController then loop over it and check if it is valid. After that, get NetworkBehaviour from it check the isLocalPlayer property. That's really it.

Something like this:

GameObject FindLocalNetworkPlayer()
{
    NetworkManager networkManager = NetworkManager.singleton;
    List<PlayerController> pc = networkManager.client.connection.playerControllers;

    for (int i = 0; i < pc.Count; i++)
    {
        GameObject obj = pc[i].gameObject;
        NetworkBehaviour netBev = obj.GetComponent<NetworkBehaviour>();

        if (pc[i].IsValid && netBev != null && netBev.isLocalPlayer)
        {
            return pc[i].gameObject;
        }
    }
    return null;
}

If you have NetworkIdentity attached to the player instead of NetworkBehaviour, just get the NetworkIdentity and check the isLocalPlayer property. You can also find the GameObject then check the NetworkIdentity and the isLocalPlayer property.

if (obj.GetComponent<NetworkIdentity>().isLocalPlayer){}

Other useful player operation you may need:

Find all players:

List<GameObject> ListPlayers()
{
    NetworkManager networkManager = NetworkManager.singleton;
    List<PlayerController> pc = networkManager.client.connection.playerControllers;

    List<GameObject> players = new List<GameObject>();
    for (int i = 0; i < pc.Count; i++)
    {
        if (pc[i].IsValid)
            players.Add(pc[i].gameObject);
    }
    return players;
}

Find player by name(Can also be changed to by tag or layer):

GameObject FindNetworkPlayer(string name)
{
    NetworkManager networkManager = NetworkManager.singleton;
    List<PlayerController> pc = networkManager.client.connection.playerControllers;

    for (int i = 0; i < pc.Count; i++)
    {
        if ((pc[i].IsValid) && (name == pc[i].gameObject.name))
            return pc[i].gameObject;
    }
    return null;
}

OLD answer before your edit:

Is there other solutions for referencing players in a proper way.

Yes, you Find the GameObject with GameObject.Find, after this, you can use the GetComponent function to get the script that is attached to it.

I notice you mentioned you want to reference prefabs in the scene too. There is a difference between prefabs and objects already the scene and they both have different ways to reference them.

Here is an example of Objects in the Scene already. You can see them in the Hierarchy tab:

enter image description here

Let's reference the "Canvas" GameObject by name:

GameObject canvasObj = GameObject.Find("Canvas");

Let's reference the Canvas script attache to the "Canvas" GameObject:

GameObject canvasObj = GameObject.Find("Canvas");
Canvas canvas = canvasObj.GetComponent<Canvas>();

Prefabs:

Here is an example of prefabs in the Project tab:

enter image description here

Put the prefabs in a folder named "Resources". You must so that you can reference them with the Resources API.

Let's reference the "Capsule" prefab GameObject:

GameObject prefab = Resources.Load<GameObject>("Capsule");

To use it, you have to instantiate it:

GameObject instance = Instantiate(prefab);

You can only get components on prefabs after when they are instantiated:

CapsuleCollider cc = instance.GetComponent<CapsuleCollider>();
like image 144
Programmer Avatar answered Mar 09 '23 05:03

Programmer


I recommend the Singleton Pattern. I implement it in almost all my Unity Projects for objects that only have 1 class, yet is a monobehaviour.

using UnityEngine;


public abstract class SingletonBehaviour<T> : MonoBehaviour where T : MonoBehaviour
{
    public bool dontDestroyOnLoad = true;
    private bool isInitialized = false;

    private static T instance;
    public static T Instance
    {
        get
        {
            if (instance == null)
            {
                instance = FindObjectOfType<T>();
                if (instance == null)
                {
                    var obj = new GameObject
                    {
                        name = typeof(T).Name
                    };
                    instance = obj.AddComponent<T>();
                }
            }

            return instance;
        }
    }

    protected virtual void Awake()
    {
        if (instance == null)
        {
            instance = this as T;

            if (dontDestroyOnLoad)
            {
                DontDestroyOnLoad(this.transform.root.gameObject);
            }
        }
        else if (instance != this)
        {
            Destroy(gameObject);
        }
    }


    private void OnEnable()
    {
        if (!isInitialized)
        {
            OnInitialize();
            isInitialized = true;
        }
    }

    public abstract void OnInitialize();
}

You can then create a class like so:

public class MyClass : SingletonBehaviour<MyClass>
{
    public void OnInitialize()
    {
        // You can use this instead of awake or start
    }

    public void DoStuff();
}

And call it like so:

MyClass.Instance.DoStuff()
like image 25
Immorality Avatar answered Mar 09 '23 07:03

Immorality