- Home /
Duplicating a prefab
I'm trying to create a Spawner, that will spawn enemies around it's location. Now, I'm trying to make this a generic Spawner, so it receives a Spawnable which is an abstract subclass of MonoBehaviour, as parameter in it's constructor, and is supposed to create objects from that Spawnable template.
Now, each Spawnable has a method that Instantiates a copy of self. This copy is not simple instantiation, becuase depending on the specific implementation of Spawnable, it copies some of its parameters to the newly instantiated object.
public abstract class Spawnable : MonoBehaviour {
public abstract void Spawn(Vector3 position);
}
public class Enemy : Spawnable {
public float health;
public override void Spawn(Vector3 position) {
GameObject spawnedObject = (GameObject)Instantiate(this.gameObject, position, Quaternion.identity);
spawnedObject.GetComponent<Enemy>().health = this.health;
}
}
Now, say I have one prefab for Enemy, but then I want to create different Spawner objects, each spawning enemies with different health. So I need several Enemy prefabs, each one with different health, and pass them on as parameters to the Spawners.
Which brings us to my question: How do I duplicate the Enemy prefab so I can have several instances with different health values? Or maybe any other ideas? It's important to me to keep these generic and not limited to specific NPC implementations.
Why not just have a list of different healths your enemy can have (probably on the enemy class) and then select one either randomly or as another argument to the Spawn function.
I do have a question though. If you want a generic prefab spawner, why is your prefab instantiating itself?
Like I said, I'm trying to keep it generic. Health is just an example, in reality the variables are much more complicated and each NPC type has different variables. I don't want them randomized altogether, for instance in the beginner area the spawner will have a prefab with low health, but farther away one with higher health.
That's also why the prefab is instantiating itself - only it knows which variables need to be copied to the new instance.
I believe that you can achieve the same thing you desire using a much "nicer" method. As far as I understand it, your "Enemy" (just an example) script is used for two things right now. It's used as the spawner (for that type), as well as a gameplay object. A much nicer method would be to have the spawner be the thing that spawns the objects (ins$$anonymous$$d of the objects themselves) and ins$$anonymous$$d of you calling something like a "Spawn" function on the prefab, you can simply write that code right in the Start function of that object. Or if you want more information passed you can simply call some setVars function from the spawner and have the same effect.
This would be much easier to understand and much much nicer to maintain. The less duplicate code the better. So why duplicate Instantiate the same way in every subclass of Spawnable when you can write it once somewhere else?
However, other than the way you instantiate, my suggestion of using lists etc, for those values is still my answer to your question :)
Ok, let's take a look at a more complicated scenario. Say we have two types of NPC - Enemy and Passerby.
Enemy has health, money and weapon, where weapon is another class by itself.
Passerby has health and appearance which is also a class by itself.
Now, I thought of a single Spawner class that can spawn either Enemys or Passerbys, depending on the prefab I give it. So if I gave it a prefab of Enemy with gun as weapon, it would spawn enemies with guns, but if I gave it a prefab of Enemy with knife as weapon, it would spawn knife wielding enemies. The same way I could have two Spawners spawn different types of Passerbys.
Now, if I went with your suggestion, I would need two different Spawner classes, and each spawner class would have to duplicate all the variables of the spawned object. So an EnemySpawner would also have health, weapon, and money, so it can set these variables when it spawns the enemy.
Now consider at least 20 classes of NPCs that can be spawned. I'd also need 20 spawner classes. And every time I change the NPC's variables (say adding a secondary weapon), I'd need to add the same variable to the spawner class.
Do you still think your suggestion is a more elegant solution? If you do, I'd love to hear what you think makes it more elegant. I'm open to other ideas as long as I can be convinced they are better :)
BTW, you can think of the Spawn() method of the spawnable object as a copy constructor in C++, or a clone() method in java, which are general practice in those languages although they don't exist in C#.
I think you missinterpreted my comment. I specifically stated, that ins$$anonymous$$d of calling something like "Spawn" to set the variabless, just do them in something like Start or even your own function.
For instance:
public class Spawner : $$anonymous$$onoBehaviour {
public void Spawn(Spawnable prefab) {
Spawnable object = (Spawnable)Instantiate(prefab);
object.Set(prefab);
}
}
public class Enemy : Spawnable {
public override void Set(Enemy prefab) {
this.health = prefab.health;
}
}
As you can see, my solution is more elegant and produces a lot less repeated code.
The problem is, that $$anonymous$$onoBehaviour objects aren't normal objects. They are tightly tied to the Unity Runtime system, so a bitwise copy just doesn't work.
Also, C# does have cloning features (even though it really is a bad idea to directly clone something). Look at the IClonable interface for instance ;)
Answer by Benproductions1 · Oct 17, 2013 at 08:45 AM
Hello,
Ok, now that we are clear about what you want to achieve I can give you a proper answer:
//This is what I would do
//You have your class everything derives from
public abstract class Spawnable : MonoBehaviour {
//This will generate a set of data contained in one object
public abstract Stats GetData();
//This will apply a set of data to the current object
public abstract void SetData(Stats stats);
}
//Then you have your spawner class
public class Spawner : MonoBehaviour {
public Spawnable prefab;
private Stats stats;
//At the start we want to get stats which all our prefabs will use
public void Start() {
stats = prefab.GetData();
}
public void Spawn() {
Spawnable obj = (Spawnable)Instantiate(prefab, transform.position, transform.rotation);
//when we spawn a prefab, we want to apply the spawners stats to it
obj.SetData(stats);
}
}
Stats can really be anything. A string, a number etc. That is up to you how you plan on formatting that ;)
This method will give you the functionality you need, along with the genericness you strive for.
Hope this helps,
Benproductions1
It does help, but unfortunately doesn't solve my original question. Since I still have to supply the spawner with a spawnable prefab, I need several instances of the same prefab (each with different stats)...
I guess it would work if I manually set the "stats" variable of the spawner ins$$anonymous$$d of getting it from the prefab in the start().
Still, for other users sake that see this question - is there a way to duplicate a prefab that you know of?
No, prefabs are just references. You can't duplicate a prefab. With my solution you don't need different instances of a prefab, that's the whole point
True, but I need to create a Stats class per NPC type (since stats are not just floats and they differ between NPC types). That' still a bit of a hassle.
Thanks anyway, I'll try searching for an alternative a bit more and update the thread soon.
It's really not very hard (or a lot of code) to make a Stats class per NPC type. $$anonymous$$uch less code than copy-pasting an instantiate method everywhere. As an alternative you could have the "Stats" stored in a static list of the "NPC" and have each spawner access that list by index (id). Of course that means you don't need a Stats class for each prefab however it wouldn't be very nice either :)
I ended up basing my solution on your answer, using an internal class within the spawnable prefab, that had all the data needed when spawning. When creating a spawner, I passed it that internal class. Whenever the spawner needed to spawn something, it instantiated the spawnable prefab, and also set the internal data class, cloning the one it had in it.
This is not entirely elegant solution, since each NPC type had a different internal data class, so I used a common parent class that the spawner could hold, but then each specific NPC class would have to force cast it to it's specific data type.
Also, found another possible solution, which also has it's problems: I could duplicate the prefab by Instantiating it, and then disabling the instantiated object. The drawback is that you then have to enable it again after instantiating again, and perhaps other problems that I can't think of...
Thanks for your help.
Your answer
Follow this Question
Related Questions
Associate objects to a prefab 1 Answer
Updating a variable on a script in an instanced object 1 Answer
Instantiate object 1 Answer