可可西

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

 

 

 

posted on 2019-04-04 18:07  可可西  阅读(958)  评论(0编辑  收藏  举报

导航