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:
gameObject.Name
(never used it but it is also not reliable)public GameObject Player;
in the inspector Drag and Drop (face problems when the player is a prefab)public static [PlayerControllerClass] instance;
and setting instance = this
in 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 thelocalPlayer
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 thelocalPlayer
by accessing it from a set of players [localPlayer
]
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:
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:
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>();
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()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With