Unity - 在实例化时传递参数

9
我制作了一个简单的“消息框”,用于向用户显示信息。它是预制件,主要在实例化时执行一些操作,包括动画。为了在实例化时运行代码,我使用了“Start()”函数。当我已经知道要发送的消息时,它可以正常工作,但我需要像“构造函数”一样的东西,在“实例化”时运行,并且可以接受参数。
现在,我完全意识到我可以实例化、设置消息并运行所有内容——使用3行代码来实例化它,但我想知道是否有另一种更合适的解决方案?我在网上找到的都是实例化,然后做一些事情。
编辑: 我的调用消息框显示的方式:
var timeBox =
    Instantiate(messageBox, penaltySpawnLoc.position, penaltyPrefab.transform.rotation, transform);
    var scr = timeBox.GetComponent<MessageBox>();
    scr.OnCreated(message);
OnCreated 是初始化、显示动画等的函数,基本上包括所有内容。但是它需要一个 string 输入来知道要显示什么,我不想在程序运行时设置文本值 - 这会导致消息框可见但文本未设置时出现一些奇怪的闪烁。

编辑2:

Instantiation 中的最后一个参数 transform 是此脚本所在的 Canvas,因为这是一个 UI 脚本。该参数表示新实例化的 GameObject 是其子级。

编辑3:

timeBox 只是一个 messageBox 的实例,它们都是 GameObject。每个消息框只使用一次。它的目的是在出现消息后约 0.5 秒淡出并移开,在移开后销毁自身。


好的,立即进行编辑。 - agiro
1
没问题。最后一个参数是“transform”。那是什么,类型是什么? - Programmer
再次编辑:我真的应该学会第一次表达自己。 - agiro
抱歉,再次问一下。timeBoxmessageBox 是什么类型?你还会再使用 timeBox 吗?它唯一被用到的地方是在你写的 timeBox.GetComponent<MessageBox>(); 这一行代码中。 - Programmer
没问题,已编辑 - agiro
显示剩余2条评论
3个回答

8
您可以使用函数或扩展方法完成此操作。
在这种情况下,扩展方法更加合适。
我们将使用Object创建一个扩展对象,而不是GameObject。由于GameObject继承自Object,因此该扩展方法应该适用于Object、GameObject和Transform。
创建一个名为ExtensionMethod的类,然后将下面的所有内容粘贴到其中。
using UnityEngine;

public static class ExtensionMethod
{
    public static Object Instantiate(this Object thisObj, Object original, Vector3 position, Quaternion rotation, Transform parent, string message)
    {
        GameObject timeBox = Object.Instantiate(original, position, rotation, parent) as GameObject;
        MessageBox scr = timeBox.GetComponent<MessageBox>();
        scr.OnCreated(message);
        return timeBox;
    }
}

用法:

Start函数中,只需调用一行代码即可处理所有其他任务。

public class Test: MonoBehaviour
{
    GameObject messageBox = null;
    Transform penaltySpawnLoc = null;
    GameObject penaltyPrefab = null;

    void Start()
    {
        gameObject.Instantiate(messageBox, penaltySpawnLoc.position, penaltyPrefab.transform.rotation, transform, "Hello");
    }
}

1
所以基本上把它包起来。这是一个好方法,因为我复制粘贴这3行可能会犯一些错误(而且我真的很讨厌复制粘贴)。谢谢! - agiro
是的。您不需要复制它,它现在是GameObject/Transform类的一部分。从项目中的任何脚本中,您可以执行gameObject.Instantiate(messageBox, penaltySpawnLoc.position, penaltyPrefab.transform.rotation, transform, "Hello");或者transform.Instantiate(messageBox, penaltySpawnLoc.position, penaltyPrefab.transform.rotation, transform, "Hello");而不是使用YourOtherClass.Instantiate... - Programmer
我会在这里使用 GameObject 而不是 Object,因为 Instantiate 只能在 GameObject 上调用。 - I Like

7
我会使用工厂模式来创建消息框,代码如下:
var timebox = MessageBoxFactory.Create(message);
timebox.Show();

在工厂类中进行设置并返回消息框。

public static MessageBox Create(string message) {
   var newTimeBox = Instantiate(messageBox, penaltySpawnLoc.position, penaltyPrefab.transform.rotation, transform);
   var scr = newTimeBox.GetComponent<MessageBox>();
   scr.SetMessage(message);
   return scr;

其中,Show()是新的OnCreated()。我发现这种模式有助于减少调用代码,如果我需要调整所需创建的内容,所有内容都在共享工厂代码中。


4
今天遇到了这个问题,我想出了一个解决方法。我的想法是用自定义方法替换Start()方法,并实现一个包装器,将您的预制件和其脚本的构造函数作为参数传递给包装器的构造函数,然后让包装器实例化并调用您的构造函数。您可以修改此代码以支持Unity提供的更多重载,但目前对我来说它工作得很好。 EditableGO.cs:
public class EditableGO<T>
{
    GameObject toInstantiate;
    Action<T> constructor;

    public EditableGO(Action<T> constructor, GameObject toInstantiate = null)
    {
        this.constructor = constructor;
        this.toInstantiate = toInstantiate == null? new GameObject() : toInstantiate;
    }

    public GameObject Instantiate(Vector3 position, Quaternion rotation, Transform parent = null)
    {
        GameObject instance;
        if(parent != null)
            instance = GameObject.Instantiate(toInstantiate, position, rotation, parent);
        else
            instance = GameObject.Instantiate(toInstantiate, position, rotation);

        constructor(instance.GetComponent<T>());

        return instance;
    }
}

为了将 Action<T> 构造函数 传递给 EditableGO 的构造函数,我需要实现一个静态构造函数生成器。下面是一个简单可定制的粒子示例,注意构造函数生成器:
public class SimpleParticle : MonoBehaviour
{
    public float Duration, Distance, Speed, traveledDistance, birth;
    public Vector3 Direction, Size;

    public static Action<SimpleParticle> ConstructorGenerator(float duration, float distance, Vector3 size, float speed, Vector3 direction)
    {
        return (self) => {
            self.Duration = duration;
            self.Distance = distance;
            self.Size = size;
            self.Direction = direction;
            self.Speed = speed;
            self.Init();
        };
    }

    void Init()
    {
        birth = Time.time;
        transform.localScale = Size;
    }

    void Update()
    {
        float deltaDist =  Speed * Time.deltaTime;
        traveledDistance += deltaDist;
        transform.position += Direction * deltaDist;

        if(Time.time - birth > Duration)
            Destroy(this.gameObject);
    }
}

然后,实例化新的自定义粒子就像这样简单:
EditableGO<SimpleParticle> particle = new EditableGO<SimpleParticle>(SimpleParticle.ConstructorGenerator(duration, distance, size, speed, direction), Game.Resources.SimpleParticle);
particle.Instantiate(position, rotation);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接