-
Notifications
You must be signed in to change notification settings - Fork 0
Program_CreateObjectInUnity
How do we create an object from prefab in Unity? Very simple:
GameObject go = Instantiate(Resources.Load("MyObject") as GameObject);
If we encapsule this line into a method, Create()
, it accepts a string as input and outputs a game object. However, there are three potential problems. First, Create()
requires a string as input, which is error prone. Second, we might want to set the object's position during creation. And lastly, we might need to add more components to the object. Let's get these things done one by one.
You can find the actual code here:
We can create an enumeration, ObjectTag
, and use it as input.
public GameObject Create(ObjectTag objectTag, Vector3 position)
{
string prefab = objectTag.ToString();
GameObject go = Instantiate(Resources.Load(prefab) as GameObject);
go.transform.position = position;
return go;
}
If we use two tags to describe one object, Create()
will require three arguments, which is kinda messy. That is why we need a custom data object, ProtoObject
, and change the method's signature into Create(IPrototype proto)
.
public class ProtoObject : IPrototype {}
public interface IPrototype
{
ObjectTag ObjectTag { get; }
Vector3 Position { get; }
}
Now it is time to define an interface (ICreateObject
) and create a class (CreateObject
) as the object factory.
public interface ICreateObject
{
GameObject Create(IPrototype proto);
}
public class CreateObject : ICreateObject {}
Remember our three problems at the beginning of this article? String input, position and adding component. Let's take a look at the third one. Suppose a building object has a Facade component, and an actor has a Momentum component, how do we identify the newly created object? By ObjectTag
, which is one of the input arguments.
If there are only a few object tags and components, switch...case
can handle the job perfectly:
switch (objectTag)
{
case ObjectTag.Building:
go.AddComponent<Facade>();
break;
case ObjectTag.Actor:
go.AddComponent<Momentum>();
break;
}
However, as you can imagine, the code will grow in size rather rapidly. So a better solution is to publish an event, AddingComponent
.
public GameObject Create(IPrototype proto)
{
// Create an object.
string prefab = proto.ObjectTag.ToString();
GameObject go = Instantiate(Resources.Load(prefab) as GameObject);
// Set position.
go.transform.position = proto.Position;
// Add components.
OnAddingComponent(new AddingComponentEventArgs(proto.ObjectTag, go));
return go;
}
public class AddingComponentEventArgs : EventArgs
{
public AddingComponentEventArgs(ObjectTag objectTag, GameObject data)
{
ObjectTag = objectTag;
Data = data;
}
public GameObject Data { get; }
public ObjectTag ObjectTag { get; }
}
The class ActorComponent
subscribes AddingComponent
:
private void ActorComponent_AddingComponent(object sender, AddingComponentEventArgs e)
{
if (e.ObjectTag == ObjectTag.Actor)
{
e.Data.AddComponent<Momentum>();
}
}
Lo and behold. Our object factory, which works smoothly, is at present obscured by one cloud. What is the value of IPrototype.Position
?
The main purpose of a dungeon generation algorithm is to decide the position of game objects. The code could be quite complicated, so we'd better put this part into another class, Blueprint
.
public interface IBlueprint
{
IPrototype[] GetBlueprint(ObjectTag objectTag);
}
public class Blueprint : IBlueprint {}
And now we need two steps to create a object:
IPrototype[] protos = Blueprint.GetBlueprint(objectTag);
foreach (IPrototype p in protos)
{
CreateObject.Create(p);
}
Similar to AddingComponent
, we publish another event, DrawingBlueprint
to get position value from various subscribers.
public IPrototype[] GetBlueprint(ObjectTag objectTag)
{
var ea = new DrawingBlueprintEventArgs(objectTag);
OnDrawingBlueprint(ea);
return ea.Data;
}
public class DrawingBlueprintEventArgs : EventArgs
{
public DrawingBlueprintEventArgs(ObjectTag objectTag)
{
ObjectTag = objectTag;
}
public ObjectTag ObjectTag { get; }
public IPrototype[] Data { get; set; }
}
By tweaking the creation code a little, we can try to fetch an object from pool.
public GameObject Create(IPrototype proto)
{
// Load an object from pool.
GameObject go = ObjectPool.LoadFromPool(proto.ObjectTag);
// Create an object from scratch.
if (go == null)
{
string prefab = proto.ObjectTag.ToString();
go = Instantiate(Resources.Load(prefab) as GameObject);
// Add components.
OnAddingComponent(new AddingComponentEventArgs(proto.ObjectTag, go));
}
// Set position.
go.transform.position = proto.Position;
return go;
}
The interface IObjectPool
is defined like this:
public interface IObjectPool
{
GameObject LoadFromPool(ObjectTag objectTag);
void SaveToPool(GameObject go);
}
Home | Latest | Game List | Design | Programming