There is no spoon

Legacy:Creating Actors And Objects

From Unreal Wiki, The Unreal Engine Documentation Site
Jump to: navigation, search

New Keyword[edit]

The New keyword is used to create new objects which are not a subclass of Actor.

The parameters are the owner object, the new object's name and the object's flags. Being one of the UnrealScript keywords rather than a function, there is no definition for New, but here's what it would look like if there were:

Object '''New'''(
  optional Object ''NewOuter'',
  optional string ''NewName'',
  optional int ''NewFlags''
  ) Class ''NewClass''
NewOuter 
An object which will be assigned to the new object's Outer property. Self if omitted. If you are creating a new object that is declared Within another class, the new outer must be an object of that class or one of its subclasses.
NewName 
The new object's name. In UT this seems to be a parameter of type Name, while in UT2003/4 this parameter is of type String. This allows you to dynamically create it, eg "MyObject"$i – you can do this sort of thing in UT, but it's more complicated.
NewFlags 
The new object's ObjectFlags. Only the RF_anything constants from the Object class and combinations of them are valid values, but you shouldn't use them unless you know what you're doing.
NewClass 
The new object's class.
return value 
Like the Spawn() method described below New returns a reference to an object of the specified class.

Examples:

local Object NewObject;
 
NewObject = New class'MyObjectClass';
NewObject = New() class'MyObjectClass';
NewObject = New(None) class'MyObjectClass';
NewObject = New(SomeOtherObject, "SomeWeirdObjectName") class'MyObjectClass';
NewObject = New(None, '', 0) class'MyObjectClass';

When non-actor objects are created in UT2003 then the object's Created function is called.
Creating Actor objects with the New keyword causes the game or UCC commandlets to crash.

When dealing with non-Actor objects you must be absolutely sure to clean up Actor references in any objects, generally by explicitly setting those references to None where appropriate. Good places for this would be right after calling the Actor's Destroy() method or in the Actor's Destroyed() event. Failing to do so can effectively prevent the entire level from being garbage-collected at map change, resulting it two levels existing in memory at the same time, which generally crashes the game.

Newer engine version (i.e. UE3), however, detect this condition and crash immediately, logging useful information, including complete reference chain. You may need to launch the game with -log switch, though, as file logs tend to be not properly flushed on crash.

Object references in Actors are no problem, though.

Foxpaw: Though you are correct that this causes problems, actors don't get garbage collected, and there is usually at least two levels loaded at any given time. (The current level you're playing, and the "entry level.") I believe that the problem arises because the object (which doesn't exist within the context of any level) doesn't get garbage collected because it's referencing a valid actor, then the reference becomes invalid due to the destruction of the actor, but the bit of code that checks for references to actors whenever an actor is destroyed doesn't get called on non-actors, thus you have an invalid reference that has not been changed to None. This points to a place in memory that is either something different or (usually) the middle of something, which, when the engine tries to do anything with it, causes a general protection fault because it's trying to read an object's data mid-object and getting information that appears to be in an invalid format.

Wormbo: Actors do get garbage-collected at level change. If the current level can't be garbage collected you will effectively have three levels in memory and if the problem persists eventually there will be too much stuff in memory to continue and the game will crash with an out-off-memory message. The changes I did to this page are all based on quite detailed information given by Evolution on IRC in ETG #unrealscript.

Foxpaw: I'm fairly certain that actors don't get garbage collected - I think that the game just walks it's actor list and Destroy()s each one. I'm pretty sure that they don't suffer normal garbage collection. Unfortunately, when I tried to set up some classes to test it conclusively, (create a reference from an object to an actor, change levels, see if actor still exists) turned out trickier than I had expected. UT destroyed the actor, then found that there was no valid references from the object, and garbage collected it. One might be able to get around that by having a reference to the object from something that won't get destroyed on level change, IE the GUI or the Player.

Wormbo: Nothing gets Destroy()ed on level change, otherwise the Destroyed() event would trigger. (That's why it's so hard to clean up stuff on map change.) The whole level is just garbage-collected and the now unused packages are unloaded from memory.

Evolution: Actors do get garbage collected, but usually not immediately. When unrealscript calls the Destroy() function on an actor, the actor's Destroyed() event is triggered, the engine sets the Actor's bDeleteMe property to True, adds the actor to a list of actors that are pending deletion, and removes the Actor from the level's "live" Actor list [which is represented by the AllActors iterator]). Once the number of Actors pending deletion reaches a certain number, a "mini" garbage collection occurs, which actually deletes those actors. When the level changes, any actors on the "pending deletion" list are deleted, even if there are less than the required number. When this occurs, any "live" actors still in the level will not receive the Destroyed() event, with the exception of PlayerController and Pawn actors.

There are actually two different problems which can result from dangling actor references in non-Actor objects. The first problem occurs when an actor which has been destroyed is still referenced by an object which is rooted ("rooted" just means that the object is linked to some other object which remains persistent throughout level changes, such as the GUIController or Player, through a chain of object references). Since rooted objects (including actors) cannot be garbage collected, they will remain in memory, and as a result, so will the level object itself, along with any objects it's referencing, so on and so on. This leads to more than one (excluding Entry) level existing in memory at the same time, which typically results what most C++ books would call "indeterminate behavior", or in layman's terms - "eventually (but usually immediately) crashes the program".

The second problem occurs when you fail to explicitly set the Actor reference to None before unrooting the object (i.e. ObjectA is the only object referencing ObjectB, which is the only object (besides the level actor) referencing ActorA. ObjectA then sets the var pointing at ObjectB to None, but ObjectB has not set its ActorA reference to None). Assume that ActorA destroyed itself at some point after this. When garbage collection occurs at level change, since both ObjectB and ActorA are considered unrooted, they are allowed to be deleted. If ActorA is deleted prior to ObjectB, then ObjectB's reference to ActorA would then be pointing at deleted memory. When the GC process reaches ObjectB, it recursively iterates through ObjectB's object references to determine whether ObjectB is unrooted, and throws an access violation when it attempts to read the memory still being pointed to by ObjectB's ActorA reference. If you are familiar with C++ programming, it's probably very obvious what the problem is there.

An important caveat of Actor references in UT2003/UT2004 is that they will return None if they are marked bDeleteMe, even if they haven't actually been deleted yet. This is the reason I set the MyGlobalActor reference to None even if the 'if ( MyGlobalActor != None )' check returns false, in the code I posted.

I hope this helps clear up exactly where the danger lies in not correctly cleaning up Actor references in your Objects. It's very important to fully understand how to correctly clean up Actor references if your mod has any interaction between Actors and Objects.

A good rule of thumb is to NEVER have a global actor variable in a non-Actor subclass and when you must, make it a private variable, and provide accessor functions like Get() or something, so that you completely control the actual reference.

Evolution

Here is an example of how to safely use an actor in a non-Actor subclass:

class MyObjectThing extends Object;
 
// assume MyActor is a subclass of Actor that is in your mod
var private MyActor MyGlobalActor;
 
// this will indicate when MyObjectThing should not be allowed to create new actors
var private bool bLocked;
 
function MyActor GetMyActor( optional Actor A )
{
    if ( !bLocked && MyGlobalActor == None && A != None )
        MyGlobalActor = A.Spawn(class'MyActor');
 
    return MyGlobalActor;
}
 
// Call this function when you're no longer going to be using this object, 
// i.e. you are releasing your reference to this object
// e.g. if this was a GUIComponent, you would call this function when the page is closed
function Shutdown()
{
    if ( MyGlobalActor != None )
        MyGlobalActor.Destroy();
 
    MyGlobalActor = None;
    bLocked = True;
}
 
function DoSomeStuff()
{
    local bool bExample;
    local MyActor MyLocalActor;
 
    // It's good practice to avoid referencing MyGlobalActor directly, even from within the same class
    MyLocalActor = GetMyActor();
    bExample = MyLocalActor.IsAnExample();
}

Foxpaw: Have you tested the above? I'm fairly certain that you can't call Spawn from something that isn't subclassed from actor. What with Spawn being defined in Actor and all. Plus the actor wouldn't have a reference to a level, because it goes into the level that the actor that spawned it was in.

Evolution: Hehe, ya got me. Yes, you'd need to have a reference to an actor in order to call the Spawn() function. I've changed the above code snippet to represent this. Thanks for pointing that out.

porkmanii: What of WeaponFire objects in Unreal Engine 2.5? I have had the game crash as a result of a Weapon referencing the WeaponFire objects/fire-modes of a Weapon which has been destroyed. Does this mean Weapons delete their WeaponFire objects when Destroyed, even if other Actors are still referencing them?

Spawn() Method[edit]

The Spawn function is used to create new actors. Unlike the New keyword, Spawn can only be called from a non-static function and can only create actors. If you need to call the Spawn function from a non-actor object make sure it has access to an Actor whose Spawn function can be used then.

native(278) final function actor Spawn
(
   class<actor>     SpawnClass,
   optional actor   SpawnOwner,
   optional name    SpawnTag,
   optional vector  SpawnLocation,
   optional rotator SpawnRotation
);

If SpawnLocation and/or SpawnRotation is not specified the spawner's Location and Rotation is used. (The spawner is the actor Spawn is called from.)

Spawn returns an object of the class specified in SpawnClass, not an object of class Actor, i.e. you don't have to cast it to the class you need. This is hardcoded in the compiler. For example:

local Projectile P;
P = Spawn(ProjectileClass, Instigator,,, Instigator.ViewRotation);

You don't have to use P = Projectile(Spawn(ProjectileClass)), in fact this will cause an error when compiling.

Warning: Spawning an actor for an owner whose deletion is pending reliably crashes the game with a "Unknown code token" error. If in doubt, make sure you check the new owner's bDeleteMe property first.

Spawn also allows you to set the Tag on the new actor. If you need to set other properties on the new actor, you'll have to either call its accessors or set them directly once it's created. For example:

local MyClass P;
P = Spawn(MyClass);
P.SetMeaning(42);
P.bHasMeaning = True;

On the other hand, if your goal is simply to have certain properties set before anything else happens to the object, you can use default properties in a sort of two part constructor:

// In the thing to be spawned..
static function PreInitialize( string SomeVar, int SomeNumber )
{
  // You can preserve the original default values in other default variables if you want, and restore them in prebeginplay. If you want.
  default.SomeOtherVar = SomeVar;
  default.SomeOtherNumber = SomeNumber;
}
 
// In the spawner..
class'Whatever'.static.PreInitialize( "Cats are soft.", 5 );
Spawn( class'Whatever' );

Another, probably less elegant-looking method would be exploiting the Chain of Events when Spawning Actors:

class TheSpawningClass extends SomeClass< SEMI >
 
event GainedChild(Actor Other)
{
  if ( Other.Class == class'WhatEver' ) {
    // this is executed before the newly spawned actor has a chance to execute its own PreBeginPlay() event
  }
}
 
function SpawnMyChild()
{
  Spawn(class'WhatEver', Self);
}

Related Topics[edit]


hc: Is there a way to get spawn create actors that don't fit in the current location? I have to spawn some big objects and move them out of the way, but spawn fails when there's not enough room.. My current workaround is to set the default collision properties to zero and have the object change them to something appropriate after it has been spawned, but that's an ugly ugly hack..