详解新版Unity中可嵌套的Prefab系统

前言

Prefab,也就是大家熟知的预制件(本文中,我们依然使用它的英文名字——Prefab),它是Unity中一个极其重要的概念。Prefab用来保存GameObjects(以及其所有的子GameObejct)、组件以及属性。在实例化Prefab的时候,Prefab扮演着资源模版的角色。
Unity2018.3的Beta版本中支持了可嵌套的Prefab系统,这个新特性使得能够把一个大型的建筑剥离成粒度更小的Prefab来。例如大型建筑,可以嵌套房间的Prefab,以此类推,房间的Prefab也可以嵌套更小的家具的Prefab。Prefab嵌套的灵活性极大的增强了Pefab在多人协同开发的效率,同时新版的Prefab系统在Prefab的编辑上也有很大的改变,使用起来非常方便。
接下来,我们就来一览新版的Prefab系统吧。开始正文之前,show一张我们工作室的游戏截图

上图为迷雾侦探,手领出品

一、创建Prefab

创建Prefab资源

创建Prefab同之前版本的Unity引擎一样,我们只需要把Hierarchy视图中GameObject拖到Project视图里,穿着蓝色衣服的Prefab资源就出现在Project的视图中了。

Project视图中的Prefab资源
二、Prefab实例化

创建Prefab实例的方法也没有改变,还是下面两个途径:

  • 从Project视图中直接把Prefab拖动到Scene或者Hierarchy视图里面。
  • 在运行时通过Instantiate这个API来创建Prefab的实例。
在Prefab模式下编辑Prefab

Prefab模式,是一种专门用来支持单独编辑Prefab的一种模式。Prefab模式允许在一个独立的场景里面查看和编辑Prefab里面的内容。

进入Prefab模式

这里有两种方法可以进入Prefab模式:

  • 在Project视图中双击Prefab
  • 在Hierarchy视图中点击Prefab实例的箭头按钮

注意:在Prefab模式下,Prefab的根节点是普通类型的GameObject(并是不显示成Prefab实例特有的蓝色图标)

在Prefab模式下的Scene和Hierarchy视图.

在Prefab模式下,Scene视图的上方会显示一个导航栏。使用导航按钮可以在主场景或其他Prefab之间切换。此外,在Hierarchy视图顶部,也会显示一个标题栏,上面显示当前打开的Prefab的名字。点击标题栏中的左箭头按钮,也可以用于回到主场景。

自动保存

在Prefab下,如果点选了自动保存的话,任何对Prefab的改动,都将会自动的保存到Prefab资源中去(自动保存默认是开启的)。

自动保存的开关按钮位于场景视图的右上角(在Prefab模式下)

我们也可以关闭自动保存。在关闭之后,当我们退出当前Prefab的Prefab模式的时候,编辑器会提醒我们是否保存修改。在某些情况下,关闭自动保存是非常有用的

设置Prefab模式的编辑环境

Prefab模式下,默认会显示一个空场景。我们可以指定一个特定的场景来作为Prefab模式的背景场景。(背景场景中的对象,都会显示在Prefab模式中,但是这些对象是无法被选中的,也不会显示在Hierarcy视图中。)
我们可以通过Editor Settings来设置这个背景场景。在打开主菜单“Edit->Project Settings -> Editor”,找到“Prefab Editing Environments”。其中“Regular Environment”适用于大多数的Prefab,“UI Environment”适用于大多数的UI相关的Prefab。

在编辑器的Project Settings中设置Prefab的编辑环境
三、实例的覆盖值

所谓覆盖值,简单地说,修改了实例的值就会产生覆盖效果,Prefab实例中值覆盖了Prefab资源中的值,这就是覆盖值的定义
这个特性是非常有用的,例如,当你需要创建多个相似的NPC,但是又需要有一些少许的差异(也就是说实例和Prefab不会完全一致的时候)。
我们对Prefab实例中的值进行修改,就相当于创建了一个覆盖值。 修改的动作,我们可以理解为覆盖

新版的Prefab支持下面几种类型的覆盖

  • 修改组件中属性的值
  • 添加/删除组件
  • 添加一个子的GameObject

同时这里也有一些限制:不能修改该Prefab中的Gameobject的根节点,也不能删除属于Prefab的GameObject(可以通过active为false的方法来绕过)

为了直观地看到被覆盖的属性,Unity把Inspector中被覆盖的属性的名字用粗体字显示,同时在左边的边缘显示了一条蓝色的线。如果是添加的一个全新的Componet的话,蓝色的线会跨过整个Componet。

Prefab 实例覆盖了"Dynamic Occluded"属性。同时新增加了一个Rigidbody

添加或者移除组件,会在Inspector界面的名字旁边生成+或者-的标记。如果是增加一个GameObject话,则会在Hierarchy视图中的图标旁边增加+的标记。

Prefab实例中添加了名为Extension的GameObject作为覆盖
实例覆盖值的优先级更高

Prefab实例的覆盖值,会取代来自Prefab资源中的值。换而言之,如果你修改了Prefab资源中的一个值,这个值对于被覆盖了值的实例来说,是不会生效的。<u>因此,发现修改了Prefab中的值,但不是所有的Prefab实例都有效果,那么建议检查一下这个值是否是在没生效的Prefab实例中被覆盖了。</u>
最后,为了尽量去避免潜在的问题和困惑,建议只在必要的情况下使用实例覆盖值取代Prefab的值。

四、通过实例来编辑Prefab

当选中Prefab实例的根节点时候,Inspector界面中会出现3个按钮,分别是Open、Select和Overrides。
点击Open按钮,将直接进入Prefab模式,点击Select,将会在Proejct视图中选中Prefab资源。点击Overrides,则会弹出一个下拉窗口。重点说下Overrides.

Overrides下拉窗口

Overrides下拉窗口中可以查看所有修改过的数据项(例如属性,组件,添加的GameObjects等)。在这个窗口中,我们可以放弃和应用所有的修改。(需要注意的是,如果这个Prefab是嵌套在其他Prefab中的话,那么Inspector中没有Overrides按钮,因此不能打开Overrides窗口)

Overrides下拉列表

通过点击下拉界面中列表的数据项,会弹出一个悬浮界面。如果是修改的属性,这个悬浮界面分为两个部分,左边是来自Prefab资源中的值,右边是Prefab实例的值,其中修改过的字段的名字被加粗显示,同时在左边缘有一个蓝色的竖线。

悬浮对比框
上下文菜单

另外,所有类型的覆盖值,都可以通过相关上下文菜单来进行应用/撤销

在Inspector界面中,加粗显示覆盖了Prefab的值的属性名。右键点击,在弹出的上下文菜单里面,选择Apply to Prefab或Revert。

Inspector中的上下文菜单

对于修改过的组件(新增加的组件,组件图标旁边有一个+标记),可以通过点击该组件右上角的齿轮按钮,或者右键点击组件顶部弹出上下文菜单来进行操作。



已经移除的组件上,会有一个-标记重叠在组件的图标上。删除操作也可以通过是上下文菜单来进行回滚或应用到Prefab资源上。撤销删除的组件,这个组件会从新回到GameObjct中。应用修改的话,会把这个组件也从Prefab中也删除。

image.png

在Prefab实例中新增加的GameObject的图标上会叠加一个+的标记。在Hierarchy视图中,右键点击新增的GameObejct,在弹出的上下文菜单中可以进行应用和撤销的操作。

五、Prefab的嵌套

当当(念一声和二声),到了本文最核心的部分了,Prefab的嵌套。Prefab嵌套是指在一个Prefab中,可以包含其他的Prefab,也就是说,一个Prefab可以包含在另外一个Prefab中。做关卡设计的同学,对以前的Prefab有多难用,有深刻的感受,现在终于可以长长地舒口气了。

在Prefab模式中添加嵌套的Prefab

添加一个嵌套的Prefab和在场景中添加实例化Prefab的操作是一样的,通过把Prefab资源拖动到Hierarchy视图或者Scene视图中就可以把一个Prefab实例添加到已经打开的Prefab中去。
这里可以看到,在Prefab模式下打开的Prefab的GameObejct的图标,显示成的是普通的图标,而嵌套的Prefab的实例则显示着象征了Prefab实例的蓝色图标。同时。我们可以像在普通场景中一样地覆盖这些Prefab实例的值。

Vase被嵌套在Table这个Prefab中
在Prefab的实例中进行嵌套的操作

我们也可在非Prefab模式下来创建Prefab的嵌套。我们只需要把Prefab资源拖动到一个Prefab实例下面,编辑器会在Hierarchy为我们创建一个叠加了+标记的Prefab实例(+标记意味着这个被添加的Prefab实例可以撤销或者应用到外部的Prefab中去)。

这里我们和前面介绍的通过实例来编辑Prefab的操作一样,可以把这个修改覆盖值应用到外部的Prefab资源中去。在进行覆盖操作后,+号标记会消失,它将成为外部Prefab的一部分。

左边:“Vase”Prefab作为一个覆盖操作被添加到Table的实例下。右边:“Vase”Prefab被嵌套到“Table”Prefab中去

六、Prefab变体

Prefab变体类似于面向对象编程中的继承的概念。Prefab变体继承了基础Prefab的所有属性,覆盖Prefab变体中的属性,会把属性覆盖到基础Prefab中去。Prefab变体也是一个会带来极大效率提升的新功能。

Prefab变体可以这样的应用场景下使用: 我们有一个武器的Prefab,为这个武器创建一个变体,通过赋予不同的材质,可以让武器呈现出不同于基础Prefab的外观,例如金属质感,破旧感。对于基础武器Prefab的修改,会影响变体(在变体中覆盖的属性不受影响)。比如我们可以通过调整基础Prefab的碰撞参数来修改各个变体的物理碰撞范围。

创建Prefab变体

Unity提供了多种方法来创建Prefab变体:

  • 在Project视图中,右键点击Prefab资源,然后选择“Create Prefab Variant”。
  • 把一个Prefab实例从Hierarchy视图中拖到Project视图中,会弹出一个对话框,让我们选择是创建一个新的Prefab还是创建一个Prefab变体。如果我们选择Prefab变体的话,就会基于拖出的Prefab实例的Prefab资源来创建一个变体,所有在这个实例中的修改,都会存储在Prefab变体内部
    Prefab变体显示和Prefab一样的图标,不同的是,图标上有叠加箭头样的装饰图形。
Prefab变体在Hierarchy视图中
编辑Prefab变体

在Prefab模式下的Prefab变体的的根节点显示着标志着是Prefab实例的蓝色图标,本质上Prefab变体的内核就是一个Prefab实例,任何对这个变体的修改,都可以覆盖到基础Prefab中去。

变体可以覆盖的类型和相关限制,与Prefab实例中修改Prefab是一样的,具体可参考第三节实例覆盖部分的描述。

例如,选择“Table with Vase”的根节点,然后点击Inspector界面中的Select按钮,编辑器会帮助选中基础Prefab资源,正如我们前面所说,这是因为在变体中,“Table with Vase“是一个Prefab实例。

对Prefab变体的相关操作,和实例覆盖完全一致。再次强调一下,所有的Override操作,都是覆盖到基础Prefab资源中的,而不是Prefab变体中。

打开"Table with Vase"变体的Overrides界面

七、多层级应用覆盖值

当我们修改一个嵌套在内部的Prefab实例时候,在执行Override操作的时候,Unity提供了在应用覆盖的时候可以选择覆盖到不同的Prefab的选项,这就是所谓的多层级应用覆盖。



如果Vase中的根节点的Scale被修改了,在应用覆盖的时候,可以选择是应用到Table还是Vase上。

  • 如果选择了应用到Vase上,则修改后Scale会保存到Vase的Prefab资源中去,进而影响到所有的Vase实例。
  • 如果选择了应用到Table上,修改后的Scale变成Table这个Prefab内部的Vase实例的值覆盖,修改后的Scale在Hierarchy或Inspector界面中不会被标记为蓝色的值覆盖。但是在Table的Prefab模式下,Vase实例的Scale值,依然标记为值覆盖。这就意味着所有的Table实例中的Vase实例都使用了这个Scale来覆盖Prefab资源中的值。*而在工程中的其他不属于Table中的Vase实例,则不受这个值的影响。

八、Unpacking Prefab Instance(解除和Prefab的关联)

在新版本中的Prefab中,可以通过Unpack Prefab来解除Prefab实例和Prefab资源之间的连接关系。Prefab实例解除连接后,在Hierarchy视图中的图标显示为普通GameObject的图标。

新版Prefab系统支持两种解除连接Prefab资源的方法,点击Prefab实例的根节点,在右键弹出的窗口中,找到下面的两个选项:

  • Unpack Prefab

解除当前Prefab实例和Prefab资源的连接关系。这个操作对内部嵌套的Prefab是没有影响的。

  • Unpack Prefab Completely

完全解除关联和旧版本引擎的“Break Prefab Instance”功能类似,一旦执行了完全解除关联,当前Prefab实例下的所有嵌套Prefab实例也会失去和对应的Prefab资源的关联。

九、结尾

新版的Prefab系统为大家介绍到这里。本文的核心内容主要参考自官方的一篇关于Prefab的文章Prefab手册
写作本文的过程中,笔者使用的Unity引擎版本是2018.3.b7。操作过程中,仍然出现了一些难以描述的bug。例如嵌套的Prefab在场景里面有显示,但是Hierarchy视图中,却看不到(此时编辑器控制台会一直报一个序列化相关的错误)。因此,不建议在正式的商业项目中使用Unity2018.3的beta版本。

受限于笔者的能力有限,因此难免有偏颇之处,希望朋友们雅正、谅解。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容