当所有播放器都具有相同的名称时,找到localPlayer

时间:2018-07-24 09:26:00

标签: c# unity3d networking reference

当涉及到将玩家引用到另一个对象的脚本时,Unity使我感到困惑。我使用了几种方法来引用localPlayer对象:

  1. 比较标签(在某些情况下效果不佳)
  2. gameObject.Name(从未使用过,但也不可靠)
  3. 在检查员拖放中分配public GameObject Player;(播放器为预制件时会出现问题)
  4. public static [PlayerControllerClass] instance;函数中进行instance = this并设置void Start()。为了从另一个脚本访问playerSpeed,我要做PlayerControllerClass.instance.playerSpeed = 10;

还有其他解决方案,可以通过适当的方式推荐玩家。

注意::我知道我的问题可能与特定问题无关,但是如果有人可以提供帮助,我可以进一步解释。

  

编辑1:例如,我想在一个网络游戏中找到localPlayer,其中所有玩家都具有相同的名称,相同的标签,但是只有一个玩家是localPlayer。我可以从其他脚本访问localPlayer吗?

     

Edit2:我接受的答案是我感觉最好的答案。我将使用score text的{​​{1}}预览中提供的答案,方法是从一组访问玩家[localPlayer]

3 个答案:

答案 0 :(得分:4)

您的问题直到您进行编辑后才清楚,然后我意识到这是一个网络问题。

  

我需要在所有玩家都拥有的NetworkGame中找到localPlayer   相同的名称,相同的标签,但只有一个玩家是本地玩家   来自另一个脚本的localPlayer?

您可以找到带有NetworkConnection.playerControllers的玩家,该玩家重新进入PlayerController的列表,然后在其上循环并检查其是否有效。之后,从中获取NetworkBehaviour,检查isLocalPlayer属性。就是这样。

类似这样的东西:

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;
}

如果您将NetworkIdentity附加到播放器而不是NetworkBehaviour,则只需获取NetworkIdentity并检查isLocalPlayer属性。您还可以找到GameObject,然后检查NetworkIdentityisLocalPlayer属性。

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

您可能需要其他有用的播放器操作:

找到所有玩家

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;
}

按名称查找播放器(也可以按标签或图层更改)

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;
}

编辑之前的旧答案:

  

还有其他解决方案,可以通过适当的方式推荐玩家。

是的,您找到带有GameObject.Find的GameObject,此后,您可以使用GetComponent函数来获取附加到它的脚本。

我注意到您提到过您也想在现场引用预制件。预制件和已经存在于场景中的对象之间是有区别的,它们都有不同的引用它们的方式。

这里是场景中对象的示例。您可以在层次结构标签中看到它们:

enter image description here

让我们通过名称引用“画布”游戏对象:

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

让我们参考Canvas脚本附加到“画布”游戏对象的情况:

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

预制件:

以下是“项目”选项卡中的预制件示例:

enter image description here

将预制件放在名为“资源”的文件夹中。您必须使用Resources API来引用它们。

让我们参考“胶囊”预制游戏对象:

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

要使用它,必须实例化它:

GameObject instance = Instantiate(prefab);

实例化后,您只能在预制件上获得组件:

CapsuleCollider cc = instance.GetComponent<CapsuleCollider>();

答案 1 :(得分:1)

我建议使用Singleton模式。我几乎在我的所有Unity项目中都为只有一个类的对象实现了它,但是它是一种单一行为。

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();
}

然后您可以像这样创建一个类:

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

    public void DoStuff();
}

并这样称呼它:

MyClass.Instance.DoStuff()

答案 2 :(得分:1)

I have always sought usage of GameObject.FindGameObjectsWithTag when I need multiple references or else GameObject.FindWithTag when I need just the one reference to a specific GameObject available in the system.

To reference a script on said GameObject (found using the API reference above), you simply use `` as described in GameObject.GetComponent API reference.

A complete example would be:

  1. Assign a tag to your Player GameObject e.g. PlayerA.
  2. GameObject player = GameObject.FindWithTag("PlayerA");
  3. PlayerScript playerScript = player.GetComponent<PlayerScript>();

I hope this helps and should you require further clarification, I'll be more than happy to help.

EDIT: Another solution:

Concern: 50 or x amount of players would be too mundane to assign tags.

Solution: If you have a set of 50 players:

  1. I would group them under a parent GameObject.
  2. Assign the parent GameObject a tag e.g. ParentPlayer
  3. Use the GetComponentsInChildren reference.