关于prefab两个疑问

  1. prefab能否嵌套?prefab A用到了prefab B,如果B改变,能否让A里的B部分也同时改变?理论上应该是可以做到的吧?

  2. 可不可以做到,在改变prefab的实例后,仍然能够和prefab同步(未改变的部分)。例如:

  • 将一个Label创建为prefab A,其font size为30,string为AAA
  • 将prefab A拖到场景上,修改其string为BBB(font size仍然为30)
  • 修改prefab A的font size为60,并保存
能否让场景上的实例的font size变为60,但string仍为BBB?

@wangzhe 请问,这两个功能有可能实现么? 尤其是第一个,如果能实现的话,真的是可以节省很多人力和时间。

  1. 抱歉目前 prefab 还不能嵌套
  2. 目前不行,将来会考虑加入。
    (不过我有个疑问,如果你 prefabA 拖到另一个场景上,创建另一个对象,修改其 string 为 CCC,然后保存 prefab。这时原场景中的 string BBB 是否也要自动变 CCC?)

应该不变。 类似Python的类和实例:

class Prefab(object):
… a = ‘a1’
… b = ‘b1’

obj = Prefab()
print obj.a, obj.b
a1 b1

obj.b = ‘b2’
print obj.a, obj.b
a1 b2

Prefab.a, Prefab.b = ‘a3’, ‘b3’
print obj.a, obj.b
a3 b2

嵌套功能非常有用,强烈建议考虑加入。:relaxed:

我的问题是,如果在另一个场景中增加了 C:

原场景中的 A,font size为30,string为AAA
A 拖到资源管理器后创建的 Prefab,font size为30,string为AAA
Prefab 拖到场景中变成 B,font size为30,string为BBB(改了 string)
Prefab 拖到另一个场景中变成 C,font size为30,string为CCC(改了 string)

然后 Prefab 改了 font size 后,变成:

A,font size自动变 60,string为AAA
Prefab,font size改成 60,string为AAA
B,font size自动变 60,string为BBB
C,font size自动变 60,string为CCC

这时如果保存 B,A 的 string 自动变成 BBB
如果保存 C,A 的 string 自动变成 CCC
这样就会导致一些难以意想到的情况。有可能 BC 是两个人在两个场景里同时编辑的,他们不单单改了 string,还可能改了很多别的地方,那么 A 就没办法同时合并 BC 的修改工作。

此外,如果涉及到还原操作,一个很困难的地方是你不知道哪些属性应该还原,如果你还原全部,就会丢失一些自己的修改。

嵌套功能确实很有用,但是实现起来也还是有问题:

  • 自己嵌套自己怎么办?
    这势必会增加开销,用于判断 instantiate 的过程中是否有出现闭环。

  • 父 Prefab 和子 Prefab 的节点相互引用怎么办?
    Prefab 保存时,会丢失所有对外的节点引用,而同一个 Prefab 中,节点相互关联是很正常的。如果父子节点的相互引用出现在两个不同的资源中,就会在资源保存时出现引用丢失的问题。

  • 已经编辑好的父 Prefab,因为子 Prefab 被其它人修改,导致父 Prefab 表现出错怎么办?
    我认为子 Prefab 修改时,都要打开所有父 Prefab 重新检查一遍,才是负责的工作流程。这样又进一步加大了多人协作的难度。


我也觉得嵌套是一个很有用的功能,但是要做好来确实需要一些工作量,而且使用上必须有所限制才行。这是嵌套功能暂时没列入开发计划的原因。

A因为是资源管理器中的prefab。所以当场景中的实例变化之后,A就会变成默认的prefab,
并且场景中的其他实例会根据这个默认的prefab的属性把没有被实例修改的属性同步到
A的属性。
B、C两人不论怎么修改自己的实例,在同步的时候都只是同步未做修改的部分。
从逻辑上来说没有太多的问题呀。

还不了解还原机制。不做讨论……

我想,这里的保存,要看是保存到场景,还是保存到prefab。
如果仅仅是保存到场景,那么并不修改prefab,所以不管是保存了B还是C,Prefab以及A都不会改变。
如果是保存到Prefab,那么Prefab和A的string会相应的改变。这样的行为我觉得应该是合理的。

实际的项目中,尤其是网游,UI的嵌套比较复杂。按照现在的设计,需要先作何很多小的Prefab(比如按钮),再利用这些小的Prefab拼大一些的prefab,然后再拼接成为一个完整的界面。如果一个小的prefab需要修改,那么就需要打开所有直接或间接引用到这个prefab的地方(prefab或者scene),手动的修改。这个工作量是很大的,而且是机械性的重复,如果能通过编辑器来实现是最好不过了。

这个功能,以及嵌套的问题,都属于比较高级的需求,而且其中的一些细节对编辑器会带来比较微妙的影响,我理解开发团队对此的顾虑。我建议一方面可以调查一下大家有对这些功能的看法(尤其是有过Unity经验的团队),另一方面能否增加几个接口,让我们可以自己写插件来实现。

接口的话大概需要这些:

  1. 保存Prefab的事件
  2. 得到Prefab的所有引用(包括曾经引用过,后来又丢失引用关系的)
  3. 打开/修改/保存一个Prefab
  4. 打开/修改/保存一个场景

其中第二条关于引用的记录比较复杂。我的理解应该是在编辑器中实例化Prefab时,在生成的Node和Prefab上互相记录了对方的uuid;在由于某些操作断开引用关西后,又去掉了这个相互的引用记录。
可以考虑在生成的Node上记录一个: originalPrefab,表示这个Node最初是通过哪一个prefab创建出来的。在Prefab上,也可以保存一些所有曾经实例化过的Node的uuid(或者其位于的场景/Prefab的uuid)。

如果担心这个引用记录也会对编辑器产生影响,也可以把这个工作交给插件来做。只要能再增加几个接口即可:

  1. 创建Prefab的事件
  2. 实例化Prefab事件(仅针对编辑器)
  3. 复制Node的事件(需要复制originalPrefab。如果默认复制行为就会复制该属性,可以不需要)
  4. 删除Node的事件(可选)
  5. 引用关系丢失的事件(可选。因为我不太清楚具体哪些时候会丢失引用关系)
1赞

这两个应该都可以通过检查循环引用来解决。当然要在编辑器里检查,不能在运行时。实际项目中有不少类似的情况。比如游戏的任务,会不会B任务需要先完成A任务,而A任务又需要先完成B任务。

理论上是有可能。但实际上大部分情况都是没问题的。而在没有嵌套功能的情况下,工作量比这个大的多,因为也需要把所有曾经用到过这个prefab的地方全部打开编辑一遍,而且由于丢失了引用关系,很可能会漏掉一些地方。

见另一条回复,是否可以考虑让我们通过插件自己来实现。以后在考虑是否要集成。

是保存到 Prefab。目前这种行为确实是“合理”的,只是容易造成冲突,在实际工作中我觉得还有更好的解决方案,而不是采用这种“继承”的方式。

我有 Unity 的经验,Unity 也不支持 Prefab 嵌套。当然它支不支持是一回事,大家都想要嵌套我们是知道的。我说那么多也不是说不做,只是解释下为什么嵌套暂时没有,因为这些使用上的困难摆在那里,我们会先把精力投入到其它更重要的功能上。等 Creator 的功能性比较完整后,Prefab 一定会再进行一轮升级的。

你说的插件机制是个不错的建议,不过这里涉及到很多内部实现,也涉及到 Undo 功能,恐怕很难文档化。这些功能我实现起来都要注意很多地方,用插件实现不太容易,何况在插件系统都还没完善的情况下。

1赞

非常理解。

还有一种更简单的方式。

  1. 实例化Node时,保存一个originalPrefab信息。这个应该不会对编辑器造成任何影响,除了项目里的文件会稍微大一点。

2.制作一个插件,遍历工程中的所有scene和prefab,检查其中每一个Node的originalPrefab,如果需要则进行相应的修改。不考虑undo等问题。

只需要提供对编辑器资源的遍历,读取,保存的接口即可。
实在不行,只要第一条能做到,第二条可以写个脚本在编辑器以外来做。

如果经过项目检验没问题,以后再考虑做到编辑器里 :smiley:

这样的插件实现起来难度可控,多谢提议。

期待早日实现:slightly_smiling:

cocos studio里就支持将一个csd拖动到另一个csd里面