【问题标题】:Calling function from another script attatched to another object not working从附加到另一个对象的另一个脚本调用函数不起作用
【发布时间】:2019-12-17 22:26:13
【问题描述】:

我正在尝试在另一个 c# 脚本中调用一个函数,该函数附加到我场景中的另一个游戏对象。我正在Grounded 中创建脚本LevelChanger 的一个实例,如下所示:

LevelChanger levelChanger;

那么,在Awake()函数中:

GameObject gameObject = new GameObject("LevelChanger");
levelChanger = gameObject.AddComponent<LevelChanger>();

然后在IEnumerator 中这样调用:

levelChanger.FadeOut(true); // line 178

那么这是LevelChanger类:

using System.Collections;
using UnityEngine;

public class LevelChanger : MonoBehaviour
{
    public Animator animator;

    public void FadeOut(bool fadeIn)
    {
        animator.SetBool("Fade", true); // line 10

        if (fadeIn) StartCoroutine(FadeInAsWell());
    }

    IEnumerator FadeInAsWell()
    {
        yield return new WaitForSeconds(0.9f);

        animator.SetBool("Fade", false);
    }
}

我已经尝试解决这个问题很长一段时间了,我到处检查(herehereherehere 和其他网站)。我看到因为我的LevelChanger脚本附加到一个游戏对象,所以它是MonoBehaviour,不可能从另一个脚本创建类的实例,像这样:

LevelChanger levelChanger = new LevelChanger();

或者像我一开始这样的“正常”:

LevelChanger levelChanger;

然后像调用它的函数

levelChanger.FadeOut(true);

大多数情况下,我在运行时(第 176 行)得到 NullReferenceException,现在直接在 LevelChanger 脚本中(第 10 行)。

目前,我真的不知道如何解决这个问题:有人知道如何解决吗? (我还是个初学者)。

提前致谢!

【问题讨论】:

  • 当组件仅在运行时创建时,您是如何将其附加到检查器中的?
  • AddComponent 创建一个 new 组件 - 您在检查器中分配的任何内容指的是与您的代码创建的组件不同的组件
  • 那你为什么要这样做? GameObject gameObject = new GameObject("LevelChanger");
  • @EnricoCortinovis 好的,new GameObject() 所做的是创建一个新的空游戏对象。它存在于场景中,通常在原点。它有一个变换,没有别的。 AddComponent 向该游戏对象添加一个组件。所以你所做的就是用你的组件LevelChanger 的实例创建了一个新的游戏对象。因为这些都是全新的对象,它们的检查器引用没有设置,所以 animator 为空。如果您想在编辑时获取场景中的预制件实例,请考虑使用检查器引用。
  • 您在检查器中编辑的LevelChanger 实例与您使用gameObject.AddComponent&lt;LevelChanger&gt;(); 创建的实例完全不同。

标签: c# unity3d


【解决方案1】:

根据我们在 cmets 中的对话,我会在这里写一个更全面的答案。

所以,让我们回顾一下。您要做的是从LevelChanger 调用一个函数。但是,您不确定如何获得对LevelChanger 的引用。这是每个程序员都面临的核心问题:我如何将这些东西拿到那里,最好的方法是什么?

根据您所说,您的预制件存在于场景中,因此您想获取对它的引用。

一种简单而懒惰的方法是致电FindObjectOfType&lt;LevelChanger&gt;。这将在整个场景中搜索该类型的组件,并返回您的 LevelChanger 实例。我不建议这样做,因为它非常懒惰且效率低下。仅当您的一个或两个对象在运行时存在但在编辑时不存在时才需要这样做

另一种方法是将LevelChanger 视为单身人士。

public static LevelChanger Instance; 字段添加到您的LevelChanger。然后,在Awake() 上将Instance 设置为this。即Instance = this;

public static LevelChanger Instance;
public void Awake()
{
    Instance = this;
}

然后,在任何其他脚本中,您可以调用LevelChanger.FadeOut(false);

这也是一种类似的懒惰方式,但效率并不低。它确实使您的代码更难遵循,并且有很多开发人员对这样的静态实例有问题。让你知道。

另一种方法是使用GameObject.Instantiate() 创建预制件的实例,并在获得对组件的引用后调用该函数。这有点复杂,但对您来说可能是一种更清洁的方式。

有两种方法可以做到这一点,所以让我们做最干净的方法。在您的项目中,创建一个名为 Resources 的文件夹(如果您还没有)。 Resources 是 unity 在调用 Resources.Load() 时专门查找的名称。将您的预制对象拖到该文件夹​​中以创建新的预制。称之为LevelChanger

//Spawn the prefab gameobject
GameObject gameObject = GameObject.Instantiate(Resources.Load("LevelChanger")) as GameObject;
//Get a reference to its component LevelChanger
LevelChanger levelChanger = gameObject.GetComponent<LevelChanger>();
//Call the function
levelChanger.FadeOut(false);

在你的预制完成淡入淡出后,你可以通过Destroy(gameObject);让它自行销毁

最后,由于您已表明两个脚本在编辑时都存在,您可以简单地添加检查器引用。这是最简单的方法,但不要对检查员的引用发疯。它使代码难以理解。

public LevelChanger levelChanger;

【讨论】:

  • 非常感谢您的回答!在第二种方法中(我现在正在测试),我不需要类也是静态的吗?好像不行
  • @EnricoCortinovis 不,不要将课程设为静态。
  • @EnricoCortinovis 不,该类不需要是静态的,因为您需要获取对它的实例的引用。静态类本身不存在于内存中。所以只有你的 Instance 字段需要是静态的。你可以随意称呼它,比如myInstancebob
【解决方案2】:

快速而肮脏,因为关于相关代码和场景的分享很少。

Awake 中,获取对gameObject 的引用,上面已经存在LevelChanger

GameObject levelChangerGO = GameObject.Find("LevelChanger");
levelChanger = levelChangerGO.GetComponent<LevelChanger>();

然后在 IEnumerator 中这样调用:

levelChanger.FadeOut(true); // line 178

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多