适用场景
优化功能时 保留老预制体且老预制体的脚本公共变量引用绑定很多 ,
新脚本需要重新绑定时
原因
- 不能在UI初始化写查找代码 查找的代码其实都是一样的 且毫无意义 从性能上说 代码里查找差一点点,所以预制体挂在了大量脚本
- 如果结合MVC思想,这种绑定只是纯View的代码
- 引用关系大量 且需要更换脚本
实现
首先,获取Mark脚本中指定的组件类型type,并调用FnmGetNameField方法获取一个字典dicNameField,该字典存储了需要绑定的成员变量名及其对应的子物体名(这里假设子物体名与成员变量名存在某种关联)。接着,为目标组件创建SerializedObject,用于在编辑器环境下操作组件的序列化数据。然后遍历字典,对于每个成员变量:
通过FindProperty方法找到对应的SerializedProperty。虽然有部分条件判断被注释掉,但原本的意图可能是进一步确保属性类型为可引用对象类型(如GameObject或其他组件)。
使用targetCompo.gameObject.transform.Find(pair.Value).gameObject在目标组件所在的游戏对象下,根据子物体名查找对应的子物体。如果找到,就将该子物体赋值给成员变量对应的SerializedProperty的objectReferenceValue,完成绑定。最后,通过ApplyModifiedProperties将修改应用到组件,使其在编辑器中显示的绑定关系得到更新。
csharp
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System;
using UnityEditor;
using UnityEngine;
using static UnityEngine.InputManagerEntry;
public class ComponentBinder : MonoBehaviour
{
/// <summary>
/// 对象
/// </summary>
private static Component targetCompo;
[MenuItem("GameObject/ForBind", priority = 29)]
public static void CreateUICode()
{
GameObject obj = Selection.activeGameObject;
if (!obj) return;
//获取标记的脚本
Mark mark = obj.GetComponent<Mark>();
if (!mark)
{
Debug.Log("请挂载Mark脚本");
return;
}
targetCompo = obj.GetComponent(mark.CompoClass.GetType().Name);
try
{
Type type = mark.CompoClass.GetType();
Dictionary<string, string> dicNameField = FnmGetNameField(type);
SerializedObject serializedObject = new SerializedObject(obj.GetComponent(mark.targetCompoClass.GetType().Name));
foreach (var pair in dicNameField)
{
SerializedProperty property = serializedObject.FindProperty(pair.Key);
//if (property != null && property.propertyType == SerializedPropertyType.ObjectReference)
//{
GameObject referencedGameObject = targetCompo.gameObject.transform.Find(pair.Value).gameObject;
if (referencedGameObject != null)
{
property.objectReferenceValue = referencedGameObject;
}
//}
}
serializedObject.ApplyModifiedProperties();
if (dicNameField.Count > 0)
{
StringBuilder msg = new StringBuilder("==>成员 : ");
foreach (string name in dicNameField.Keys)
{
msg.Append(name + "、");
}
Debug.LogError(msg + " 无法绑定,请确认物体绑定了Bind脚本或名字与成员名是否一致。");
return;
}
}
catch (Exception e)
{
Debug.LogError("绑定对应物体出错了\n" + e.Message);
return;
}
Debug.Log("=====>获取物体" + obj.name + "上的脚本" + mark.CompoClass.name + "的成员成功了。");
}
/// <summary>
/// 查找脚本中所有需要绑定成员的名字、数据
/// </summary>
/// <param name="type">脚本类型</param>
/// <returns></returns>
private static Dictionary<string, string> FnmGetNameField(Type type)
{
Dictionary<string, string> dicNameField = new Dictionary<string, string>();
SerializedObject serializedObject = new SerializedObject(targetCompo);
foreach (FieldInfo field in type.GetFields())
{
if (!field.IsNotSerialized)
{
dicNameField[field.Name] = (serializedObject.FindProperty(field.Name).objectReferenceValue).name;
Debug.Log("=====>获取物体" + field.Name + "|||" + (serializedObject.FindProperty(field.Name).objectReferenceValue).name + "");
}
}
return dicNameField;
}
}
遇到的问题
1.提示了Scene mismatch (cross scene references not supported)
当在 Unity 中遇到 "Scene mismatch (cross scene references not supported)" 这个提示时,意味着你正在尝试进行跨场景的引用操作,但 Unity 默认是不支持这种跨场景引用的,以下是可能导致该问题出现的原因以及对应的解决办法:
一、原因分析
- 通过 GameObject.Find 查找其他场景中的物体
在你之前的代码中,如果使用了 GameObject.Find 函数去查找物体,并且期望找到的物体位于当前场景之外的其他场景中,就会触发这个错误。例如,在场景 A 的脚本代码里,尝试通过 GameObject.Find 去获取场景 B 中的某个游戏对象来绑定到变量上,像这样:
csharp
public class MyScript : MonoBehaviour
{
void Start()
{
GameObject otherSceneObject = GameObject.Find("ObjectNameInAnotherScene");
// 后续尝试使用这个对象进行赋值等操作,就可能触发错误
}
}
因为 GameObject.Find 只会在当前激活的场景中查找符合条件的对象,无法跨越场景去查找。
- 在序列化属性中设置了跨场景的对象引用
在 Unity 编辑器环境下,通过 Inspector 面板或者自定义编辑器脚本设置序列化公共变量(比如 GameObject 类型的变量)的引用时,如果不小心将一个来自其他场景的对象拖放到变量引用位置,同样会出现此问题。例如,在场景 A 中的脚本组件的 Inspector 中,想要为一个 public GameObject myObject; 变量指定引用,却错误地拖放了场景 B 里的游戏对象。
2.pecified cast is not valid.
在 Unity 中遇到 "Specified cast is not valid." 这个错误提示,通常意味着你正在进行一个类型转换操作,但实际的数据类型与期望转换的类型不匹配,以下是几种可能导致该错误出现的情况以及相应的解决办法:
- 变量赋值时类型不匹配
情况描述:
当你试图将一个不兼容类型的值赋给某个变量,并且进行了显式或隐式的类型转换时,就可能出现此错误。例如,你有一个变量声明为 int 类型,却试图将一个 string 值赋给它并进行转换(即便在代码中看起来像是合理的类型转换写法)。
示例代码及分析: