問題描述
我一直在嘗試制作一個(gè)類似于 Unity 的基于組件的系統(tǒng),但使用的是 C++.我想知道 Unity 實(shí)現(xiàn)的 GetComponent()
方法是如何工作的.這是一個(gè)非常強(qiáng)大的功能.具體來說,我想知道它使用什么樣的容器來存儲(chǔ)其組件.
我在此函數(shù)的克隆中需要的兩個(gè)條件如下.1. 我還需要返回任何繼承的組件.例如,如果SphereCollider
繼承了Collider,GetComponent
將返回附加到GameObject
的SphereCollider
,但 GetComponent
不會(huì)返回任何附加的 Collider
.2.我需要快速的功能.最好是使用某種哈希函數(shù).
對(duì)于標(biāo)準(zhǔn)一,我知道我可以使用類似于以下實(shí)現(xiàn)的東西
std::vector組件模板 T* GetComponent(){對(duì)于每個(gè)(組件中的組件* c)if (dynamic_cast(*c))返回 (T*)c;返回 nullptr;}
但這不符合快速的第二個(gè)標(biāo)準(zhǔn).為此,我知道我可以做這樣的事情.
std::unordered_map組件模板 T* GetComponent(){返回 (T*)components[typeid(T)];}
但同樣,這不符合第一個(gè)標(biāo)準(zhǔn).
如果有人知道結(jié)合這兩個(gè)功能的某種方法,即使它比第二個(gè)示例慢一點(diǎn),我也愿意犧牲一點(diǎn).謝謝!
由于我正在編寫自己的游戲引擎并采用相同的設(shè)計(jì),所以我想我會(huì)分享我的結(jié)果.
概述
我為我想用作GameObject
實(shí)例的Components
的類編寫了自己的RTTI.通過 #define
兩個(gè)宏來減少輸入量:CLASS_DECLARATION
和 CLASS_DEFINITION
CLASS_DECLARATION
聲明了唯一的 static const std::size_t
,用于識(shí)別 class
類型(Type
code>),以及一個(gè) virtual
函數(shù),它允許對(duì)象通過調(diào)用同名的父類函數(shù)來遍歷它們的 class
層次結(jié)構(gòu) (IsClassType
).
CLASS_DEFINITION
定義了這兩件事.即 Type
被初始化為 class
名稱的字符串化版本的散列(使用 TO_STRING(x) #x
),這樣 Type
比較只是一個(gè) int 比較,而不是一個(gè)字符串比較.
std::hash<std::string>
是使用的哈希函數(shù),它保證相等的輸入產(chǎn)生相等的輸出,并且沖突次數(shù)接近于零.
除了散列沖突的低風(fēng)險(xiǎn)之外,這個(gè)實(shí)現(xiàn)還有一個(gè)額外的好處,它允許用戶使用這些宏創(chuàng)建他們自己的Component
類,而無需參考|擴(kuò)展一些主包含
文件,或者使用enum class
s的typeid
(只提供運(yùn)行時(shí)類型,不提供父類).
添加組件
這個(gè)自定義 RTTI 將 Add|Get|RemoveComponent
的調(diào)用語(yǔ)法簡(jiǎn)化為僅指定 template
類型,就像 Unity 一樣.
AddComponent
方法完美地將通用引用可變參數(shù)包轉(zhuǎn)發(fā)到用戶的構(gòu)造函數(shù).因此,例如,用戶定義的 Component
派生的 class CollisionModel
可以具有構(gòu)造函數(shù):
CollisionModel(GameObject * owner, const Vec3 & size, const Vec3 & offset, bool active );
然后用戶只需調(diào)用:
myGameObject.AddComponent(this, Vec3( 10, 10, 10 ), Vec3( 0, 0, 0 ), true );
請(qǐng)注意 Vec3
的顯式構(gòu)造,因?yàn)槿绻褂孟?{ 10, 10, 10 } 這樣推導(dǎo)出的初始化列表語(yǔ)法,完美轉(zhuǎn)發(fā)可能無法鏈接
不管 Vec3
的構(gòu)造函數(shù)聲明.
此自定義 RTTI 還解決了 std::unordered_map<std::typeindex,...>
解決方案的 3 個(gè)問題:
- 即使使用
std::tr2::direct_bases
進(jìn)行層次遍歷,最終結(jié)果仍然是映射中相同指針的重復(fù)項(xiàng). - 用戶不能添加多個(gè)等效類型的組件,除非使用的映射允許/解決沖突而不覆蓋,這會(huì)進(jìn)一步減慢代碼速度.
- 不需要不確定和緩慢的
dynamic_cast
,只需直接的static_cast
.
獲取組件
GetComponent
只是使用 template
類型的 static const std::size_t Type
作為 virtual bool IsClassType 的參數(shù)
方法并迭代 std::vector<;std::unique_ptr<組件>
尋找第一個(gè)匹配項(xiàng).
我還實(shí)現(xiàn)了一個(gè) GetComponents
方法,可以獲取請(qǐng)求類型的所有組件,同樣包括從父類獲取.
請(qǐng)注意,static
成員 Type
可以在有和沒有類實(shí)例的情況下訪問.
另請(qǐng)注意,Type
是 public
,為每個(gè) Component
派生類聲明,...并大寫以強(qiáng)調(diào)其靈活使用,盡管是 POD 會(huì)員.
移除組件
最后,RemoveComponent
使用 C++14
的 init-capture 來傳遞相同的 static const std::size_t Type
template
輸入一個(gè) lambda,所以它基本上可以進(jìn)行相同的向量遍歷,這次是獲取一個(gè) iterator
到第一個(gè)匹配元素.
代碼中有一些關(guān)于更靈活實(shí)現(xiàn)的想法的注釋,更不用說所有這些的 const
版本也可以輕松實(shí)現(xiàn).
代碼
類.h
#ifndef TEST_CLASSES_H#define TEST_CLASSES_H#include <字符串>#include <功能>#include <向量>#include <內(nèi)存>#include <算法>#define TO_STRING( x ) #x//****************//類聲明////這個(gè)宏必須包含在 Component 的任何子類的聲明中.//它聲明了用于類型檢查的變量.//****************#define CLASS_DECLARATION( 類名) 上市: 靜態(tài)常量 std::size_t 類型;virtual bool IsClassType( const std::size_t classType ) const override;//****************//類定義////這個(gè)宏必須包含在類定義中才能正確初始化//用于類型檢查的變量.請(qǐng)?zhí)貏e注意以確保//指示正確的父類或運(yùn)行時(shí)類型信息//不正確.僅適用于單繼承 RTTI.//****************#define CLASS_DEFINITION( parentclass, childclass ) const std::size_t childclass::Type = std::hash<std::string >()( TO_STRING( childclass ) );ool childclass::IsClassType( const std::size_t classType ) const { if ( classType == childclass::Type ) 返回真;返回 parentclass::IsClassType( classType );} 命名空間 rtti {//****************//零件//基類//****************類組件{上市:靜態(tài)常量 std::size_t 類型;virtual bool IsClassType( const std::size_t classType ) const {返回類類型 == 類型;}上市:虛擬 ~Component() = 默認(rèn)值;組件( std::string && initialValue ):值(初始值){}上市:std::string 值 =未初始化";};//****************//碰撞器//****************類碰撞器:公共組件{CLASS_DECLARATION(碰撞器)上市:碰撞器( std::string && initialValue ): 組件( std::move(initialValue ) ) {}};//****************//盒子碰撞器//****************類 BoxCollider : 公共碰撞器 {CLASS_DECLARATION(BoxCollider)上市:BoxCollider( std::string && initialValue ): Collider( std::move(initialValue ) ) {}};//****************//渲染圖像//****************類RenderImage:公共組件{CLASS_DECLARATION(渲染圖像)上市:RenderImage( std::string && initialValue ): 組件( std::move(initialValue ) ) {}};//****************//游戲?qū)ο?/****************類游戲?qū)ο髙上市:std::vector獲取組件();模板<類組件類型 >int RemoveComponents();};//****************//游戲?qū)ο?:添加組件//使用匹配的參數(shù)列表將所有參數(shù)完美轉(zhuǎn)發(fā)到 ComponentType 構(gòu)造函數(shù)//DEBUG: 務(wù)必將這個(gè) fn 的參數(shù)與所需的構(gòu)造函數(shù)進(jìn)行比較,以避免完美轉(zhuǎn)發(fā)失敗的情況//EG:推導(dǎo)的初始化列表、僅聲明的靜態(tài) const int 成員、0|NULL 代替 nullptr、重載的 fn 名稱和位域//****************模板(std::forward(params)...));}//****************//游戲?qū)ο?:獲取組件//返回匹配模板類型的第一個(gè)組件//或者是從模板類型派生的//EG:如果模板類型是Component,components[0]類型是BoxCollider//然后將返回 components[0] 因?yàn)樗缮?Component//****************模板<類組件類型 >組件類型游戲?qū)ο?:GetComponent() {for(自動(dòng)&&組件:組件){if ( 組件-> IsClassType( ComponentType::Type ) )返回 *static_cast<組件類型 * >( component.get() );}返回 *std::unique_ptr<組件類型 >( nullptr );}//****************//游戲?qū)ο?:移除組件//刪除成功返回真//如果組件為空,或者不存在這樣的組件,則返回 false//****************模板<類組件類型 >bool GameObject::RemoveComponent() {如果 ( components.empty() )返回假;汽車&index = std::find_if( components.begin(),組件.end(),[ classType = ComponentType::Type ]( auto & component ) {返回組件-> IsClassType( classType );});bool 成功 = 索引 != components.end();如果(成功)組件.擦除(索引);返回成功;}//****************//游戲?qū)ο?:GetComponents//遵循與 GetComponent 相同的匹配標(biāo)準(zhǔn),返回指向所請(qǐng)求組件模板類型的指針向量//注意:編譯器可以選擇復(fù)制省略或移動(dòng)構(gòu)造 componentsOfType 到這里的返回值中//TODO:傳入所需的元素?cái)?shù)量(例如:最多 7 個(gè),或僅前 2 個(gè)),這將允許 std::array 返回值,//除非用戶不知道 GameObject 有多少這樣的組件,否則需要一個(gè)單獨(dú)的 fn 來獲取它們 *all*//TODO:定義一個(gè)可以直接抓取到請(qǐng)求類型的第n個(gè)組件的GetComponentAt()//****************模板<類組件類型 >std::vector<組件類型 * >游戲?qū)ο?:GetComponents() {std::vector<組件類型 * >組件類型;for(自動(dòng)&&組件:組件){if ( 組件-> IsClassType( ComponentType::Type ) )componentsOfType.emplace_back(static_cast(component.get()));}返回 componentsOfType;}//****************//游戲?qū)ο?:移除組件//返回成功刪除的次數(shù),如果沒有刪除則返回 0//****************模板<類組件類型 >int GameObject::RemoveComponents() {如果 ( components.empty() )返回0;int numRemoved = 0;布爾成功=假;做 {汽車&index = std::find_if( components.begin(),組件.end(),[ classType = ComponentType::Type ]( auto & component ) {返回組件-> IsClassType( classType );});成功 = 索引 != components.end();如果(成功){組件.擦除(索引);++numRemoved;}} while ( 成功 );返回 numRemoved;}}/* rtti */#endif/* TEST_CLASSES_H */
類.cpp
#include "Classes.h";使用命名空間 rtti;const std::size_t Component::Type = std::hash()(TO_STRING(Component));CLASS_DEFINITION(組件,碰撞器)CLASS_DEFINITION(碰撞器,BoxCollider)CLASS_DEFINITION(組件,渲染圖像)
main.cpp
#include #include "Classes.h";#define MORE_CODE 0int main( int argc, const char * argv ) {使用命名空間 rtti;游戲?qū)ο鬁y(cè)試;//添加組件測(cè)試test.AddComponent<組件 >(組件");test.AddComponent<對(duì)撞機(jī)>(對(duì)撞機(jī)");test.AddComponent(渲染圖像");#萬(wàn)一std::cout <<添加:
------
Component (1)
Collider (1)
BoxCollider (2)
RenderImage (0)
";//獲取組件測(cè)試汽車&componentRef = test.GetComponent<組件 >();汽車&colliderRef = test.GetComponent<對(duì)撞機(jī)>();汽車&boxColliderRef1 = test.GetComponent();//使用 MORE_CODE 0 獲取 &nullptrstd::cout <<值:
-------
componentRef: ";<<組件引用值<<
colliderRef: "<<colliderRef.value<<
boxColliderRef1: "<<boxColliderRef1.value<<
boxColliderRef2: "<<boxColliderRef2.value<<
renderImageRef: "<<( &renderImageRef != nullptr ? renderImageRef.value : nullptr");//獲取組件測(cè)試auto allColliders = test.GetComponents<對(duì)撞機(jī)>();std::cout <<"
有 (<< allColliders.size() << ") 碰撞器組件附加到測(cè)試游戲?qū)ο?
";for ( auto && c : allColliders ) {std::cout <<c->值<<'
';}//移除組件測(cè)試test.RemoveComponent();#萬(wàn)一std::cout <<
從測(cè)試游戲?qū)ο笾谐晒h除(<<刪除<<)組件
";系統(tǒng)(暫停");返回0;}
輸出
添加:------組件 (1)對(duì)撞機(jī) (1)BoxCollider (2)渲染圖像 (0)價(jià)值觀:-------componentRef: 組件colliderRef: 碰撞器boxColliderRef1:BoxCollider_AboxColliderRef2:BoxCollider_ArenderImageRef: nullptr有 (3) 個(gè)碰撞器組件附加到測(cè)試游戲?qū)ο?對(duì)撞機(jī)BoxCollider_ABoxCollider_B刪除了第一個(gè) BoxCollider 實(shí)例boxColliderRef3:BoxCollider_B從測(cè)試游戲?qū)ο笾谐晒σ瞥?(3) 個(gè)組件
旁注:Unity 使用 Destroy(object)
而不是 RemoveComponent
,但我的版本現(xiàn)在適合我的需求.>
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 #define
ing 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 class
s, 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 Vec3
s 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:
- Even with the hierarchy traversal using
std::tr2::direct_bases
the end result is still duplicates of the same pointer in the map. - 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.
- No uncertain and slow
dynamic_cast
is needed, just a straightstatic_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:
------
Component (1)
Collider (1)
BoxCollider (2)
RenderImage (0)
";
// 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:
-------
componentRef: " << componentRef.value
<< "
colliderRef: " << colliderRef.value
<< "
boxColliderRef1: " << boxColliderRef1.value
<< "
boxColliderRef2: " << boxColliderRef2.value
<< "
renderImageRef: " << ( &renderImageRef != nullptr ? renderImageRef.value : "nullptr" );
// GetComponents test
auto allColliders = test.GetComponents< Collider >();
std::cout << "
There are (" << allColliders.size() << ") collider components attached to the test GameObject:
";
for ( auto && c : allColliders ) {
std::cout << c->value << '
';
}
// RemoveComponent test
test.RemoveComponent< BoxCollider >(); // removes boxColliderA
auto & boxColliderRef3 = test.GetComponent< BoxCollider >(); // now this is the second BoxCollider "BoxCollider_B"
std::cout << "
First BoxCollider instance removed
boxColliderRef3: " << boxColliderRef3.value << '
';
#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 << "
Successfully removed (" << removed << ") components from the test GameObject
";
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.
這篇關(guān)于在 Unity 中用 C++ 實(shí)現(xiàn)組件系統(tǒng)的文章就介紹到這了,希望我們推薦的答案對(duì)大家有所幫助,也希望大家多多支持html5模板網(wǎng)!