Reputation: 2080
I made a simple message box that should show a message to the user. It's a prefab and does a couple of things, mostly animations upon instantiation. To run code upon instantiation I used the Start()
function.
It worked when I already knew what to message but I need something like a constructor
, that runs before Start()
, but upon instantiation
and can take parameters.
Now, I'm fully aware that I could instantiate, set the message and run everything - so using 3 lines of code where I instantiate it, but I'm curious if there is another, more proper solution? All I found on the net was that instantiate, then do something.
EDIT:
My calling of a messagebox to show up:
var timeBox =
Instantiate(messageBox, penaltySpawnLoc.position, penaltyPrefab.transform.rotation, transform);
var scr = timeBox.GetComponent<MessageBox>();
scr.OnCreated(message);
OnCreated
does the initialization, show up animations, so basically everything.
But it needs a string
input to know what to show up and I don't want to set the text value "on the fly" - it would make appear some weird flickering when the messagebox is visible but the text isn't set.
EDIT2:
the last parameter transform
in the Instantiation
is the Canvas
this script is on as this is a UI script. That parameter means that the freshly instantiated GameObject
is the child of that.
EDIT3:
timeBox is just an instance of messageBox
, they are GameObject
s.
One message box is used only once. Its purpose is to appear with the message and after like 0.5 seconds fade out and move away. After moving away it destroys itself.
Upvotes: 9
Views: 30252
Reputation: 366
Had this problem today and came up with this. The idea is to replace the Start()
method with a custom one and implement a wrapper that takes your prefab and its script's constructor as arguments for its (the wrapper) constructor, then have the wrapper instantiate and call your constructor. You can modify this to support more of the overloads provided by unity, but for now it works just fine for me.
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;
}
}
To pass the Action<T> constructor
to the constructor of EditableGO
, what I do is implement a static constructor generator. Here's an example of a simple customizable particle, notice the constructor generator:
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);
}
}
And then, instantiating new custom particles is as easy as
EditableGO<SimpleParticle> particle = new EditableGO<SimpleParticle>(SimpleParticle.ConstructorGenerator(duration, distance, size, speed, direction), Game.Resources.SimpleParticle);
particle.Instantiate(position, rotation);
Upvotes: 4
Reputation: 125255
You can do this with a function or extension method.
In this case, extension method is more appropriate.
We will make an extension object with Object
instead of GameObject
. Since GameObject inherits from Object, this extension method should work for both Object, GameObject and Transform.
Create a class called ExtensionMethod
then paste everything below inside it.
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;
}
}
Usage:
Only one line call in the Start
function should handle all other tasks.
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");
}
}
Upvotes: 8
Reputation: 73
I'd use a Factory for the message boxes, so something like
var timebox = MessageBoxFactory.Create(message);
timebox.Show();
and in the factory class do your setup and return the message box
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;
where Show() is the new OnCreated(). I find this pattern helps out with reducing the calling code and if I need tweaks to how the thing I want gets created it is all in one place with the shared factory code.
Upvotes: 7