|
以MassSample项目为例,梳理创建Entity并且设置初始Fragments的流程。
一、创建Entity模板
创建Entity时需要一个创建模板类:FMassEntityTemplate。
1、通过UMassEntityConfigAsset创建FMassEntityTemplate类
Template = *MassEntityConfig->GetConfig().GetOrCreateEntityTemplate(*GameState,*MassEntityConfig);
首先是通过GetEntityTemplateInternal从缓存中取FMassEntityTemplate,具体的方式是UMassSpawnerSubsystem中成员变量UMassEntityTemplateRegistry中记录了一个Map,一个唯一id对应一个Template。如果缓存中没有,则首先调用Trait的两个默认函数,向FMassEntityTemplate里添加Fragment、Tag等组件或标签。然后再通过UMassEntityTemplateRegistry初始化Template。
void UMassEntityTemplateRegistry::InitializeEntityTemplate(FMassEntityTemplate& OutTemplate) const
{
// expected to be ensured by the caller
check(!OutTemplate.IsEmpty());
UWorld* World = GetWorld();
// find or create template
UMassEntitySubsystem* EntitySys = UWorld::GetSubsystem<UMassEntitySubsystem>(World);
check(EntitySys);
// Sort anything there is to sort for later comparison purposes
OutTemplate.Sort();
const FMassArchetypeHandle ArchetypeHandle =
EntitySys->CreateArchetype(OutTemplate.GetCompositionDescriptor(), OutTemplate.GetSharedFragmentValues());
OutTemplate.SetArchetype(ArchetypeHandle);
}CreateArchetype:首先从缓存中找有相同Fragment等参数的FMassArchetypeData,如果找不到,则创建一个FMassArchetypeData,并且添加到缓存中。FMassArchetypeData由一组唯一的fragment定义(没有重复),即一个组件的数组,拥有相同组件集合的Entity指向相同的Archetype,Archetype对应一个Chunk数组,Chunk是一个固定大小的混村空间,当一个Chunk空间用光了就多创建一个。FMassArchetypeData的初始化主要是初始化一些用来记录内存结构的变量。
二、创建Entity
通过UMassEntitySubsystem创建。这里涉及到双向添加,一是通过传进来的FMassArchetyoeHandle参数构造FEntityData,并添加到UMassEntitySubsystem的Entities数组中。另外也会把新创建出来的FMassEntityHandle添加到FMassArchetypeData中。
FMassArchetypeData::AddEntityInternal:1、找到可以添加的Chunk,如果有空的Chunk,就把空Chunk通过ChunkFragmentsTemplate(FMassArchetypeData的Fragments类型模板)重置一下,如果有没有填充满的Chunk,则取这个Chunk,并向这个Chunk添加实例。2、添加到EntityMap,EntityMap是的key和value都是int32类型,key值是FMassEntityHandle里的Index,这个Index是UMassEntitySubsystem在创建Entity时赋的值,和Entities的下标是一致的。Value是在所有Chunks里累计的Entity下标。3、把Entity添加到目标Chunk里面。这里向Chunk添加Entity的的方式注意一下,FMassArchetypeChunk的参数有5个:RawMemory一个指向Chunk的大小的指针,AllocSize是Chunk的大小,NumInstances是Chunk包含的Entity的个数,ChunkFragmentData是Chunk包含Fragment的类型。取对应下标的Entity是把RawMemory加上偏移转换成FMassEntityHandle的指针数组,然后取对应下标值。
DestinationChunk->GetEntityArrayElementRef(EntityListOffsetWithinChunk, IndexWithinChunk) = Entity;
// Returns the Entity array element at the specified index
FMassEntityHandle& GetEntityArrayElementRef(int32 ChunkBase, int32 IndexWithinChunk)
{
return ((FMassEntityHandle*)(RawMemory + ChunkBase))[IndexWithinChunk];
}
int32 FMassArchetypeData::AddEntityInternal(FMassEntityHandle Entity, const bool bInitializeFragments)
{
int32 IndexWithinChunk = 0;
int32 AbsoluteIndex = 0;
int32 ChunkIndex = 0;
int32 EmptyChunkIndex = INDEX_NONE;
int32 EmptyAbsoluteIndex = INDEX_NONE;
FMassArchetypeChunk* DestinationChunk = nullptr;
// Check chunks for a free spot (trying to reuse the earlier ones first so later ones might get freed up)
//@TODO: This could be accelerated to include a cached index to the first chunk with free spots or similar
for (FMassArchetypeChunk& Chunk : Chunks)
{
if (Chunk.GetNumInstances() == 0)
{
// Remember first empty chunk but continue looking for a chunk that has space and same group tag
if (EmptyChunkIndex == INDEX_NONE)
{
EmptyChunkIndex = ChunkIndex;
EmptyAbsoluteIndex = AbsoluteIndex;
}
}
else if (Chunk.GetNumInstances() < NumEntitiesPerChunk)
{
IndexWithinChunk = Chunk.GetNumInstances();
AbsoluteIndex += IndexWithinChunk;
Chunk.AddInstance();
DestinationChunk = &Chunk;
break;
}
AbsoluteIndex += NumEntitiesPerChunk;
++ChunkIndex;
}
if (DestinationChunk == nullptr)
{
// Check if it is a recycled chunk
if (EmptyChunkIndex != INDEX_NONE)
{
DestinationChunk = &Chunks[EmptyChunkIndex];
DestinationChunk->Recycle(ChunkFragmentsTemplate);
AbsoluteIndex = EmptyAbsoluteIndex;
}
else
{
DestinationChunk = &Chunks.Emplace_GetRef(GetChunkAllocSize(), ChunkFragmentsTemplate);
}
check(DestinationChunk);
DestinationChunk->AddInstance();
}
// Initialize the fragment memory
if (bInitializeFragments)
{
for (const FMassArchetypeFragmentConfig& FragmentConfig : FragmentConfigs)
{
void* FragmentPtr = FragmentConfig.GetFragmentData(DestinationChunk->GetRawMemory(), IndexWithinChunk);
FragmentConfig.FragmentType->InitializeStruct(FragmentPtr);
}
}
// Add to the table and map
EntityMap.Add(Entity.Index, AbsoluteIndex);
DestinationChunk->GetEntityArrayElementRef(EntityListOffsetWithinChunk, IndexWithinChunk) = Entity;
return AbsoluteIndex;
}三、给Entity设置Fragment
void UMassEntitySubsystem::SetEntityFragmentsValues(FMassEntityHandle Entity, TArrayView<const FInstancedStruct> FragmentInstanceList)
{
CheckIfEntityIsActive(Entity);
const FEntityData& EntityData = Entities[Entity.Index];
EntityData.CurrentArchetype->SetFragmentsData(Entity, FragmentInstanceList);
}从Chunks中取出对应类型的fragment,然后替换
void FMassArchetypeData::SetFragmentsData(const FMassEntityHandle Entity, TArrayView<const FInstancedStruct> FragmentInstances)
{
FMassRawEntityInChunkData InternalIndex = MakeEntityHandle(Entity);
for (const FInstancedStruct& Instance : FragmentInstances)
{
const UScriptStruct* FragmentType = Instance.GetScriptStruct();
check(FragmentType);
const int32 FragmentIndex = FragmentIndexMap.FindChecked(FragmentType);
void* FragmentMemory = GetFragmentData(FragmentIndex, InternalIndex);
// No UE::Mass::Core::bBitwiseRelocateFragments, this isn&#39;t a move fragment
FragmentType->CopyScriptStruct(FragmentMemory, Instance.GetMemory());
}
}
FORCEINLINE FMassRawEntityInChunkData MakeEntityHandle(int32 EntityIndex) const
{
// Entity在所有Chunks里的下标
const int32 AbsoluteIndex = EntityMap.FindChecked(EntityIndex);
// 位于第几个chunk
const int32 ChunkIndex = AbsoluteIndex / NumEntitiesPerChunk;
// Entity所在Chunk的内存,Entity所在Chunk的下标
return FMassRawEntityInChunkData(Chunks[ChunkIndex].GetRawMemory(), AbsoluteIndex % NumEntitiesPerChunk);
}
FORCEINLINE void* GetFragmentData(const int32 FragmentIndex, const FMassRawEntityInChunkData EntityIndex) const
{
return FragmentConfigs[FragmentIndex].GetFragmentData(EntityIndex.ChunkRawMemory, EntityIndex.IndexWithinChunk);
}
void* GetFragmentData(uint8* ChunkBase, int32 IndexWithinChunk) const
{
// 所在Chunk的首地址+在chunk的偏移+归属Entity的Index*Fragment的大小
return ChunkBase + ArrayOffsetWithinChunk + (IndexWithinChunk * FragmentType->GetStructureSize());
} |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|