Unity引擎源码解析(伪) - 2 MonoBehaviour

文章详细解析了Unity中GameObject.AddComponent()函数的内部工作机制,包括类型检查、MonoBehaviour的创建与初始化过程,以及C#和C++之间的交互。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在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# 的关联关系基本理清了。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值