【问题标题】:Find localPlayer when all players have same name当所有玩家同名时查找 localPlayer
【发布时间】:2018-07-24 09:25:59
【问题描述】:

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

  1. 比较标签(在某些情况下不太好)
  2. gameObject.Name(没用过但也不靠谱)
  3. 在检查器中分配public GameObject Player;拖放(当播放器是预制件时会遇到问题)
  4. 制作public static [PlayerControllerClass] instance;并在void Start()函数中设置instance = this。为了从另一个脚本访问playerSpeed,我做PlayerControllerClass.instance.playerSpeed = 10;

是否有其他解决方案可以以正确的方式引用玩家。

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

Edit1: 例如,我想在 NetworkGame 中找到 localPlayer,其中所有玩家都具有相同的名称、相同的标签,但只有一个玩家是 localPlayer。我可以从另一个脚本访问localPlayer 吗?

Edit2 : 我接受的答案是我认为最好的答案。我将使用在预览score text 中提供的答案localPlayer 通过从一组玩家访问它[@987654333 @]

【问题讨论】:

  • 你的意思是在场景和层次标签中引用对象?
  • 是的,我的意思是我想通过 (prefabs,UI,...) 访问其他对象,例如 prefabs
  • 我经常使用 GameObject.Find(),如果对象有唯一的名称:)
  • FindObjectOfType<T>,然后您可以使用.gameObject。对于localPlayer 部分,您的网络使用什么? NetworkBehaviour 有一个 isLocalPlayer 属性(其他类似 OnStartLocalPlayer,请参阅文档 docs.unity3d.com/ScriptReference/…

标签: c# unity3d networking reference


【解决方案1】:

在您编辑之前,您的问题并不清楚,然后我意识到这是一个网络问题。

我需要在所有玩家都拥有的 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函数来获取附加到它的脚本。

我注意到您提到您也想在场景中引用预制件。预制件和场景中的对象之间存在差异,它们都有不同的引用方式。

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

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

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

让我们将Canvas 脚本附加到“Canvas”游戏对象:

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

预制件:

这是项目选项卡中的预制件示例:

将预制件放在名为“Resources”的文件夹中。您必须这样才能使用Resources API 引用它们。

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

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

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

GameObject instance = Instantiate(prefab);

只有在实例化之后才能在预制件上获取组件:

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

【讨论】:

  • 谢谢你解释了很多。
  • 为回复干杯!
  • 关于旧答案:我不擅长编码,但我得到它的方式是从资源文件夹加载预制件并不总是最好的方法,尤其是对于大型预制件(Resource. Load 从磁盘加载预制件)。顺便说一句,编辑器中没有注意到这种减速,因为编辑器将资源文件夹加载到内存中。如果在检查器中直接引用预制件,则统一创建一个原型实例,它在实例化时“加载”它(价格是原型占用的内存)。
  • @Nikaas 是的,确实如此。使用AssetBundle。新用户使用 AssetBundle 的过程有点复杂,但更好。
【解决方案2】:

我推荐单例模式。我在几乎所有 Unity 项目中都为只有 1 个类但属于单一行为的对象实现了它。

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

【讨论】:

  • 我将搜索Singleton这个我已经多次看到但没有深入研究的词。
  • 它并不适合所有场景,但在处理 GameManager、InventoryManager 等类时,它肯定会让生活更轻松,这些类都不应超过 1 个实例。随意使用我帖子中的第一个代码 sn-p。可以使用了。
【解决方案3】:

当我需要多个引用时,我总是寻求使用 GameObject.FindGameObjectsWithTag,或者当我只需要对系统中可用的特定 GameObject 的一个引用时使用 GameObject.FindWithTag。

要引用上述 GameObject 上的脚本(使用上面的 API 参考找到),您只需使用 GameObject.GetComponent API 参考中所述的``。

一个完整的例子是:

  1. 为您的玩家游戏对象分配一个标签,例如PlayerA
  2. GameObject player = GameObject.FindWithTag("PlayerA");
  3. PlayerScript playerScript = player.GetComponent&lt;PlayerScript&gt;();

我希望这会有所帮助,如果您需要进一步说明,我将非常乐意提供帮助。

编辑:另一种解决方案:

关注:50 或 x 数量的玩家太平凡而无法分配标签。

解决方案:如果您有一组 50 名玩家:

  1. 我会将它们分组到父 GameObject 下。
  2. 为父游戏对象分配一个标签,例如ParentPlayer
  3. 使用GetComponentsInChildren 引用。

【讨论】:

  • 感谢您的回答,您已经接近我所需要的,但有一些事情我需要知道管理。制作 50 个标签,每个标签旁边有玩家字母,每个玩家都有不同的标签,这是否可行。我认为这不是解决方案。
  • 这真的取决于。如果您有一组 50 名玩家,我会将他们分组在一个父游戏对象下,给该父游戏对象一个标签,例如ParentPlayer,然后使用 GetComponentsInChildren 引用。
  • 好的,现在据我了解,我必须实例化父级 (EmptyGameObject) 中的所有玩家并遍历这些游戏对象以了解哪个是本地玩家?
  • 您是否假设只有 1 个名为 localPlayer 的游戏对象?
猜你喜欢
  • 2022-01-26
  • 1970-01-01
  • 1970-01-01
  • 2021-03-26
  • 1970-01-01
  • 2023-02-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多