使用嵌套 Class 作为 Unity3D 组件
Using Nested Class as Unity3D Component
我使用的是Unity 5.4.2f2 Personal,我写了一个类似下面的C#脚本:
using UnityEngine;
using System;
public class Outer : MonoBehaviour {
// Some public outer fields
public class Inner : MonoBehaviour {
// Some public inner fields
}
}
我希望能够将 Outer
和 Inner
附加到场景中的游戏对象。不幸的是,即使我向它添加 [Serializable]
,Inner
也不会在 Unity Inspector 中显示为可用脚本。我知道很多人可能会说如果我试图让嵌套的 class 对外界可见,我的设置有问题,但为了这个问题,我只想知道这是否可以完成。即,是否有任何方法可以使嵌套的 classes 可用作 Unity 组件?
不,无法将嵌套的 class 用作统一组件。要将 MonoBehaviour class 用作统一组件,class 名称 必须 等于脚本的文件名,因此使用嵌套 class 会有可能。
如果您在不重命名脚本文件的情况下将外部 class 重命名为您想要的任何其他名称,您将发现也无法使用新的外部 class。
[编辑]
我找到了 documentation。正如我所说:
"The class name and file name must be the same to enable the script component to be attached to a GameObject."
我认为之前的回答不正确。
您应该能够通过使用 [MenuItem] 属性创建和绑定 MonoBehaviour 的静态方法创建编辑器脚本来添加嵌套的组件。
这在 Unity 5 及更高版本中有效。
您不能通过统一标准菜单添加它。
但是你可以通过脚本添加它。
添加组件();
自问这个问题以来,我学到了很多东西,所以我想我会添加更多信息。 and 仍然是最正确的答案。
Unity如何编译脚本
Unity 使用内置版本的 Mono 编译器(跨平台 C# 编译器)编译项目中的所有脚本。它为每个脚本文件(在关联的 .meta
文件中)分配一个唯一的 GUID,就像它为每个其他资产所做的那样。这带来了一些不错的可用性改进:classes 可以在不破坏现有脚本引用的情况下重命名,因为 GUID 不会更改,并且脚本可以与其他相关资产(例如,所有“士兵”scripts/sounds/textures可以组合在一起)。
Unity的脚本命名限制
这种可用性的缺点是 Unity 实施了一些命名限制:
- 每个文件只有一个 class,与文件同名,否则 Unity 将不知道在组件脚本引用中使用哪个 GUID
- 没有嵌套 classes
- 在名为
MyBehaviour.cs
的文件中没有 class 名为 TheBehaviour
的文件
可能 没有 partial
classes 如@derHugo 的评论所述,这是可能的。部分声明可以在相同或不同的文件中,即使名称不同,只要其中一个文件名与 class. 的名称匹配即可
如果您 select 项目 Window 中的脚本资产并看到以下消息,那么您就知道您正在尝试以 Unity 不支持的方式命名事物:
但是,这些限制仅适用于 public class 派生自 MonoBehaviour
(或者可能来自 UnityEngine.Object
? ).您可以在文件中包含任意数量的其他私有 MonoBehaviour
或 POCOs,因为 Unity 永远不会序列化它们,因此它们不需要自己的 GUID。 Public 标记为 [Serializable]
的 POCOs 可能 具有与 public MonoBehaviour
s 相同的命名限制......我没有测试过这个所以我不确定。为什么我没有测试过这个?嗯...
使用托管插件避免这些限制
如果您和我一样,并且来自纯 .NET 背景,那么对可以在哪些文件中定义 classes 的任何限制可能会让您抓狂。在这种情况下,您可以将脚本存储在 Unity 项目之外(我使用与 Unity 项目相同的存储库中的另一个文件夹),并使用 IDE 像 Visual Studio 或 JetBrains Rider 手动构建它们。然后可以将生成的程序集(.dll 和 .pdb 文件)复制到您的 Unity 项目中(最好通过一些自动化的 post-build 事件)作为 managed plugins。然后您可以:
- 为每个文件定义任意数量的 classes
- 使用您想要的任何命名约定
- 使用嵌套的 classes 和
partial
classes,随心所欲,与纯 .NET 相同
- 为您的 POCO 编写不依赖于 Unity 或 Unity 测试框架的单元测试(它具有让您 运行 并行单元测试的令人敬畏的附带好处;Unity 只能 运行一次一个测试)
- 您甚至可以使用您想要的任何 C# 版本(尽管某些语言功能需要 运行时间功能,而这些功能在 Unity 的 Mono 版本中不存在)。 Unity 5.x 中的 C# 版本限制实际上是促使我首先选择此选项的原因!
不过,这绝对是一种更底层的脚本编写方法,需要更多的 C#、CLR、MSBuild 等知识。但是,如果您不怕弄脏自己的双手,那么 Ashley Davis 有一个 excellent article about setting up Visual Studio to generated managed plugins, and the pros/cons of that approach. You can also use the excellent Unity3D NuGet package 可以在您的源代码项目中对 Unity 程序集进行跨平台和面向未来的引用。
希望事情已经解决了,再次感谢之前的 answers/comments!
错了 !
至少它只是半真:
好的,是的,如果您的目标是通过 Inspector 的 Add Component 按钮使用和添加此组件,那么 NO 它需要位于具有匹配 class 名称的单个脚本文件中!
但是,如果你只在运行时通过脚本添加那个组件,那么YES你实际上可以做到!
Unity 本身就是这样做的,例如在 Dropdown
组件中(摘自使用 Rider 反编译的源代码)
public class Dropdown : Selectable, IPointerClickHandler, ISubmitHandler, ICancelHandler
{
protected internal class DropdownItem : MonoBehaviour, IPointerEnterHandler, ICancelHandler
{
...
}
...
}
(Selectable
继承自 UIBehaviour
继承自 MonoBehaviour
)
由于这种嵌套,在运行时稍后在检查器中看起来像这样
但绝对有可能!
要证明这一点,您可以轻松地做同样的事情,例如
public class TEST : MonoBehaviour
{
private class NESTED : MonoBehaviour
{
[SerializeField] private int someInt;
[SerializeField] private Transform someReference;
}
[ContextMenu("Add NESTED component")]
private void AddNested()
{
gameObject.AddComponent<NESTED>();
}
}
所以你可以看到,即使在播放模式之外的编辑时间,我也可以通过上下文菜单简单地添加这个“隐藏”组件
它仍然是序列化的,并且在关闭并重新加载场景后仍然存在:
我使用的是Unity 5.4.2f2 Personal,我写了一个类似下面的C#脚本:
using UnityEngine;
using System;
public class Outer : MonoBehaviour {
// Some public outer fields
public class Inner : MonoBehaviour {
// Some public inner fields
}
}
我希望能够将 Outer
和 Inner
附加到场景中的游戏对象。不幸的是,即使我向它添加 [Serializable]
,Inner
也不会在 Unity Inspector 中显示为可用脚本。我知道很多人可能会说如果我试图让嵌套的 class 对外界可见,我的设置有问题,但为了这个问题,我只想知道这是否可以完成。即,是否有任何方法可以使嵌套的 classes 可用作 Unity 组件?
不,无法将嵌套的 class 用作统一组件。要将 MonoBehaviour class 用作统一组件,class 名称 必须 等于脚本的文件名,因此使用嵌套 class 会有可能。
如果您在不重命名脚本文件的情况下将外部 class 重命名为您想要的任何其他名称,您将发现也无法使用新的外部 class。
[编辑]
我找到了 documentation。正如我所说: "The class name and file name must be the same to enable the script component to be attached to a GameObject."
我认为之前的回答不正确。
您应该能够通过使用 [MenuItem] 属性创建和绑定 MonoBehaviour 的静态方法创建编辑器脚本来添加嵌套的组件。
这在 Unity 5 及更高版本中有效。
您不能通过统一标准菜单添加它。
但是你可以通过脚本添加它。
添加组件
自问这个问题以来,我学到了很多东西,所以我想我会添加更多信息。
Unity如何编译脚本
Unity 使用内置版本的 Mono 编译器(跨平台 C# 编译器)编译项目中的所有脚本。它为每个脚本文件(在关联的 .meta
文件中)分配一个唯一的 GUID,就像它为每个其他资产所做的那样。这带来了一些不错的可用性改进:classes 可以在不破坏现有脚本引用的情况下重命名,因为 GUID 不会更改,并且脚本可以与其他相关资产(例如,所有“士兵”scripts/sounds/textures可以组合在一起)。
Unity的脚本命名限制
这种可用性的缺点是 Unity 实施了一些命名限制:
- 每个文件只有一个 class,与文件同名,否则 Unity 将不知道在组件脚本引用中使用哪个 GUID
- 没有嵌套 classes
- 在名为
MyBehaviour.cs
的文件中没有 class 名为 可能 没有如@derHugo 的评论所述,这是可能的。部分声明可以在相同或不同的文件中,即使名称不同,只要其中一个文件名与 class.partial
classes 的名称匹配即可
TheBehaviour
的文件
如果您 select 项目 Window 中的脚本资产并看到以下消息,那么您就知道您正在尝试以 Unity 不支持的方式命名事物:
但是,这些限制仅适用于 public class 派生自 MonoBehaviour
(或者可能来自 UnityEngine.Object
? ).您可以在文件中包含任意数量的其他私有 MonoBehaviour
或 POCOs,因为 Unity 永远不会序列化它们,因此它们不需要自己的 GUID。 Public 标记为 [Serializable]
的 POCOs 可能 具有与 public MonoBehaviour
s 相同的命名限制......我没有测试过这个所以我不确定。为什么我没有测试过这个?嗯...
使用托管插件避免这些限制
如果您和我一样,并且来自纯 .NET 背景,那么对可以在哪些文件中定义 classes 的任何限制可能会让您抓狂。在这种情况下,您可以将脚本存储在 Unity 项目之外(我使用与 Unity 项目相同的存储库中的另一个文件夹),并使用 IDE 像 Visual Studio 或 JetBrains Rider 手动构建它们。然后可以将生成的程序集(.dll 和 .pdb 文件)复制到您的 Unity 项目中(最好通过一些自动化的 post-build 事件)作为 managed plugins。然后您可以:
- 为每个文件定义任意数量的 classes
- 使用您想要的任何命名约定
- 使用嵌套的 classes 和
partial
classes,随心所欲,与纯 .NET 相同
- 为您的 POCO 编写不依赖于 Unity 或 Unity 测试框架的单元测试(它具有让您 运行 并行单元测试的令人敬畏的附带好处;Unity 只能 运行一次一个测试)
- 您甚至可以使用您想要的任何 C# 版本(尽管某些语言功能需要 运行时间功能,而这些功能在 Unity 的 Mono 版本中不存在)。 Unity 5.x 中的 C# 版本限制实际上是促使我首先选择此选项的原因!
不过,这绝对是一种更底层的脚本编写方法,需要更多的 C#、CLR、MSBuild 等知识。但是,如果您不怕弄脏自己的双手,那么 Ashley Davis 有一个 excellent article about setting up Visual Studio to generated managed plugins, and the pros/cons of that approach. You can also use the excellent Unity3D NuGet package 可以在您的源代码项目中对 Unity 程序集进行跨平台和面向未来的引用。
希望事情已经解决了,再次感谢之前的 answers/comments!
至少它只是半真:
好的,是的,如果您的目标是通过 Inspector 的 Add Component 按钮使用和添加此组件,那么 NO 它需要位于具有匹配 class 名称的单个脚本文件中!
但是,如果你只在运行时通过脚本添加那个组件,那么YES你实际上可以做到!
Unity 本身就是这样做的,例如在 Dropdown
组件中(摘自使用 Rider 反编译的源代码)
public class Dropdown : Selectable, IPointerClickHandler, ISubmitHandler, ICancelHandler { protected internal class DropdownItem : MonoBehaviour, IPointerEnterHandler, ICancelHandler { ... } ... }
(Selectable
继承自 UIBehaviour
继承自 MonoBehaviour
)
由于这种嵌套,在运行时稍后在检查器中看起来像这样
但绝对有可能!
要证明这一点,您可以轻松地做同样的事情,例如
public class TEST : MonoBehaviour
{
private class NESTED : MonoBehaviour
{
[SerializeField] private int someInt;
[SerializeField] private Transform someReference;
}
[ContextMenu("Add NESTED component")]
private void AddNested()
{
gameObject.AddComponent<NESTED>();
}
}
所以你可以看到,即使在播放模式之外的编辑时间,我也可以通过上下文菜单简单地添加这个“隐藏”组件
它仍然是序列化的,并且在关闭并重新加载场景后仍然存在: