在C ++中从Unity实现组件系统 [英] Implementing Component system from Unity in c++

查看:134
本文介绍了在C ++中从Unity实现组件系统的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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


在此函数的克隆中,我需要满足以下两个条件。 1.我也需要返回任何继承的组件。例如,如果 SphereCollider 继承对撞机,则 GetComponent< Collider>()将返回 SphereCollider 附加到 GameObject ,但 GetComponent< SphereCollider>()不会返回任何<附加了code>对撞机。 2.我需要功能快速。最好使用某种哈希函数。


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

  std :: vector< Component *>组件
模板< typename T>
T * GetComponent()
{
(每个(Component * c在组件中))
如果(dynamic_cast< T>(* c))
return(T * )C;
返回nullptr;
}

但这并不适合第二个要求快速的条件。为此,我知道我可以做这样的事情。

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

但是,这又不符合第一个条件。


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

解决方案

由于我正在编写自己的游戏引擎并采用相同的设计,因此我想与大家分享结果。


概述


我为自己想用作 Components GameObject 实例的c>。通过 #define 减少两个宏的输入量: CLASS_DECLARATION CLASS_DEFINITION


CLASS_DECLARATION 声明唯一的静态const std :: size_t 将用于标识类型( Type )和 virtual 函数,该函数允许对象通过调用同名的父类函数( IsClassType )。


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


std :: hash< std :: string> 是使用的哈希函数,可确保相等的输入产生相等的输出,并且冲突次数接近零




除了哈希冲突的风险较低之外,此实现还具有其他优点,即允许用户创建自己的 Component 使用这些宏的类,而不必引用|扩展枚举类 include 文件> s,或使用 typeid (仅提供运行时类型,不提供父类)。




AddComponent


此自定义RTTI将 Add | Get | RemoveComponent 的调用语法简化为仅指定 template 类型,仅l


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 s,因为如果使用推导的初始化程序列表语法(例如 {10,10,10} ),而无论 Vec3 的构造函数声明。




此自定义RTTI还解决了 std :: unordered_map< std :: typeindex,...> 解决方案:


  1. 即使使用 std :: tr2 :: direct_bases 最终结果仍然是地图中同一指针的重复项。

  2. 用户无法添加等效类型的多个组件,除非使用的映射允许/解决冲突而不覆盖,否则会进一步降低代码的速度。

  3. 没有不确定且缓慢的 dynamic_cast 是必需的,只是直接的 static_cast




GetComponent


GetComponent 仅使用 模板类型的tatic const std :: size_t类型作为虚拟布尔IsClassType 方法并遍历 std :: vector< std :: unique_ptr<组件> > 寻找第一个匹配项。


我还实现了 GetComponents 方法所有所请求类型的组件,再次包括从父类中获取。


请注意,静态成员 Type 可以在有和没有类实例的情况下访问。


还要注意, Type public ,是为每个 Component 声明的派生类,尽管成为POD成员,但大写以强调其灵活使用。




RemoveComponent


最后, RemoveComponent 使用 C ++ 14 的init-capture传递相同的 static const std :: size_t类型 template 的类型到lambda中,因此它基本上可以进行相同的向量遍历,这



在代码中有一些注释b提出了实现更灵活实施的想法,更不用说所有这些版本的 const 版本了。




代码


Classes.h


  #ifndef TEST_CLASSES_H 
#define TEST_CLASSES_H

#include< string>
#include< functional>
#include< vector>
#include< memory>
#include< algorithm>

#定义TO_STRING(x)#x

// ****************
// CLASS_DECLARATION
//
//此宏必须包含在Component的任何子类的声明中。
//声明用于类型检查的变量。
// ****************
#define CLASS_DECLARATION(classname)\
public:\
static const std: :size_t类型; \
虚拟布尔IsClassType(const std :: size_t classType)const覆盖; \

// ****************
// CLASS_DEFINITION
//
//此宏必须包含在类定义中以正确初始化
//用于类型检查的变量。请特别注意确保指示了
//正确的父类,否则运行时类型信息将是
//不正确。仅适用于单继承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)\
返回true; \
返回parentclass :: IsClassType(classType); \
} \

命名空间rtti {

// ***************
/ /组件
//基类
// ***************
类组件{
public:

static const std :: size_t类型;
虚拟布尔IsClassType(const std :: size_t classType)const {
return classType == Type;
}

public:

virtual〜Component()=默认值;
Component(std :: string&& initialValue)
:value(initialValue){
}

public:

std :: string value = uninitialized;
};

// ***************
//对撞机
// ************ ***
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
// ************ ***
类GameObject {
public:

std :: vector< std :: unique_ptr<组件> >组件;

public:

template<类ComponentType,类型名... Args>
void AddComponent(Args& ... params);

模板<类ComponentType>
ComponentType& GetComponent();

模板<类ComponentType>
bool RemoveComponent();

模板<类ComponentType>
std :: vector< ComponentType *> GetComponents();

模板<类ComponentType>
int RemoveComponents();
};

// ***************
// GameObject :: AddComponent
//将所有参数完美地转发到ComponentType构造函数与匹配的参数列表
//调试:请确保将此fn的参数与所需的构造函数进行比较,以避免出现完美转发失败的情况
// EG:推导的初始化列表,仅包含decl静态const int成员,0 | NULL代替nullptr,重载了fn名称和位字段
// ***************
template<类ComponentType,类型名... Args>
void GameObject :: AddComponent(Args& ... params){
components.emplace_back(std :: make_unique< ComponentType>(std :: forward< Args>(params).. 。));
}

// ***************
// GameObject :: GetComponent
//返回第一个组件匹配模板类型
//或派生自模板类型
// EG:如果模板类型为Component,而components [0]类型为BoxCollider
//则为components将返回[0],因为它源自组件
// ***************
template<类ComponentType>
ComponentType& GameObject :: GetComponent(){
for(auto&&&&&&组件:组件){
if(component-> IsClassType(ComponentType :: Type))
return * static_cast< ComponentType *>(component.get());
}

return * std :: unique_ptr< ComponentType>(nullptr);
}

// ***************
// GameObject :: RemoveComponent
//成功返回true删除
//如果组件为空,或者不存在这样的组件,则返回false
// ***************
template<类ComponentType>
bool GameObject :: RemoveComponent(){
if(components.empty())
返回false;

自动& index = std :: find_if(components.begin(),
components.end(),
[classType = ComponentType :: Type](自动& component){
返回component-> ; IsClassType(classType);
});

bool成功=索引!= components.end();

if(成功)
components.erase(index);

成功返回;
}

// ***************
// GameObject :: GetComponents
//返回一个向量遵循与GetComponent
相同的匹配条件的指向所请求组件模板类型的指针//注意:编译器可以选择将elide-elide或move-construct componentOfType复制到此处的返回值
// TODO:传入所需元素的数量(例如:最多7个,或者仅前两个),这将允许std :: array返回值
// //除非需要单独的fn才能获取它们*全部*,如果用户不知道GameObject有多少个此类组件
// TODO:定义一个GetComponentAt< ComponentType,int>(),它可以直接获取所请求的第n个组件输入
// ***************
template<类ComponentType>
std :: vector< ComponentType *> GameObject :: GetComponents(){
std :: vector< ComponentType *> componentsOfType;

for(auto&&&&&& ;; >(component.get()));
}

returncomponentsOfType;
}

// ***************
// GameObject :: RemoveComponents
//返回成功删除,如果未删除则为0
// ***************
template<类ComponentType>
int GameObject :: RemoveComponents(){
if(components.empty())
返回0;

int numRemoved = 0;
bool成功=否;

do {
auto& index = std :: find_if(components.begin(),
components.end(),
[classType = ComponentType :: Type](自动& component){
返回component-> ; IsClassType(classType);
});

成功=索引!= components.end();

if(成功){
components.erase(index);
++ numRemoved;
}
} while(success);

返回numRemoved;
}

} / * rtti * /
#endif / * TEST_CLASSES_H * /




Classes.cpp


  #include Classes.h 

使用命名空间rtti;

const std :: size_t Component :: Type = std :: hash< std :: string>()(TO_STRING(Component));

CLASS_DEFINITION(组件,对撞机)
CLASS_DEFINITION(组件对撞机,BoxCollider)
CLASS_DEFINITION(组件,RenderImage)




main.cpp


  #include< iostream> 
#include Classes.h

#define MORE_CODE 0

int main(int argc,const char * argv){

使用命名空间rtti;

GameObject测试;

// AddComponent测试
test.AddComponent<组件>(组件);
test.AddComponent<撞机>(撞机);
test.AddComponent< BoxCollider>( BoxCollider_A);
test.AddComponent< BoxCollider>( BoxCollider_B);

#if MORE_CODE
test.AddComponent< RenderImage>(渲染图像);
#endif

std :: cout<< "添加:\n ------ \nComponent\t(1)\nCollider\t(1)\nBoxCollider\t(2)\nRenderImage\t(0)\ \n\n;

// GetComponent测试
自动& componentRef = test.GetComponent<组件>();
自动& colliderRef = test.GetComponent<对撞机>();
自动& boxColliderRef1 = test.GetComponent< BoxCollider>();
自动& boxColliderRef2 = test.GetComponent< BoxCollider>(); // boxColliderB == boxColliderA,因为GetComponent仅获得类层次结构中的第一个匹配项
auto& renderImageRef = test.GetComponent< RenderImage>(); //使用更多代码0获取& nullptr

std :: cout<< 值:\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测试
auto allColliders = test.GetComponents<对撞机>();
std :: cout<< \n\n有(<<< allColliders.size()<<<)碰撞机组件连接到测试GameObject上。
for(auto& c:allColliders){
std :: cout<< c->值<< ‘n’;
}

// RemoveComponent测试
test.RemoveComponent< BoxCollider>(); //删除boxColliderA
auto& boxColliderRef3 = test.GetComponent< BoxCollider>(); //现在是第二个BoxCollider BoxCollider_B

std :: cout<< \n\n第一个BoxCollider实例已删除\nboxColliderRef3:\t; << boxColliderRef3.value<< ‘n’;

#if MORE_CODE
// RemoveComponent返回测试
int = 0;
while(test.RemoveComponent< Component>()){
++已移除;
}
#else
// RemoveComponents测试
int删除= test.RemoveComponents<组件>();
#endif

std :: cout<< 从测试GameObject中成功删除( <<<已删除<<<)组件);

系统(暂停);
返回0;
}




输出


 已添加:
------
组件(1)
对撞机(1)
BoxCollider(2)
RenderImage (0)

值:
-------
componentRef:组件
colliderRef:对撞机
boxColliderRef1:BoxCollider_A
boxColliderRef2 :BoxCollider_A
renderImageRef:nullptr

测试游戏对象附带有(3)个碰撞器组件:
碰撞器
BoxCollider_A
BoxCollider_B


第一个BoxCollider实例已删除
boxColliderRef3:BoxCollider_B

成功从测试GameObject中删除了(3)个组件

侧注:授予Unity使用 Destroy(object)而不是 RemoveComponent ,但我的版本暂时满足我的需求。


I've been experimenting with making a component based system similar to Unity's, but in C++. I'm wondering how the GetComponent() method that Unity implements works. It is a very powerful function. Specifically, I want to know what kind of container it uses to store its components.

The two criteria I need in my clone of this function are as follows. 1. I need any inherited components to be returned as well. For example, if SphereCollider inherits Collider, GetComponent<Collider>() will return the SphereCollider attached to the GameObject, but GetComponent<SphereCollider>() will not return any Collider attached. 2. I need the function to be fast. Preferably, it would use some kind of hash function.

For criteria one, I know that I could use something similar to the following implementation

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;
}

But that doesn't fit the second criteria of being fast. For that, I know I could do something like this.

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

But again, that doesn't fit the first criteria.

If anybody knows of some way to combine those two features, even if it's a little slower than the second example, I would be willing to sacrifice a little bit. Thank you!

解决方案

Since I'm writing my own game engine and incorporating the same design, I thought I'd share my results.

Overview

I wrote my own RTTI for the classes I cared to use as Components of my GameObject instances. The amount of typing is reduced by #defineing the two macros: CLASS_DECLARATION and CLASS_DEFINITION

CLASS_DECLARATION declares the unique static const std::size_t that will be used to identify the class type (Type), and a virtual function that allows objects to traverse their class hierarchy by calling their parent-class function of the same name (IsClassType).

CLASS_DEFINITION defines those two things. Namely the Type is initialized to a hash of a stringified version of the class name (using TO_STRING(x) #x), so that Type comparisons are just an int compare and not a string compare.

std::hash<std::string> is the hash function used, which guarantees equal inputs yield equal outputs, and the number of collisions is near-zero.


Aside from the low risk of hash collisions, this implementation has the added benefit of allowing users to create their own Component classes using those macros without ever having to refer to|extend some master include file of enum classs, or use typeid (which only provides the run-time type, not the parent-classes).


AddComponent

This custom RTTI simplifies the call syntax for Add|Get|RemoveComponent to just specifying the template type, just like Unity.

The AddComponent method perfect-forwards a universal reference variadic parameter pack to the user's constructor. So, for example, a user-defined Component-derived class CollisionModel could have the constructor:

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

then later on the user simply calls:

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

Note the explicit construction of the Vec3s because perfect-forwarding can fail to link if using deduced initializer-list syntax like { 10, 10, 10 } regardless of Vec3's constructor declarations.


This custom RTTI also resolves 3 issues with the std::unordered_map<std::typeindex,...> solution:

  1. Even with the hierarchy traversal using std::tr2::direct_bases the end result is still duplicates of the same pointer in the map.
  2. A user can't add multiple Components of equivalent type, unless a map is used that allows/solves collisions without overwriting, which further slows down the code.
  3. No uncertain and slow dynamic_cast is needed, just a straight static_cast.


GetComponent

GetComponent just uses the static const std::size_t Type of the template type as an argument to the virtual bool IsClassType method and iterates over std::vector< std::unique_ptr< Component > > looking for the first match.

I've also implemented a GetComponents method that can get all components of the requested type, again including getting from the parent-class.

Note that the static member Type can be accessed both with and without an instance of the class.

Also note that Type is public, declared for each Component-derived class, ...and capitalized to emphasize its flexible use, despite being a POD member.


RemoveComponent

Lastly, RemoveComponent uses C++14's init-capture to pass that same static const std::size_t Type of the template type into a lambda so it can basically do the same vector traversal, this time getting an iterator to the first matching element.


There are a few comments in the code about ideas for a more flexible implementation, not to mention const versions of all these could also easily be implemented.


The Code

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;
}


Output

    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

Side-note: granted Unity uses Destroy(object) and not RemoveComponent, but my version suits my needs for now.

这篇关于在C ++中从Unity实现组件系统的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆