UE3中Object和Actor的创建与销毁
创建Object
① 在uc脚本中使用new运算符来创建
/********************************************************************************** outer : The value for the new object's Outer variable. The default value is None, which means the object is created within the "transient package", a virtual package for runtime objects. InName : The name for the new object. flags : Specifies object flags for the new object. class : The class to create an object of. No abstract object & No Actor. **********************************************************************************/ new ( optional Object outer=none , optional String ObjName="" , optional int flags=0 ) class(template);
注1:outer记录当前object的来源,当前object对象会引用着outer对象;当前object对象也会被放置在outer对象chain的下一级中(在加载卸载map或level时,会通过outer chain来决定销毁那些object对象)
注2:new的实现在c++的UObject::execNew函数中
一些示例:
local TObject1 O1, O2, O3, O4; O1 = new class'TObject1'; O2 = new(self) class'TObject1'; // O2.Outer=self O3 = new(none, "TObject1_xxx") class'TObject1'; // O3.Name="TObject1_xxx" O4 = new(self, "", 0) class'TObject1';
② 在c++中使用StaticConstructObject函数来创建
/** * Create a new instance of an object. The returned object will be fully initialized. If InFlags contains RF_NeedsLoad (indicating that the object still needs to load its object data from disk), components * are not instanced (this will instead occur in PostLoad()). The different between StaticConstructObject and StaticAllocateObject is that StaticConstructObject will also call the class constructor on the object * and instance any components. * * @param Class the class of the object to create * @param InOuter the object to create this object within (the Outer property for the new object will be set to the value specified here). * @param Name the name to give the new object. If no value (NAME_None) is specified, the object will be given a unique name in the form of ClassName_#. * @param SetFlags the ObjectFlags to assign to the new object. some flags can affect the behavior of constructing the object. * @param Template if specified, the property values from this object will be copied to the new object, and the new object's ObjectArchetype value will be set to this object. * If NULL, the class default object is used instead. * @param Error the output device to use for logging errors * @param SubobjectRoot * Only used to when duplicating or instancing objects; in a nested subobject chain, corresponds to the first object that is not a subobject. * A value of INVALID_OBJECT for this parameter indicates that we are calling StaticConstructObject to duplicate or instance a non-subobject (which will be the subobject root for any subobjects of the new object) * A value of NULL indicates that we are not instancing or duplicating an object. * @param InstanceGraph * contains the mappings of instanced objects and components to their templates * * @return a pointer to a fully initialized object of the specified class. */ UObject* UObject::StaticConstructObject ( UClass* InClass, UObject* InOuter /*=GetTransientPackage()*/, FName InName /*=NAME_None*/, EObjectFlags InFlags /*=0*/, UObject* InTemplate /*=NULL*/, FOutputDevice* Error /*=GError*/, UObject* SubobjectRoot /*=NULL*/, FObjectInstancingGraph* InInstanceGraph /*=NULL*/ ) { // ... ... // Allocate the object. UObject* Result = StaticAllocateObject( InClass, InOuter, InName, InFlags, InTemplate, Error, NULL, SubobjectRoot, InstanceGraph ); if( Result ) { { // call the base UObject class constructor if the class is misaligned (i.e. a native class that is currently being recompiled) // 调用UObject::InternalConstructor,里面会使用placement new来构造UObject,使得其能调用UObject()无参构造函数来初始化 if ( !InClass->IsMisaligned() ) { (*InClass->ClassConstructor)( Result ); } else { (*UObject::StaticClass()->ClassConstructor)( Result ); } } // ... ... } // ... ... return Result; }
销毁Object
GC系统检查当前Object对象是否被Root类型Object对象直接或间接引用,如果没有则会销毁该Object
当某个Root类型Object不再使用某个Object对象时,应及时将其设置成null,来防止因为被引用无法及时被GC回收
注1:在UWorld::Tick中GC系统会按照一定的时间间隔调用UWorld::PerformGarbageCollection()来开始查找并标记那些对象是垃圾(GCMark),被标记的的object对象会触发BeginDestroy()调用
时间间隔变量TimeBetweenPurgingPendingKillObjects可以在*Engine.ini的[Engine.Engine]标签中配置,缺省配置为60秒
注2:在Tick中调用UObject::IncrementalPurgeGarbage(TRUE)分帧来回收垃圾(GCSweep),在delete Object前还会触发FinishDestroy()调用
由于Object是个对象,delete Object等价于:
Object->~Object(); // ~Object是个虚析构函数,不同Object类型对象会先调用自己的析构函数,然后依次调父类的
operator delete(Object);
由于UObject实现了void operator delete( void* Object, size_t Size )成员函数,最后会调用UObject中的delete操作符重载函数
为了减少内存碎片和减少分配和回收内存开销,UE自己对UObject进行了内存管理,delete实际上不会真正地释放内存
注3:也可以通过执行obj gc来触发UObject::CollectGarbage( GARBAGE_COLLECTION_KEEPFLAGS )调用,来GCMark并GCSweep垃圾
注4:编辑器的GC引用分析逻辑在void FArchiveTagUsedNonRecursive::PerformReachabilityAnalysis( EObjectFlags KeepFlags )
游戏的在void FArchiveRealtimeGC::PerformReachabilityAnalysis( EObjectFlags KeepFlags )中
创建Actor
① 在uc脚本中使用Spawn函数来创建
' Spawn an actor. Returns an actor of the specified class, not of class Actor (this is hardcoded in the compiler).
' Returns None if the actor could not be spawned (if that happens, there will be a log warning indicating why) Defaults to spawning at the spawner's location. ' ' @note: ActorTemplate is sent for replicated actors and therefore its properties will also be applied at initial creation on the client.
' However, because of this, ActorTemplate must be a static resource (an actor archetype, default object, or a bStatic/bNoDelete actor in a level package) or the spawned Actor cannot be replicated native noexport final function coerce actor Spawn ( class<actor> SpawnClass, optional actor SpawnOwner, optional name SpawnTag, optional vector SpawnLocation, optional rotator SpawnRotation, optional Actor ActorTemplate, optional bool bNoCollisionFail );
一些示例:
local UTBot NewBot; local Pawn NewPawn; local class<HUD> NewHUDType; local HUD NewHUD; NewBot = Spawn(class'UTBot'); NewPawn = Spawn(class'UTPawn',,,vect(10.0,20.0,0.0),rot(1234, 5678 , 9012)); NewHUDType = class'UTEntryHUD'; NewHUD = Spawn(NewHUDType, self); //self为当前controller对象
注:Spawn的实现在c++的AActor::execSpawn函数中
② 在cpp中使用SpawnActor函数来创建
// Create a new actor. Returns the new actor, or NULL if failure. AActor* UWorld::SpawnActor ( UClass* Class, FName InName=NAME_None, const FVector& Location=FVector(0,0,0), const FRotator& Rotation=FRotator(0,0,0), AActor* Template=NULL, UBOOL bNoCollisionFail=0, UBOOL bRemoteOwned=0, AActor* Owner=NULL, APawn* Instigator=NULL, UBOOL bNoFail=0 ) { // 游戏世界WorldInfo是否创建完毕 const UBOOL bBegunPlay = HasBegunPlay(); FVector NewLocation = Location; // 根据Actor类型的碰撞信息和碰撞体大小调整Actor的位置 if( (Template->bCollideWorld || (Template->bCollideWhenPlacing && (GetNetMode() != NM_Client))) && !bNoCollisionFail ) { FindSpot(Template->GetCylinderExtent(), NewLocation, Template->bCollideComplex); } // 创建Actor ULevel* LevelToSpawnIn = Owner ? CastChecked<ULevel>(Owner->GetOuter()) : CurrentLevel; AActor* Actor = ConstructObject<AActor>( Class, LevelToSpawnIn, InName, RF_Transactional, Template ); // 添加Actor到关卡中 LevelToSpawnIn->Actors.AddItem( Actor ); // 添加Actor到关卡的Tick列表中 if (Actor->WantsTick()) { LevelToSpawnIn->TickableActors.AddItem(Actor); } Actor->bTicked = !Ticked; Actor->CreationTime = GetTimeSeconds(); Actor->WorldInfo = GetWorldInfo(); // Set the actor's location and rotation. Actor->Location = NewLocation; Actor->Rotation = Rotation; // Initialize the actor's components. Actor->ConditionalForceUpdateComponents(FALSE,FALSE); // init actor's physics volume Actor->PhysicsVolume = GetWorldInfo()->PhysicsVolume; // Set owner. Actor->SetOwner( Owner ); // Set instigator Actor->Instigator = Instigator; if (bBegunPlay) { Actor->InitRBPhys(); } Actor->InitExecution(); Actor->Spawned(); if(bBegunPlay) { Actor->PreBeginPlay(); { eventPreBeginPlay(); // 调用uc脚本的event PreBeginPlay()事件回调函数 // ... ... } for(INT ComponentIndex = 0;ComponentIndex < Actor->Components.Num();ComponentIndex++) { if(Actor->Components(ComponentIndex)) { Actor->Components(ComponentIndex)->ConditionalBeginPlay(); } } } // Check for encroachment. if( !bNoCollisionFail ) { CheckEncroachment( Actor, Actor->Location, Actor->Rotation, 1 ); } else if ( Actor->bCollideActors ) { Actor->FindTouchingActors(); } if(bBegunPlay) { Actor->PostBeginPlay(); { // Send PostBeginPlay. eventPostBeginPlay();// 调用uc脚本的event PostBeginPlay()事件回调函数 // Init scripting. eventSetInitialState(); // 调用uc脚本的event SetInitialState()函数 // Find Base if( !Base && bCollideWorld && bShouldBaseAtStartup && ((Physics == PHYS_None) || (Physics == PHYS_Rotating)) ) { FindBase(); } } } if( InTick ) { NewlySpawned.AddItem( Actor ); } return Actor; }
销毁Actor
Actor本质还是一个Object,其回收是通过GC完成的,显示调用uc的Destory和cpp中的DestroyActor只是去除Actor对象的所有引用,让其能被GC视为垃圾
① 在uc脚本中使用Destory函数来销毁
'Destroy this actor. Returns true if destroyed, false if indestructible. 'Destruction is latent. It occurs at the end of the tick. native(279) final noexport function k2call bool Destroy();
② 在cpp中使用DestroyActor函数来销毁
/** * Removes the actor from its level's actor list and generally cleans up the engine's internal state. * What this function does not do, but is handled via garbage collection instead, is remove references * to this actor from all other actors, and kill the actor's resources. This function is set up so that * no problems occur even if the actor is being destroyed inside its recursion stack. * * @param ThisActor Actor to remove. * @param bNetForce [opt] Ignored unless called during play. Default is FALSE. * @param bShouldModifyLevel [opt] If TRUE, Modify() the level before removing the actor. Default is TRUE. * @return TRUE if destroy, FALSE if actor couldn't be destroyed. */ UBOOL UWorld::DestroyActor( AActor* ThisActor, UBOOL bNetForce=FALSE, UBOOL bShouldModifyLevel=TRUE ) { check(ThisActor); check(ThisActor->IsValid()); ThisActor->bPendingDelete = true; // Terminate any physics engine stuff for this actor right away. ThisActor->TermRBPhys(NULL); // Tell this actor it's about to be destroyed. ThisActor->eventDestroyed();// 调用uc脚本的event Destroyed()事件回调函数 ThisActor->PostScriptDestroyed(); // Remove from base. if( ThisActor->Base ) { ThisActor->SetBase( NULL ); } // Make a copy of the array, as calling SetBase might change the contents of the array. TArray<AActor*> AttachedCopy = ThisActor->Attached; for( INT AttachmentIndex=0; AttachmentIndex < AttachedCopy.Num(); AttachmentIndex++ ) { AActor* AttachedActor = AttachedCopy(AttachmentIndex); if( AttachedActor && AttachedActor->Base == ThisActor && !AttachedActor->bDeleteMe ) { AttachedActor->SetBase( NULL ); } } // Then empty the array. ThisActor->Attached.Empty(); // Clean up all touching actors. INT iTemp = 0; for ( INT i=0; i<ThisActor->Touching.Num(); i++ ) { if ( ThisActor->Touching(i) && ThisActor->Touching(i)->Touching.FindItem(ThisActor, iTemp) ) { ThisActor->EndTouch( ThisActor->Touching(i), 1 ); i--; } } // If this actor has an owner, notify it that it has lost a child. if( ThisActor->Owner ) { ThisActor->SetOwner(NULL); } // Notify net players that this guy has been destroyed. if( NetDriver ) { NetDriver->NotifyActorDestroyed( ThisActor ); } // Remove the actor from the actor list. RemoveActor( ThisActor, bShouldModifyLevel ); // Mark the actor and its direct components as pending kill. ThisActor->bDeleteMe = 1; ThisActor->MarkPackageDirty(); ThisActor->MarkComponentsAsPendingKill( TRUE ); // Clean up the actor's components. ThisActor->ClearComponents(); // Return success. return TRUE; }