Unity的GetComponent()如何工作?

时间:2017-05-22 05:12:19

标签: c++ unity3d data-structures

我一直在尝试制作一个类似于Unity的基于组件的系统,但是在C ++中。我想知道Unity实现的GetComponent()方法是如何工作的。这是一个非常强大的功能。具体来说,我想知道它用来存储组件的容器类型。

我在克隆此功能时需要的两个标准如下。 1.我还需要返回任何继承的组件。例如,如果SphereCollider继承Collider,则GetComponent< Collider>()将返回附加到GameObject的SphereCollider,但GetComponent< SphereCollider>()不会返回附加的任何Collider。我需要快速的功能。优选地,它将使用某种散列函数。

对于标准一,我知道我可以使用类似于以下实现的东西

std::vector<Component*> components
template <typename T>
T* GetComponent()
{
    for each (Component* c in components)
        if (dynamic_cast<T>(*c))
            return (T*)c;
    return nullptr;
}

但这不符合快速的第二个标准。为此,我知道我可以做这样的事情。

std::unordered_map<type_index, Component*> components
template <typename T>
T* GetComponent()
{
    return (T*)components[typeid(T)];
}

但同样,这不符合第一个标准。

如果有人知道某种方法来结合这两个功能,即使它比第二个例子慢一点,我也愿意牺牲一点。谢谢!

4 个答案:

答案 0 :(得分:5)

由于我正在编写自己的游戏引擎并采用相同的设计,我以为我会分享我的结果。

概述

我为我的Components实例的GameObject编写了我自己的RTTI课程。 #define两个宏CLASS_DECLARATIONCLASS_DEFINITION

会减少输入的数量

CLASS_DECLARATION声明将用于标识static const std::size_t类型(class)的唯一Type,以及允许对象遍历的virtual函数他们的class层次结构通过调用同名的父类功能(IsClassType)。

CLASS_DEFINITION定义了这两件事。即Type被初始化为class名称的字符串化版本的哈希值(使用TO_STRING(x) #x),因此Type比较只是一个int比较而不是一个字符串比较

std::hash<std::string>是使用的哈希函数,它保证相等的输入产生相等的输出,并且冲突的数量接近于零。

除了散列冲突的低风险之外,此实现还有一个额外的好处,即允许用户使用这些宏创建自己的Component类,而无需参考|扩展某些主include文件enum class的{​​{1}},或使用typeid(仅提供运行时类型,而不是父类)。

AddComponent

此自定义RTTI简化了Add|Get|RemoveComponent的调用语法,只需指定template类型,就像Unity一样。

AddComponent方法完美地将通用引用可变参数包转发给用户的构造函数。因此,例如,用户定义的Component派生的class CollisionModel可以拥有构造函数:

CollisionModel( GameObject * owner, const Vec3 & size, const Vec3 & offset, bool active );

然后用户只需调用:

myGameObject.AddComponent<CollisionModel>(this, Vec3( 10, 10, 10 ), Vec3( 0, 0, 0 ), true );

请注意Vec3的显式构造,因为如果使用推导的初始化列表语法(如{ 10, 10, 10 }而不管Vec3&},则完美转发可能无法链接#39;构造函数声明。

此自定义RTTI还解决了std::unordered_map<std::typeindex,...>解决方案的3个问题:

  1. 即使使用std::tr2::direct_bases进行层次结构遍历,最终结果仍然是地图中相同指针的重复。
  2. 用户无法添加多个等效类型的组件,除非使用允许/解决冲突而不会覆盖的映射,这会进一步降低代码速度。
  3. 不确定且需要慢dynamic_cast,只需要static_cast
  4. GetComponent

    GetComponent只使用static const std::size_t Type类型的template作为virtual bool IsClassType方法的参数,并迭代std::vector< std::unique_ptr< Component > >寻找第一个匹配。< / p>

    我还实现了GetComponents方法,该方法可以获取所请求类型的所有组件,同样包括从父级获取。

    请注意,使用和不使用该类的实例都可以访问static成员Type

    另请注意,Typepublic,为每个Component派生类声明,并且尽管是POD成员,但大写以强调其灵活使用。

    RemoveComponent

    最后,RemoveComponent使用C++14的init-capture将static const std::size_t Type类型的template传递给lambda,因此基本上可以执行相同的向量遍历,这次得到iterator到第一个匹配元素。

    代码中有一些关于更灵活实施的想法的评论,更不用说所有这些版本的const版本也可以轻松实现。

    代码

    Classes.h

    #ifndef TEST_CLASSES_H
    #define TEST_CLASSES_H
    
    #include <string>
    #include <functional>
    #include <vector>
    #include <memory>
    #include <algorithm>
    
    #define TO_STRING( x ) #x
    
    //****************
    // CLASS_DECLARATION
    //
    // This macro must be included in the declaration of any subclass of Component.
    // It declares variables used in type checking.
    //****************
    #define CLASS_DECLARATION( classname )                                                      \
    public:                                                                                     \
        static const std::size_t Type;                                                          \
        virtual bool IsClassType( const std::size_t classType ) const override;                 \
    
    //****************
    // CLASS_DEFINITION
    // 
    // This macro must be included in the class definition to properly initialize 
    // variables used in type checking. Take special care to ensure that the 
    // proper parentclass is indicated or the run-time type information will be
    // incorrect. Only works on single-inheritance RTTI.
    //****************
    #define CLASS_DEFINITION( parentclass, childclass )                                         \
    const std::size_t childclass::Type = std::hash< std::string >()( TO_STRING( childclass ) ); \
    bool childclass::IsClassType( const std::size_t classType ) const {                         \
            if ( classType == childclass::Type )                                                \
                return true;                                                                    \
            return parentclass::IsClassType( classType );                                       \
    }                                                                                           \
    
    namespace rtti {
    
    //***************
    // Component
    // base class
    //***************
    class Component {
    public:         
    
    static const std::size_t                    Type;
    virtual bool                                IsClassType( const std::size_t classType ) const { 
                                                    return classType == Type; 
                                                }
    
    public:
    
        virtual                                ~Component() = default;
                                                Component( std::string && initialValue ) 
                                                    : value( initialValue ) { 
                                                }
    
    public:
    
        std::string                             value = "uninitialized";
    };
    
    //***************
    // Collider
    //***************
    class Collider : public Component {
    
        CLASS_DECLARATION( Collider )
    
    public:
    
                                                Collider( std::string && initialValue ) 
                                                    : Component( std::move( initialValue ) ) { 
                                                }
    };
    
    //***************
    // BoxCollider
    //***************
    class BoxCollider : public Collider {
    
        CLASS_DECLARATION( BoxCollider )
    
    public:
    
                                                BoxCollider( std::string && initialValue ) 
                                                    : Collider( std::move( initialValue ) ) { 
                                                }
    };
    
    //***************
    // RenderImage
    //***************
    class RenderImage : public Component {
    
        CLASS_DECLARATION( RenderImage )
    
    public:
    
                                                RenderImage( std::string && initialValue ) 
                                                    : Component( std::move( initialValue ) ) { 
                                                }
    };
    
    //***************
    // GameObject
    //***************
    class GameObject {
    public:
    
        std::vector< std::unique_ptr< Component > > components;
    
    public:
    
        template< class ComponentType, typename... Args >
        void                                    AddComponent( Args&&... params );
    
        template< class ComponentType >
        ComponentType &                         GetComponent();
    
        template< class ComponentType >
        bool                                    RemoveComponent();
    
        template< class ComponentType >
        std::vector< ComponentType * >          GetComponents();
    
        template< class ComponentType >
        int                                     RemoveComponents();
    };
    
    //***************
    // GameObject::AddComponent
    // perfect-forwards all params to the ComponentType constructor with the matching parameter list
    // DEBUG: be sure to compare the arguments of this fn to the desired constructor to avoid perfect-forwarding failure cases
    // EG: deduced initializer lists, decl-only static const int members, 0|NULL instead of nullptr, overloaded fn names, and bitfields
    //***************
    template< class ComponentType, typename... Args >
    void GameObject::AddComponent( Args&&... params ) {
        components.emplace_back( std::make_unique< ComponentType >( std::forward< Args >( params )... ) );
    }
    
    //***************
    // GameObject::GetComponent
    // returns the first component that matches the template type
    // or that is derived from the template type
    // EG: if the template type is Component, and components[0] type is BoxCollider
    // then components[0] will be returned because it derives from Component
    //***************
    template< class ComponentType >
    ComponentType & GameObject::GetComponent() {
        for ( auto && component : components ) {
            if ( component->IsClassType( ComponentType::Type ) )
                return *static_cast< ComponentType * >( component.get() );
        }
    
        return *std::unique_ptr< ComponentType >( nullptr );
    }
    
    //***************
    // GameObject::RemoveComponent
    // returns true on successful removal
    // returns false if components is empty, or no such component exists
    //***************
    template< class ComponentType >
    bool GameObject::RemoveComponent() {
        if ( components.empty() )
            return false;
    
        auto & index = std::find_if( components.begin(), 
                                        components.end(), 
                                        [ classType = ComponentType::Type ]( auto & component ) { 
                                        return component->IsClassType( classType ); 
                                        } );
    
        bool success = index != components.end();
    
        if ( success )
            components.erase( index );
    
        return success;
    }
    
    //***************
    // GameObject::GetComponents
    // returns a vector of pointers to the the requested component template type following the same match criteria as GetComponent
    // NOTE: the compiler has the option to copy-elide or move-construct componentsOfType into the return value here
    // TODO: pass in the number of elements desired (eg: up to 7, or only the first 2) which would allow a std::array return value,
    // except there'd need to be a separate fn for getting them *all* if the user doesn't know how many such Components the GameObject has
    // TODO: define a GetComponentAt<ComponentType, int>() that can directly grab up to the the n-th component of the requested type
    //***************
    template< class ComponentType >
    std::vector< ComponentType * > GameObject::GetComponents() {
        std::vector< ComponentType * > componentsOfType;
    
        for ( auto && component : components ) {
            if ( component->IsClassType( ComponentType::Type ) )
                componentsOfType.emplace_back( static_cast< ComponentType * >( component.get() ) );
        }
    
        return componentsOfType;
    }
    
    //***************
    // GameObject::RemoveComponents
    // returns the number of successful removals, or 0 if none are removed
    //***************
    template< class ComponentType >
    int GameObject::RemoveComponents() {
        if ( components.empty() )
            return 0;
    
        int numRemoved = 0;
        bool success = false;
    
        do {
            auto & index = std::find_if( components.begin(), 
                                            components.end(), 
                                            [ classType = ComponentType::Type ]( auto & component ) { 
                                            return component->IsClassType( classType ); 
                                            } );
    
            success = index != components.end();
    
            if ( success ) {
                components.erase( index );
                ++numRemoved;
            }
        } while ( success );
    
        return numRemoved;
    }
    
    }      /* rtti */
    #endif /* TEST_CLASSES_H */
    

    Classes.cpp

    #include "Classes.h"
    
    using namespace rtti;
    
    const std::size_t Component::Type = std::hash<std::string>()(TO_STRING(Component));
    
    CLASS_DEFINITION(Component, Collider)
    CLASS_DEFINITION(Collider, BoxCollider)
    CLASS_DEFINITION(Component, RenderImage)
    

    的main.cpp

    #include <iostream>
    #include "Classes.h"
    
    #define MORE_CODE 0
    
    int main( int argc, const char * argv ) {
    
        using namespace rtti;
    
        GameObject test;
    
        // AddComponent test
        test.AddComponent< Component >( "Component" );
        test.AddComponent< Collider >( "Collider" );
        test.AddComponent< BoxCollider >( "BoxCollider_A" );
        test.AddComponent< BoxCollider >( "BoxCollider_B" );
    
    #if MORE_CODE
        test.AddComponent< RenderImage >( "RenderImage" );
    #endif
    
        std::cout << "Added:\n------\nComponent\t(1)\nCollider\t(1)\nBoxCollider\t(2)\nRenderImage\t(0)\n\n";
    
        // GetComponent test
        auto & componentRef     = test.GetComponent< Component >();
        auto & colliderRef      = test.GetComponent< Collider >();
        auto & boxColliderRef1  = test.GetComponent< BoxCollider >();
        auto & boxColliderRef2  = test.GetComponent< BoxCollider >();       // boxColliderB == boxColliderA here because GetComponent only gets the first match in the class hierarchy
        auto & renderImageRef   = test.GetComponent< RenderImage >();       // gets &nullptr with MORE_CODE 0
    
        std::cout << "Values:\n-------\ncomponentRef:\t\t"  << componentRef.value
                  << "\ncolliderRef:\t\t"                   << colliderRef.value    
                  << "\nboxColliderRef1:\t"                 << boxColliderRef1.value
                  << "\nboxColliderRef2:\t"                 << boxColliderRef2.value
                  << "\nrenderImageRef:\t\t"                << ( &renderImageRef != nullptr ? renderImageRef.value : "nullptr" );
    
        // GetComponents test
        auto allColliders = test.GetComponents< Collider >();
        std::cout << "\n\nThere are (" << allColliders.size() << ") collider components attached to the test GameObject:\n";
        for ( auto && c : allColliders ) {
            std::cout << c->value << '\n';
        }
    
        // RemoveComponent test
        test.RemoveComponent< BoxCollider >();                              // removes boxColliderA
        auto & boxColliderRef3      = test.GetComponent< BoxCollider >();   // now this is the second BoxCollider "BoxCollider_B"
    
        std::cout << "\n\nFirst BoxCollider instance removed\nboxColliderRef3:\t" << boxColliderRef3.value << '\n';
    
    #if MORE_CODE
        // RemoveComponent return test
        int removed = 0;
        while ( test.RemoveComponent< Component >() ) {
            ++removed;
        }
    #else
        // RemoveComponents test
        int removed = test.RemoveComponents< Component >();
    #endif
    
        std::cout << "\nSuccessfully removed (" << removed << ") components from the test GameObject\n";
    
        system( "PAUSE" );
        return 0;
    }
    

    输出

        Added:
        ------
        Component       (1)
        Collider        (1)
        BoxCollider     (2)
        RenderImage     (0)
    
        Values:
        -------
        componentRef:           Component
        colliderRef:            Collider
        boxColliderRef1:        BoxCollider_A
        boxColliderRef2:        BoxCollider_A
        renderImageRef:         nullptr
    
        There are (3) collider components attached to the test GameObject:
        Collider
        BoxCollider_A
        BoxCollider_B
    
    
        First BoxCollider instance removed
        boxColliderRef3:        BoxCollider_B
    
        Successfully removed (3) components from the test GameObject
    

    旁注:授权Unity使用Destroy(object)而不是RemoveComponent,但我的版本现在适合我的需要。

答案 1 :(得分:1)

道歉,如果这不是你想要的,但我有一个想法,使用带有类型索引的无序地图,并在一些元编程和TR2的帮助下,将多个指针放到地图中,包括它的直接基类作为附加键。因此,getComponent<SphereCollider>()getComponent<Collider>()以及向下投射将具有相同的指针。

#include <tr2/type_traits>
#include <tuple>
#include <typeindex>
#include <unordered_map>
#include <iostream>

class Component {
public:
  virtual ~Component() {}
};

class GameObject {
public:
  template <typename T>
  void addComponent(T *component);

  template <typename T>
  T *getComponent();

  std::unordered_map<std::typeindex, Component *> components;
};

template <typename>
struct direct_bases_as_tuple {};

template <typename... Types>
struct direct_bases_as_tuple<std::tr2::__reflection_typelist<Types...>> {
  typedef std::tuple<Types...> type;
};

template <std::size_t N, typename ComponentBases, typename ComponentType>
struct AddComponent {
  GameObject *owner;

  explicit AddComponent(GameObject *owner) : owner(owner) {}

  void operator()(ComponentType *component) {
    AddComponent<N-1, ComponentBases, ComponentType>{owner}(component);

    using BaseType = std::tuple_element<N-1, ComponentBases>::type;

    owner->components[typeid(BaseType)] = component;
  }
};

template <typename ComponentBases, typename ComponentType>
struct AddComponent<0u, ComponentBases, ComponentType> {
  GameObject *owner;

  explicit AddComponent(GameObject *owner) : owner(owner) {}

  void operator()(ComponentType *component) {
    return;
  }
};

template <typename T>
void GameObject::addComponent(T *component) {
  using ComponentBases = direct_bases_as_tuple<std::tr2::direct_bases<ComponentType>::type>::type;

  constexpr classCount = std::tuple_size<ComponentBases>::value;

  AddComponent<classCount, ComponentBases, T>{this}(component);

  components[typeid(T)] = component;
}

template <typename T>
T * GameObject::getComponent() {
  auto iter = components.find(typeid(T));

  if (iter != std::end(components)) {
    return dynamic_cast<T *>(iter->second);
  }

  return nullptr;
}

class Collider : public Component {};
class SphereCollider : public Collider {};

int main() {
  GameObject gameObject;
  gameObject.addComponent(new SphereCollider);

  //get by derived class
  SphereCollider *sphereColliderA = gameObject.getComponent<SphereCollider>();

  //get by subclass
  SphereCollider *sphereColliderB = dynamic_cast<SphereCollider *>(
    gameObject.getComponent<Collider>()
  );

  if (sphereColliderA == sphereColliderB) {
    std::cout << "good" << std::endl;
  }
}

我创建了AddComponent结构,以便在编译时通过组件基类进行递归,并在每次迭代时使用相应的类(键)插入指针(值)。辅助结构direct_bases_as_tuple的灵感来自Andy Prowl's answer,将直接基础更改为元组。我使用GCC 4.9.2使用C ++ 11功能编译了这个。

答案 2 :(得分:0)

我知道这篇文章已经回答了,但是如果您研究游戏编程模式,在本书中他有一个称为Service Locator的设计模式,最后,它说Unity将此模式与Component Pattern一起使用。我希望我可以回答更多具体问题,但这可能是解决此问题的另一种方法。

答案 3 :(得分:0)

Unity引擎与派生的mono运行时链接,在该运行时执行unity脚本。

UnityEngine.Component

public class Component : Object
  {
    .
    .        
    [TypeInferenceRule(TypeInferenceRules.TypeReferencedByFirstArgument)]
    public Component GetComponent(Type type)
    {
        return this.gameObject.GetComponent(type);
    }

    [GeneratedByOldBindingsGenerator]
    [MethodImpl(MethodImplOptions.InternalCall)]
    internal extern void GetComponentFastPath(Type type, IntPtr oneFurtherThanResultValue);

    [SecuritySafeCritical]
    public unsafe T GetComponent<T>()
    {
        CastHelper<T> castHelper = default(CastHelper<T>);
        this.GetComponentFastPath(typeof(T), new IntPtr((void*)(&castHelper.onePointerFurtherThanT)));
        return castHelper.t;
    }
    .
    .
}

C#代码执行本机调用,即对使用C#运行时库API绑定到C#方法的C ++方法的Icalls。无主体(未实现)方法通常需要externabstractpartial说明符,因此所有内部调用都标记为extern。当运行时看到具有[MethodImpl(MethodImplOptions.InternalCall)]属性的方法时,它知道需要进行Icall调用,因此它将查找已绑定到的函数并跳转到该地址。

在C#中,Icall不必为static,它会自动将此组件的MonoObject传递给C ++处理函数。如果它们是static,则通常使用C#shim方法故意将该对象作为参数传递,并将shim方法设为静态Icall。使用Icalls types are not marshalled unless they are blittable types,意味着所有其他类型都以MonoObjectMonoString等形式传递。

通常,C ++方法是函数或静态方法,但我认为它们也可以是非静态方法,只要它们不是虚拟的即可,因为运行时无法固定地址。

UnityEngine.GameObject

public sealed class GameObject : Object
 {  
    .
    .
    public GameObject(string name)
    {
      GameObject.Internal_CreateGameObject(this, name);
    }

    public GameObject()
    {
      GameObject.Internal_CreateGameObject(this, (string) null);
    }

    [WrapperlessIcall]
    [TypeInferenceRule(TypeInferenceRules.TypeReferencedByFirstArgument)]
    [MethodImpl(MethodImplOptions.InternalCall)]
    public extern Component GetComponent(System.Type type);

    [WrapperlessIcall]
    [MethodImpl(MethodImplOptions.InternalCall)]
    private static extern void Internal_CreateGameObject([Writable] GameObject mono, string name);
    .
    .
 }

GameObject的C#构造函数包含对本机方法的调用。构造函数的主体在初始化C#对象之后运行,以便已经有一个this指针。 Internal_CreateGameObject是实际调用的静态shim函数。

某人使用mono的自己的C ++ Internal_CreateGameObject的示例实现:

bool GameObjectBinding::init()
{
    MonoClass *gameObjectClass = Mono::get().getClass("GameObject");
    gameObject_NativeID_Field = mono_class_get_field_from_name(gameObjectClass, "nativeID");

    MonoClass *transformClass = Mono::get().getClass("Transform");
    transform_NativeID_Field = mono_class_get_field_from_name(transformClass, "nativeID");

    mono_add_internal_call("GameEngine_CS.GameObject::internal_createGameObject", GameObjectBinding::createGameObject);
    mono_add_internal_call("GameEngine_CS.GameObject::internal_deleteGameObject", GameObjectBinding::deleteGameObject);
    mono_add_internal_call("GameEngine_CS.GameObject::internal_getGameObject", GameObjectBinding::getGameObject);

    mono_add_internal_call("GameEngine_CS.GameObject::internal_getTransform", GameObjectBinding::getTransform);

    return true;
}

void GameObjectBinding::createGameObject(MonoObject * monoGameObject)
{
    Object *newObject = LevelManager::get().getCurrentLevel()->createObject(0);
    mono_field_set_value(monoGameObject, gameObject_NativeID_Field, (void*)newObject->getID());
}

mono_add_internal_call已用于将此方法绑定到GameObjectBinding::createGameObject,this指针作为MonoObject指针传递到该方法。然后创建一个本机对象来表示GameObject,然后使用mono_field_set_value将C#对象的NativeID字段设置为新本机对象的ID。这样,可以从MonoObject访问本地对象,该GameObject是C#对象的内部实现。 public sealed class GameObject : Object { . . private UInt32 nativeID; public UInt32 id { get { return nativeID; } } . . } 本质上由2个对象表示。

mono_set_dirs( "/Library/Frameworks/Mono.framework/Home/lib", "/Library/Frameworks/Mono.framework/Home/etc" );
mono_config_parse( nullptr );
const char* managedbinarypath = "C:/Test.dll";
MonoDomain* domain = mono_jit_init(managedbinarypath)
MonoAssembly* assembly = mono_domain_assembly_open (domain, managedbinarypath);
MonoImage* image = mono_assembly_get_image (assembly);
MonoClass* gameobjectclass = mono_class_from_name(image, "ManagedLibrary", "GameObject");
gameObject_NativeID_Field = mono_class_get_field_from_name( gameobjectclass, "nativeID" );

此字段在运行时使用绑定

GetComponent<T>()

typeof(T)传递GetComponentFastPathGetComponentFastPath(本地调用),后者也传递组件的this指针。 MonoObject*的本机实现将以MonoReflectionType*mono_reflection_type_get_type()的形式接收该类型。然后,绑定的C ++方法将在MonoReflectionType*上调用MonoType*以获取MonoClass*(以下是原始类型:https://github.com/samneirinck/cemono/blob/master/src/native/inc/mono/mono/metadata/blob.h),对于对象类型,则可以获取{ MonoType*中的{1}}使用mono_class_from_mono_type()。然后,它将获取附加到组件的游戏对象,并在某种内部数据结构中搜索该对象具有的组件。

某人使用mono的自己的C ++ GetComponent的示例实现:

id ModuleScriptImporter::RegisterAPI()
{
    //GAMEOBJECT
    mono_add_internal_call("TheEngine.TheGameObject::CreateNewGameObject", (const void*)CreateGameObject);
    mono_add_internal_call("TheEngine.TheGameObject::AddComponent", (const void*)AddComponent);
    mono_add_internal_call("TheEngine.TheGameObject::GetComponent", (const void*)GetComponent);
}

MonoObject* ModuleScriptImporter::GetComponent(MonoObject * object, MonoReflectionType * type)
{
    return current_script->GetComponent(object, type);
}

MonoObject* CSharpScript::GetComponent(MonoObject* object, MonoReflectionType* type)
{
    if (!CheckMonoObject(object))
    {
        return nullptr;
    }

    if (currentGameObject == nullptr)
    {
        return nullptr;
    }

    MonoType* t = mono_reflection_type_get_type(type);
    std::string name = mono_type_get_name(t);

    const char* comp_name = "";

    if (name == "CulverinEditor.Transform")
    {
        comp_name = "Transform";
    }

    MonoClass* classT = mono_class_from_name(App->importer->iScript->GetCulverinImage(), "CulverinEditor", comp_name);
    if (classT)
    {
        MonoObject* new_object = mono_object_new(CSdomain, classT);
        if (new_object)
        {
            return new_object;
        }
    }
    return nullptr;
}

可以从C ++调用C#方法:

MonoMethodDesc* desc = mono_method_desc_new (const char *name, gboolean include_namespace);
MonoClass* class = mono_class_from_name (MonoImage *image, const char* name_space, const char *name);
MonoMethod* method = mono_method_desc_search_in_class (MonoMethodDesc *desc, MonoClass *klass);
MonoMethod* method = mono_method_desc_search_in_image (MonoMethodDesc *desc, MonoImage *image);
MonoObject* obj = mono_runtime_invoke (MonoMethod *method, void *obj, void **params,
                       MonoObject **exc);

请参阅:https://gamedev.stackexchange.com/questions/115573/how-are-methods-like-awake-start-and-update-called-in-unity/183091#183091