在unity的C#脚本中添加一个组件的操作 gameObject.AddComponent() 虽然只是简单的一句话,但是其内部做了什么?
C#脚本绑定如下:
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Collections;
using System.Collections.Generic;
using UnityEngineInternal;
using UnityEngine.SceneManagement;
using UnityEngine.Bindings;
using UnityEngine.Scripting;
using uei = UnityEngine.Internal;
namespace UnityEngine
{
[ExcludeFromPreset]
[UsedByNativeCode]
[NativeHeader("Runtime/Export/Scripting/GameObject.bindings.h")]
public sealed partial class GameObject : Object
{
public T AddComponent<T>() where T : Component
{
return AddComponent(typeof(T)) as T;
}
[TypeInferenceRule(TypeInferenceRules.TypeReferencedByFirstArgument)]
public Component AddComponent(Type componentType)
{
return Internal_AddComponentWithType(componentType);
}
[FreeFunction(Name = "MonoAddComponentWithType", HasExplicitThis = true)] //C++中的 MonoAddComponentWithType()函数
private extern Component Internal_AddComponentWithType(Type componentType);
}
}
可以看到内部的函数调用是:ScriptingObjectPtr MonoAddComponentWithType(GameObject& go, ScriptingSystemTypeObjectPtr reflectionTypeObject);
ScriptingObjectPtr MonoAddComponentWithType(GameObject& go, ScriptingSystemTypeObjectPtr systemTypeInstance)
{
core::string error;
Unity::Component* component = NULL;
ScriptingClassPtr klass = scripting_class_from_systemtypeinstance(systemTypeInstance); //查找mono中的脚本类型
if (klass == SCRIPTING_NULL) //没有这样的类型
{
WarningStringObject("AddComponent asking for invalid type", &go);
return SCRIPTING_NULL;
}
if (klass == GetCoreScriptingClasses().monoBehaviour) //MonoBehaviour基础类型,不通过,必须是其派生类才行
{
ErrorStringObject("AddComponent with MonoBehaviour is not allowed. Create a class that derives from MonoBehaviour and add it instead.", &go);
return SCRIPTING_NULL;
}
InstanceID instanceID = go.GetInstanceID();
if (!IsMonoBehaviourUnityEngineType(klass))
{
...
}
else
{
MonoScript* script = GetMonoScriptManager().FindRuntimeScript(klass); //从已创建的类型中查找
#if UNITY_EDITOR
if (!script)
script = GetMonoScriptManager().FindEditorScript(klass);
#endif
if (!script)
CreateMonoScriptFromScriptingType(klass); //重新创建一个mono类型,并加入缓存
component = AddComponent(go, TypeOf<MonoBehaviour>(), klass, &error); //跳转 AddComponent()
}
if (component)
return Scripting::ScriptingWrapperFor(component);
else
{
if (!error.empty())
LogStringObject(error, PPtr<Object>(instanceID));
return SCRIPTING_NULL;
}
}
Unity::Component* AddComponent(GameObject& go, const Unity::Type* componentType, ScriptingClassPtr klass, core::string* error)
{
PROFILER_AUTO(gAddComponentProf, &go);
dynamic_array<AddComponentData> components(kMemTempAlloc);
dynamic_array<AddComponentData> processed(kMemTempAlloc);
if (componentType == NULL)
return NULL;
//搜集组件,以及所有依赖的组件,记得吗?C#中有个特性标签 [RequireComponent(typeof(...))]
if (!CollectComponentsWithoutAdding(go, componentType, klass, components, processed, error))
return NULL;
if (components.size() <= 0)
return NULL;
// Just simply add component without check.
Unity::Component* addedComponent = NULL;
//依次添加进 gameObject
for (unsigned int i = 0; i < components.size(); ++i)
{
AddComponentData& component = components[i];
if (error)
{
core::string tempError;
addedComponent = AddComponentUnchecked(go, component.m_Type, component.m_Class, &tempError);
if (!tempError.empty())
*error += tempError;
}
else
{
addedComponent = AddComponentUnchecked(go, component.m_Type, component.m_Class, NULL);//开始添加组件
}
}
return addedComponent;
}
static Unity::Component* AddComponentUnchecked(GameObject& go, const Unity::Type* componentType, ScriptingClassPtr klass, MonoScriptPtr script, core::string* error)
{
if (componentType == NULL)
return NULL;
const bool isTransform = componentType->IsDerivedFrom<Transform>();
if (isTransform)
{
if (componentType == TypeOf<Transform>() && go.QueryComponentByExactType(TypeOf<Transform>()))
{
ErrorFormatChecked(error, "Can't add a %s component because one is already added.", componentType->GetName());
return NULL;
}
}
Unity::Component* component = ProduceComponentFromCode(componentType, error); //创建新的组件
if (component == NULL)
return NULL;
if (isTransform) //Transform组件特殊处理,略...
{
bool hasTransform = (go.QueryComponent<Transform>() != NULL);
Transform* transform = dynamic_pptr_cast<Transform*>(component);
if (hasTransform)
{
go.ReplaceTransformComponentInternal(transform);
transform->ResetReplacement();
}
else
{
go.AddFirstTransformComponentInternal(transform);
InitializeComponentCreatedFromCode(transform);
}
}
else
{
go.AddComponentInternal(component); //将新组件添加进 GameObject 中缓存起来
MonoBehaviour* monoBehaviour = dynamic_pptr_cast<MonoBehaviour*>(component);
if (monoBehaviour) //是 MonoBehaviour 类型的组件
{
if (!SetupScriptForMonoBehaviour(monoBehaviour, klass, script)) //将mono脚本类型添加进 MonoBehaviour组件中
return NULL;
if (!InitializeMonoBehaviourCreatedFromCode(monoBehaviour)) //一些初始化如调用 MonoBehaviour::SmartReset()
return NULL;
}
else
{
InitializeComponentCreatedFromCode(component);
}
}
go.SendMessage(kDidAddComponent, component);
FinalizeComponentCreationFromCode(component); //完成组件添加,调转。。。
InvokeAddComponentCallback(*component);
return component;
}
static inline void FinalizeComponentCreationFromCode(Unity::Component* component)
{
component->AwakeFromLoad(kInstantiateOrCreateFromCodeAwakeFromLoad); //组件添加完成最后触发 MonoBehaviour::AwakeFromLoad()
component->SetDirty();
}
可以看到简单的一句 gameObject.AddComponent 尽然做了这么多事,从查找mono中是否有该类型,到创建mono类型缓存,到创建MonoBehaviour对象,到加入GameObject,到最后的 MonoBehaviour::AwakeFromLoad() 触发。
再次回顾一下C++中的 MonoBehaviour 一些关键数据:
class MonoBehaviour : public Behaviour, public IManagedObjectHost
{
REGISTER_CLASS_TRAITS(kTypeIsSealed);
REGISTER_CLASS(MonoBehaviour);
DEFINE_GET_TYPESTRING(MonoBehaviour);
MANAGED_OBJECT_HOST_VIRTUAL_REDIRECT(MonoBehaviour);
MANAGED_OBJECT_HOST_VIRTUAL_REDIRECT_STRIPPED(MonoBehaviour);
public:
MonoBehaviour(MemLabelId label, ObjectCreationMode mode);
...
//调用C#脚本中的方法,按方法名调
bool CallMethodInactive(const char* methodName) { return m_ScriptedObject.CallMethod(*this, methodName); }
//调用C#脚本中的方法,按方法引用调
bool CallMethodInactive(ScriptingMethodPtr method) { return m_ScriptedObject.CallMethod(*this, method); }
//查找C#脚本中的方法
riptingMethodPtr GetMethod(int index) const { return m_ScriptedObject.GetCachedMethod(index); }
...
private:
...
ManagedMonoBehaviourRef m_ScriptedObject; //保存的mono脚本的引用(也就是我们写的C#脚本)
...
}
到此,关于 MonoBehaviour 的初始化以及 C++和 C# 的关联关系基本理清了。