C# Unity:如何将未知脚本动态附加到游戏对象(自定义编辑器)
我目前正在为Unity编辑器(自定义检查器和自定义窗口)制作一个系统,该系统将使制作游戏的艺术家实现自动化并使事情变得更容易,但我遇到了麻烦 我试图找到一种方法,通过编辑器文本字段输入和GUI按钮,将未知脚本动态添加到场景中的游戏对象中。 艺术家/程序员将在文本字段中键入脚本的名称,它将搜索并添加到gameobject,但我不知道如何继续,特别是因为自Unity 5.3起,C# Unity:如何将未知脚本动态附加到游戏对象(自定义编辑器),c#,unity3d,unity5,C#,Unity3d,Unity5,我目前正在为Unity编辑器(自定义检查器和自定义窗口)制作一个系统,该系统将使制作游戏的艺术家实现自动化并使事情变得更容易,但我遇到了麻烦 我试图找到一种方法,通过编辑器文本字段输入和GUI按钮,将未知脚本动态添加到场景中的游戏对象中。 艺术家/程序员将在文本字段中键入脚本的名称,它将搜索并添加到gameobject,但我不知道如何继续,特别是因为自Unity 5.3起,gameobject.AddComponent()的某些函数已被弃用 以下是我试图做的: public string scr
gameobject.AddComponent()
的某些函数已被弃用
以下是我试图做的:
public string scriptname;
GameObject obj = null;
scriptname = EditorGUILayout.TextField("Script name:", scriptname, GUILayout.MaxHeight(25));
if (GUILayout.Button("Attach script"))
{
//search for the script to check if it exists, using DirectoryInfo
DirectoryInfo dir = new DirectoryInfo(Application.dataPath);
FileInfo[] info = dir.GetFiles("*.*", SearchOption.AllDirectories);
foreach (FileInfo f in info) // cycles through all the files
{
if(f.Name == scriptname)
{
//attaches to the gameobject (NOT WORKING)
System.Type MyScriptType = System.Type.GetType(scriptname + ",Assembly-CSharp");
obj.AddComponent(MyScriptType);
}
}
}
(当然,这是一个总结版本,我从脚本的不同部分复制了相关行)
但它不起作用。
有什么想法吗?我建议这样做:
if(GUILayout.Button("Attach script"))
{
// check if type is contained in your assembly:
Type type = typeof(MeAssemblyType).Assembly.GetTypes().FirstOrDefault(t => t.Name == scriptname);
if(type != null)
{
// script exists in the same assembly that MeAssemblyType is
obj.AddComponent(type); // add the component
}
else
{
// display some error message
}
}
当然,如果您使用一些包含其他组件的插件(依赖项),这将失败,但要处理此问题,您只需检查程序集的依赖项:
typeof(MeAssemblyType) // your type from Assembly-CSharp
.Assembly // Assembly-CSharp assembly
.GetReferencedAssemblies() // get referenced assemblies
.FirstOrDefault(m =>
m.Assembly // from this assembly
.GetTypes() // get all types
.FirstOrDefault(t =>
t.Name == scriptname // select first one that matches the name
)
)
备注:
getReferenceAssembly
方法将仅返回程序集“使用”(加载)的程序集。为了清楚起见,假设您正在引用这些程序集:
static void Main()
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(<some_xml_input>);
}
这意味着它不会加载NewtonsoftJson
,因为它没有在该程序集中使用
更好的建议:
我建议您混合使用answer中的方法,但不要加载程序集,因为当Unity的编辑器开始您的项目时,已经加载了程序集。而是使用
getReferenceAssemblys
方法,从那里您应该调用GetTypes
方法来检索该程序集中所有可能的类型。(这会很慢,但会保证您获得所需的结果)之后,您可以使用FirstOrDefault
或自己迭代键入[]
来找到所需的内容。这仍然是可能的。用这个
UnityEngineInternal.APIUpdaterRuntimeServices.AddComponent(GameObject go, "", string componentName);
希望这对我有所帮助经过广泛的实验后,我能得到这个。这也涵盖了所有Unity组件。只是把它作为一种扩展方法,让生活更轻松
public static class ExtensionMethod
{
public static Component AddComponentExt(this GameObject obj, string scriptName)
{
Component cmpnt = null;
for (int i = 0; i < 10; i++)
{
//If call is null, make another call
cmpnt = _AddComponentExt(obj, scriptName, i);
//Exit if we are successful
if (cmpnt != null)
{
break;
}
}
//If still null then let user know an exception
if (cmpnt == null)
{
Debug.LogError("Failed to Add Component");
return null;
}
return cmpnt;
}
private static Component _AddComponentExt(GameObject obj, string className, int trials)
{
//Any script created by user(you)
const string userMadeScript = "Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null";
//Any script/component that comes with Unity such as "Rigidbody"
const string builtInScript = "UnityEngine, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null";
//Any script/component that comes with Unity such as "Image"
const string builtInScriptUI = "UnityEngine.UI, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null";
//Any script/component that comes with Unity such as "Networking"
const string builtInScriptNetwork = "UnityEngine.Networking, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null";
//Any script/component that comes with Unity such as "AnalyticsTracker"
const string builtInScriptAnalytics = "UnityEngine.Analytics, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null";
//Any script/component that comes with Unity such as "AnalyticsTracker"
const string builtInScriptHoloLens = "UnityEngine.HoloLens, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null";
Assembly asm = null;
try
{
//Decide if to get user script or built-in component
switch (trials)
{
case 0:
asm = Assembly.Load(userMadeScript);
break;
case 1:
//Get UnityEngine.Component Typical component format
className = "UnityEngine." + className;
asm = Assembly.Load(builtInScript);
break;
case 2:
//Get UnityEngine.Component UI format
className = "UnityEngine.UI." + className;
asm = Assembly.Load(builtInScriptUI);
break;
case 3:
//Get UnityEngine.Component Video format
className = "UnityEngine.Video." + className;
asm = Assembly.Load(builtInScript);
break;
case 4:
//Get UnityEngine.Component Networking format
className = "UnityEngine.Networking." + className;
asm = Assembly.Load(builtInScriptNetwork);
break;
case 5:
//Get UnityEngine.Component Analytics format
className = "UnityEngine.Analytics." + className;
asm = Assembly.Load(builtInScriptAnalytics);
break;
case 6:
//Get UnityEngine.Component EventSystems format
className = "UnityEngine.EventSystems." + className;
asm = Assembly.Load(builtInScriptUI);
break;
case 7:
//Get UnityEngine.Component Audio format
className = "UnityEngine.Audio." + className;
asm = Assembly.Load(builtInScriptHoloLens);
break;
case 8:
//Get UnityEngine.Component SpatialMapping format
className = "UnityEngine.VR.WSA." + className;
asm = Assembly.Load(builtInScriptHoloLens);
break;
case 9:
//Get UnityEngine.Component AI format
className = "UnityEngine.AI." + className;
asm = Assembly.Load(builtInScript);
break;
}
}
catch (Exception e)
{
//Debug.Log("Failed to Load Assembly" + e.Message);
}
//Return if Assembly is null
if (asm == null)
{
return null;
}
//Get type then return if it is null
Type type = asm.GetType(className);
if (type == null)
return null;
//Finally Add component since nothing is null
Component cmpnt = obj.AddComponent(type);
return cmpnt;
}
}
了解我是如何做到这一点很重要,这样您就可以在将来的任何Unity更新中添加对新组件的支持 对于用户创建的任何脚本:
gameObject.AddComponentExt("YourScriptOrComponentName");
1。找出需要加入的内容???在Assembly.Load
功能中
Assembly asm = Assembly.Load("???");
Assembly asm = Assembly.Load("Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null");
Type type = asm.GetType(???);
Assembly asm = Assembly.Load("???");
Assembly asm = Assembly.Load("UnityEngine, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null");
Type type = asm.GetType(???);
您可以在脚本中添加以下内容:
Debug.Log("Info: " + this.GetType().Assembly);
ParticleSystem pt = gameObject.AddComponent<ParticleSystem>();
Debug.Log("Info11: " + pt.GetType().Assembly);
ParticleSystem pt = gameObject.AddComponent<ParticleSystem>();
Debug.Log("Info: " + pt.GetType());
我得到:Assembly-CSharp,Version=0.0.0,Culture=neutral,PublicKeyToken=null
我们现在应该替换???用这个
Assembly asm = Assembly.Load("Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null");
Assembly asm = Assembly.Load("UnityEngine, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null");
2。找出需要加入的内容???在asm.GetType
函数中
Assembly asm = Assembly.Load("???");
Assembly asm = Assembly.Load("Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null");
Type type = asm.GetType(???);
Assembly asm = Assembly.Load("???");
Assembly asm = Assembly.Load("UnityEngine, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null");
Type type = asm.GetType(???);
在本例中,它只是要添加到游戏对象的脚本的名称
假设脚本名为NathanScript
:
Assembly asm = Assembly.Load("Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null");
Type type = asm.GetType("NathanScript");
gameObject.AddComponent(type);
对于非用户创建的Unity内置脚本/组件脚本:
gameObject.AddComponentExt("YourScriptOrComponentName");
例如,刚体
,线条渲染器
,图像
组件。只是用户未创建的任何组件
1。找出需要加入的内容???在Assembly.Load
功能中
Assembly asm = Assembly.Load("???");
Assembly asm = Assembly.Load("Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null");
Type type = asm.GetType(???);
Assembly asm = Assembly.Load("???");
Assembly asm = Assembly.Load("UnityEngine, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null");
Type type = asm.GetType(???);
您可以在脚本中添加以下内容:
Debug.Log("Info: " + this.GetType().Assembly);
ParticleSystem pt = gameObject.AddComponent<ParticleSystem>();
Debug.Log("Info11: " + pt.GetType().Assembly);
ParticleSystem pt = gameObject.AddComponent<ParticleSystem>();
Debug.Log("Info: " + pt.GetType());
2。找出需要加入的内容???在asm.GetType
函数中
Assembly asm = Assembly.Load("???");
Assembly asm = Assembly.Load("Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null");
Type type = asm.GetType(???);
Assembly asm = Assembly.Load("???");
Assembly asm = Assembly.Load("UnityEngine, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null");
Type type = asm.GetType(???);
您可以在脚本中添加以下内容:
Debug.Log("Info: " + this.GetType().Assembly);
ParticleSystem pt = gameObject.AddComponent<ParticleSystem>();
Debug.Log("Info11: " + pt.GetType().Assembly);
ParticleSystem pt = gameObject.AddComponent<ParticleSystem>();
Debug.Log("Info: " + pt.GetType());
假设要添加的组件是:
将其组合到扩展方法中:
gameObject.AddComponentExt("YourScriptOrComponentName");
如您所见,添加您创建的脚本和Unity附带的脚本/组件需要完全不同的过程。您可以通过检查类型if
null
来修复此问题。如果类型为null
,请执行其他步骤。如果另一个步骤也是null
,那么脚本只会不退出。反编译AddComponentUnity窗口。还添加了链接:然后对窗口进行如下调用:
string typeString = "UnityEngine." + componentName;
ws.winx.editor.windows.AddComponentWindow.Show(rect);
ws.winx.editor.windows.AddComponentWindow.OnClose += OnCloseComponentSelectedFromPopUpMenu;
ws.winx.editor.windows.AddComponentWindow.ComponentSelected += (menuPath) => ComponentSelectedFromPopUpMenu(positionData.Item1, menuPath);
手柄返回
private void ComponentSelectedFromPopUpMenu(Vector2 position, string menuPath) {
MonoScript monoScript;
char[] kPathSepChars = new char[]
{
'/',
'\\'
};
menuPath = menuPath.Replace(" ", "");
string[] pathElements = menuPath.Split(kPathSepChars);
string fileName = pathElements[pathElements.Length - 1].Replace(".cs", "");
if (pathElements[0] == "Assets") {
Debug.LogWarning("Unity need to compile new added file so can be included");
} else if (pathElements.Length == 2) {
//use fileName
//do something
} else if (pathElements[1] == "Scripts") {//Component/Scripts/MyScript.cs
string[] guids = AssetDatabase.FindAssets("t:Script " + fileName.Replace(".cs", ""));
if (guids.Length > 0) {
for (int i = 0; i < guids.Length; i++) {
monoScript = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(guids[i]), typeof(MonoScript)) as MonoScript;
Type typet = monoScript.GetClass();
if (typet == null) continue;
} else {//Component/Physics/Rigidbody
//try to find by type, cos probably Unity type
Type unityType = ReflectionUtility.GetType("UnityEngine." + fileName);
if (unityType != null) {
//do something
return;
}
//Based on attribute [AddComponentMenu("Logic/MyComponent")]
//Component/Logics/MyComponent
string[] guids = AssetDatabase.FindAssets("t:Script " + fileName.Replace(".cs", ""));
if (guids.Length > 0) {
for (int i = 0; i < guids.Length; i++) {
monoScript = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(guids[i]), typeof(MonoScript)) as MonoScript;
Type typet = monoScript.GetClass();
if (typet == null) continue;
object[] addComponentMenuAttributes = typet.GetCustomAttributes(typeof(AddComponentMenu), true);
if (addComponentMenuAttributes != null && addComponentMenuAttributes.Length > 0 && "Component/" + ((AddComponentMenu)addComponentMenuAttributes[0]).componentMenu == menuPath)
{
//do somethings
}
}
}
}
}
private void component从弹出菜单中选择(矢量2位置,字符串菜单路径){
单字单字;
char[]kPathSepChars=新字符[]
{
'/',
'\\'
};
menuPath=menuPath.Replace(“,”);
string[]pathElements=menuPath.Split(kPathSepChars);
字符串文件名=pathElements[pathElements.Length-1]。替换(“.cs”,”);
if(路径元素[0]=“资产”){
LogWarning(“Unity需要编译新添加的文件以便可以包含在内”);
}else if(pathElements.Length==2){
//使用文件名
//做点什么
}else if(pathElements[1]=“Scripts”){//Component/Scripts/MyScript.cs
字符串[]guids=AssetDatabase.FindAssets(“t:Script”+fileName.Replace(“.cs”,”);
如果(guids.Length>0){
for(int i=0;i