Instantiating Prefabs at runtime在运行时实例化预设
By this point you should understand the concept of Prefabs at a fundamental level. They are a collection of predefined GameObjects &Components that are re-usable throughout your game. If you don’t know what a Prefab is, we recommend you read the Prefabs page for a more basic introduction.
在这你将理解预设的基础概念。它们是预定义好的物体和组件,用来在游戏里复用。如果你不知道预设是什么,我们建议你读一下预设的基本介绍。
Prefabs come in very handy when you want to instantiate complicated GameObjects at runtime. The alternative to instantiating Prefabs is to create GameObjects from scratch using code. Instantiating Prefabs has many advantages over the alternative approach:
当你想在运行时实例化复杂的游戏物体时预设非常方便。另一种实例化预设的是从头写代码。实例化预设比变换方法有很多优势。
- You can instantiate a Prefab from one line of code, with complete functionality. Creating equivalent GameObjects from code takes an average of five lines of code, but likely more.实例化一个预设只需要一行代码就能实现全部功能,而创建一个等价的物体平均要5行,可能更多。
- You can set up, test, and modify the Prefab quickly and easily in the Scene and Inspector.在场景和监视面板里创建、测试和更改预设都很快速方便。
- You can change the Prefab being instanced without changing the code that instantiates it. A simple rocket might be altered into a super-charged rocket, and no code changes are required.你可以只修改预设而不修改实例化它的代码。一个简单的火箭可以改为一个超级火箭,但是代码不需要修改。
Common Scenarios常见场景
To illustrate the strength of Prefabs, let’s consider some basic situations where they would come in handy:为了说明预设的强大,让我们考虑几个用起来非常方便的情况。
- Building a wall out of a single “brick” Prefab by creating it several times in different positions.用一块砖实例化在不同的地方来造一栋墙。
- A rocket launcher instantiates a flying rocket Prefab when fired. The Prefab contains a Mesh, Rigidbody, Collider, and a child GameObject with its own trail Particle System.一个火箭发射器发射时实例化一个飞行的火箭预设。这个预设包括网格,刚体,碰撞体和一个包含拖尾粒子系统的子物体。
- A robot exploding to many pieces. The complete, operational robot is destroyed and replaced with a wrecked robot Prefab. This Prefab would consist of the robot split into many parts, all set up with Rigidbodies and Particle Systems of their own. This technique allows you to blow up a robot into many pieces, with just one line of code, replacing one object with a Prefab.一个机器人爆炸成好几片。这个完整,可操作(军事?)的机器人被摧毁,然后用一个毁坏的机器人预设来替代。这个预设由包含机器人分裂成的很多部分,全部是由它自己的刚体和粒子系统组成的。这个技术可以让你用一行代码将机器人炸成碎片。
Building a wall建一栋墙
This explanation will illustrate the advantages of using a Prefab vs creating objects from code.这个解释说明使用预设创建物体相对于使用代码的优势。
First, lets build a brick wall from code:首先,我们用代码。
// JavaScript
function Start () {
for (var y = 0; y < 5; y++) {
for (var x = 0; x < 5; x++) {
var cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
cube.AddComponent(Rigidbody);
cube.transform.position = Vector3 (x, y, 0);
}
}
}
// C#
public class Instantiation : MonoBehaviour {
void Start() {
for (int y = 0; y < 5; y++) {
for (int x = 0; x < 5; x++) {
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
cube.AddComponent<Rigidbody>();
cube.transform.position = new Vector3(x, y, 0);
}
}
}
}
- To use the above script we simply save the script and drag it onto an empty GameObject.要使用上面的代码,我们只需要存储脚本并把它拖到一个空物体上。
- Create an empty GameObject with .创建空物体的方法
If you execute that code, you will see an entire brick wall is created when you enter Play Mode. There are two lines relevant to the functionality of each individual brick: the CreatePrimitive() line, and the AddComponent() line. Not so bad right now, but each of our bricks is un-textured. Every additional action to want to perform on the brick, like changing the texture, the friction, or the Rigidbody mass, is an extra line.
如果执行上述代码,播放模式时你会看到整个砖墙。每块单独的砖有两行相关的功能代码;CreatePrimitive() 行和 AddComponent() 行。现在还不错,但是我们的砖是没有贴图的。每一个往砖上添加的额外的行为,改贴图,摩擦力或者刚体的重力,都是额外的行。
If you create a Prefab and perform all your setup before-hand, you use one line of code to perform the creation and setup of each brick. This relieves you from maintaining and changing a lot of code when you decide you want to make changes. With a Prefab, you just make your changes and Play. No code alterations required.
如果创建一个预设并预先设置好,你就可以只用一行代码执行创建和设置每一块砖。这会方便你维护和修改代码。通过使用预设,你只需要修改和播放,一点代码也不用改。
If you’re using a Prefab for each individual brick, this is the code you need to create the wall.下面是使用预设的代码。
// JavaScript
var brick : Transform;
function Start () {
for (var y = 0; y < 5; y++) {
for (var x = 0; x < 5; x++) {
Instantiate(brick, Vector3 (x, y, 0), Quaternion.identity);
}
}
}
// C#
public Transform brick;
void Start() {
for (int y = 0; y < 5; y++) {
for (int x = 0; x < 5; x++) {
Instantiate(brick, new Vector3(x, y, 0), Quaternion.identity);
}
}
}
This is not only very clean but also very reusable. There is nothing saying we are instantiating a cube or that it must contain a rigidbody. All of this is defined in the Prefab and can be quickly created in the Editor.
这不仅很干净而且可以复用。这不是说我们实例化一个立方体或者它需要包含刚体。所有这些都定义在预设里了并且很容易在编辑器里创建。
Now we only need to create the Prefab, which we do in the Editor. Here’s how:下面我们仅需要在编辑器里创建预设了,步骤如下:
- Choose
- Choose
- Choose
- In the Project View, change the name of your new Prefab to “Brick” 将预设重命名为“Brick"
- Drag the cube you created in the Hierarchy onto the “Brick” Prefab in the Project View 将立方体拖到预设上
- With the Prefab created, you can safely delete the Cube from the Hierarchy ( on Windows, on Mac)删除立方体
We’ve created our Brick Prefab, so now we have to attach it to the brick variable in our script. When you select the empty GameObject that contains the script, the Brick variable will be visible in the inspector.我们已经创建了砖块预设了,所以只要把它添加到我们脚本里的砖变量就行了。当你把脚本放到空物体上在检视面板上看到变量。
Now drag the “Brick” Prefab from the Project View onto the brick variable in the Inspector. Press Play and you’ll see the wall built using the Prefab.把预设拖到变量,运行,可以看到预设创建的墙。
This is a workflow pattern that can be used over and over again in Unity. In the beginning you might wonder why this is so much better, because the script creating the cube from code is only 2 lines longer.
这个工作流程模式可以在Unity中一遍一遍的使用。一开始你会想为什么这个会更好,因为代码创建立方体至少两行代码(翻译对不?)
But because you are using a Prefab now, you can adjust the Prefab in seconds. Want to change the mass of all those instances? Adjust the Rigidbody in the Prefab only once. Want to use a different Material for all the instances? Drag the Material onto the Prefab only once. Want to change friction? Use a different Physic Material in the Prefab’s collider. Want to add a Particle System to all those boxes? Add a child to the Prefab only once.
但是因为你现在用了预设了,你可以很快调整预设。想改变所有实例的重力?只要调整预设一次就行了。想改变说有实例的材质,拖拽材质到预设一次就好了。想改变摩擦力?给预设换个物理材质。想给所有盒子加一个粒子系统?给预设加个子物体就行了。
Instantiating rockets & explosions实例化火箭和爆炸
Here’s how Prefabs fit into this scenario:这里展示预设怎样用于下面的场景:
- A rocket launcher instantiates a rocket Prefab when the user presses fire. The Prefab contains a mesh, Rigidbody, Collider, and a child GameObject that contains a trail particle system.用户点发射,火箭发射器实例化一个火箭预设,这个预设包含网格、刚体、碰撞体和包含粒子拖尾的子物体。
- The rocket impacts and instantiates an explosion Prefab. The explosion Prefab contains a Particle System, a light that fades out over time, and a script that applies damage to surrounding GameObjects.火箭撞击并且实例化一个爆炸预设。这个爆炸预设包含一个粒子系统,一个随时间消退的光和一个对周围物体实施爆炸伤害的脚本。
While it would be possible to build a rocket GameObject completely from code, adding Components manually and setting properties, it is far easier to instantiate a Prefab. You can instantiate the rocket in just one line of code, no matter how complex the rocket’s Prefab is. After instantiating the Prefab you can also modify any properties of the instantiated object (e.g. you can set the velocity of the rocket’s Rigidbody).
通过代码创建火箭、添加组件和设置属性也是可以的,但是预设会更方便。不过预设多复杂,你只需要1行代码来实例化,实例化完成之后,你也可以修改已经实例化的物体的属性(比如改变火箭刚体的速度)。
Aside from being easier to use, you can update the prefab later on. So if you are building a rocket, you don’t immediately have to add a Particle trail to it. You can do that later. As soon as you add the trail as a child GameObject to the Prefab, all your instantiated rockets will have particle trails. And lastly, you can quickly tweak the properties of the rocket Prefab in the Inspector, making it far easier to fine-tune your game.
除了易用外,你还可以稍后改变预设。所以如果你建立了一个火箭,你不需要立即加一个拖尾。你可以稍后再加。一旦你给预设加一个拖尾子物体,你实例化的所有火箭都会有拖尾。最后,你可以在检视面板里调整火箭预设的属性,这会很方便的来调整游戏。
This script shows how to launch a rocket using the Instantiate() function.下面是代码。
// JavaScript
// Require the rocket to be a rigidbody.
// This way we the user can not assign a prefab without rigidbody
var rocket : Rigidbody;
var speed = 10.0;
function FireRocket () {
var rocketClone : Rigidbody = Instantiate(rocket, transform.position, transform.rotation);
rocketClone.velocity = transform.forward * speed;
// You can also acccess other components / scripts of the clone
rocketClone.GetComponent(MyRocketScript).DoSomething();
}
// Calls the fire method when holding down ctrl or mouse
function Update () {
if (Input.GetButtonDown("Fire1")) {
FireRocket();
}
}
// C#
// Require the rocket to be a rigidbody.
// This way we the user can not assign a prefab without rigidbody
public Rigidbody rocket;
public float speed = 10f;
void FireRocket () {
Rigidbody rocketClone = (Rigidbody) Instantiate(rocket, transform.position, transform.rotation);
rocketClone.velocity = transform.forward * speed;
// You can also acccess other components / scripts of the clone
rocketClone.GetComponent<MyRocketScript>().DoSomething();
}
// Calls the fire method when holding down ctrl or mouse
void Update () {
if (Input.GetButtonDown("Fire1")) {
FireRocket();
}
}
Replacing a character with a ragdoll or wreck用一个布偶或者残骸替代角色
Let’s say you have a fully rigged enemy character and he dies. You could simply play a death animation on the character and disable all scripts that usually handle the enemy logic. You probably have to take care of removing several scripts, adding some custom logic to make sure that no one will continue attacking the dead enemy anymore, and other cleanup tasks.
你有一个完全操纵的敌人角色,他死亡了。你可以简单的播放一个死亡动画,并且关闭所有控制敌人逻辑的脚本。你也可以除去几个脚本,添加一些定制的逻辑来确定没人会继续攻击敌人的尸体并且除去任务。
A far better approach is to immediately delete the entire character and replace it with an instantiated wrecked prefab. This gives you a lot of flexibility. You could use a different material for the dead character, attach completely different scripts, spawn a Prefab containing the object broken into many pieces to simulate a shattered enemy, or simply instantiate a Prefab containing a version of the character.
一个更好的方法是立即删除整个角色,用一个实例化的残骸预设来替代它。这么做会给你很多灵活性。你可以给尸体一个不同的材质或者添加不同的脚本,生成一个包含很多块的预设可以模拟一个易碎的敌人或者简单的实例化另一个版本的角色。
Any of these options can be achieved with a single call to Instantiate(), you just have to hook it up to the right prefab and you’re set!所有这些选项通过调用Instantiate() 都能实现,你仅仅是挂对预设。
The important part to remember is that the wreck which you Instantiate() can be made of completely different objects than the original. For example, if you have an airplane, you would model two versions. One where the plane consists of a single GameObject with Mesh Renderer and scripts for airplane physics. By keeping the model in just one GameObject, your game will run faster since you will be able to make the model with less triangles and since it consists of fewer objects it will render faster than using many small parts. Also while your plane is happily flying around there is no reason to have it in separate parts.
需要记住的重要的部分是你实例化的残骸预设可以是完全不同物体物体组成的。比如,你有一架飞机,你可以塑造两个版本。一个只包含单独的游戏物体和它的网格渲染器和飞机物理脚本。保持模型仅在一个物体里,可以让游戏更快,因为一个物体可以包含更少的三角形,也因为包含少的物体渲染起来比很多小部件要快。当然你的飞机整体飞也没必要用分开的部件组合。
To build a wrecked airplane Prefab, the typical steps are:下面是残骸版飞机预设的制作步骤
- Model your airplane with lots of different parts in your favorite modeler用不同的部分塑造你得灰机。
- Create an empty Scene建立一个空场景
- Drag the model into the empty Scene把模型拽到空场景中
- Add Rigidbodies to all parts, by selecting all the parts and choosing
- Add Box Colliders to all parts by selecting all the parts and choosing
- For an extra special effect, add a smoke-like Particle System as a child GameObject to each of the parts给每一个部分添加一个冒烟粒子效果的子物体
- Now you have an airplane with multiple exploded parts, they fall to the ground by physics and will create a Particle trail due to the attached particle system. Hit Play to preview how your model reacts and do any necessary tweaks.你现在已经有一个可以爆炸为多块的灰机,它们会因为重力作用掉到地面,也因为粒子特效而有拖尾效果。播放,通过模型的反应作调整。
- Choose
- Drag the root GameObject containing all the airplane parts into the Prefab将模型的根目录拖到预设上
The following example shows how these steps are modelled in code.
// JavaScript
var wreck : GameObject;
// As an example, we turn the game object into a wreck after 3 seconds automatically
function Start () {
yield WaitForSeconds(3);
KillSelf();
}
// Calls the fire method when holding down ctrl or mouse
function KillSelf () {
// Instantiate the wreck game object at the same position we are at
var wreckClone = Instantiate(wreck, transform.position, transform.rotation);
// Sometimes we need to carry over some variables from this object
// to the wreck
wreckClone.GetComponent(MyScript).someVariable = GetComponent(MyScript).someVariable;
// Kill ourselves
Destroy(gameObject);
// C#
public GameObject wreck;
// As an example, we turn the game object into a wreck after 3 seconds automatically
IEnumerator Start() {
yield return new WaitForSeconds(3);
KillSelf();
}
// Calls the fire method when holding down ctrl or mouse
void KillSelf () {
// Instantiate the wreck game object at the same position we are at在同一位置和角度创建残骸模型
GameObject wreckClone = (GameObject) Instantiate(wreck, transform.position, transform.rotation);
// Sometimes we need to carry over some variables from this object脚本处理些事情
// to the wreck
wreckClone.GetComponent<MyScript>().someVariable = GetComponent<MyScript>().someVariable;
// Kill ourselves把原模型销毁
Destroy(gameObject);
}
}
Placing a bunch of objects in a specific pattern在特定的形状上放大量物体
Lets say you want to place a bunch of objects in a grid or circle pattern. Traditionally this would be done by either:如果你想在一个方格或圆圈里放大量物体。传统式都会发生。
- Building an object completely from code. This is tedious! Entering values from a script is both slow, unintuitive and not worth the hassle.完全用代码,非常冗长。从脚本输入数值又慢又不直观,完全不值得这么繁琐。
- Make the fully rigged object, duplicate it and place it multiple times in the scene. This is tedious, and placing objects accurately in a grid is hard.完全操纵(摆放?)物体,复制并在场景里摆放多次。这么做也非常繁琐,精确摆放也很难。
So use Instantiate() with a Prefab instead! We think you get the idea of why Prefabs are so useful in these scenarios. Here’s the code necessary for these scenarios:用预设的代码如下
// JavaScript
// Instantiates a prefab in a circle
var prefab : GameObject;
var numberOfObjects = 20;
var radius = 5;
function Start () {
for (var i = 0; i < numberOfObjects; i++) {
var angle = i * Mathf.PI * 2 / numberOfObjects;
var pos = Vector3 (Mathf.Cos(angle), 0, Mathf.Sin(angle)) * radius;
Instantiate(prefab, pos, Quaternion.identity);
}
}
// C#
// Instantiates a prefab in a circle圆圈
public GameObject prefab;
public int numberOfObjects = 20;
public float radius = 5f;
void Start() {
for (int i = 0; i < numberOfObjects; i++) {
float angle = i * Mathf.PI * 2 / numberOfObjects;
Vector3 pos = new Vector3(Mathf.Cos(angle), 0, Mathf.Sin(angle)) * radius;
Instantiate(prefab, pos, Quaternion.identity);
}
}
// JavaScript
// Instantiates a prefab in a grid
var prefab : GameObject;
var gridX = 5;
var gridY = 5;
var spacing = 2.0;
function Start () {
for (var y = 0; y < gridY; y++) {
for (var x=0;x<gridX;x++) {
var pos = Vector3 (x, 0, y) * spacing;
Instantiate(prefab, pos, Quaternion.identity);
}
}
}
// C#
// Instantiates a prefab in a grid方格
public GameObject prefab;
public float gridX = 5f;
public float gridY = 5f;
public float spacing = 2f;
void Start() {
for (int y = 0; y < gridY; y++) {
for (int x = 0; x < gridX; x++) {
Vector3 pos = new Vector3(x, 0, y) * spacing;
Instantiate(prefab, pos, Quaternion.identity);
}
}
}