The three virtues of a programmer: Laziness, Impatience, and Hubris. – Larry Wall

Legacy:Typecasting

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

What is Typecasting?[edit]

For an overview of typecasting, please read the indroduction to Typecasting located at Using Objects.

Syntax:

This is the basic typecasting syntax:

desiredType ( expression )

Often you typecast objects to access certain properties, so here are two more specialized explainations for that case:

ClassToTypecastTo ( ObjectToTypecast ) . PropertyToAccess
ClassToTypecastTo ( ThingToGetObjectFrom . ObjectToTypecast ) . PropertyToAccess

Why Do I Need That?[edit]

Example 1: Owners[edit]

Imagine you have a weapon class and you need to access properties or functions of its owner (a Pawn class). The most obvious way is to use the weapon's Owner property, but there's a small problem with that:

The weapon has inherited the Owner property from the Actor class, and it's defined there using this line:

var const Actor Owner;   // Owner actor.

That means no matter what you specified as the actor's owner, the compiler will always think of it as an object of class Actor. If you want to access the Health property of the player holding the weapon, the compiler will tell you that there isn't a Health property in the actor class. This happens quite often and therefore UnrealScript provides a way around these errors:

local Pawn MyPawn; 
MyPawn = Pawn(Owner); 
log(MyPawn.Health);

This logs the health of the pawn owning this actor. We've typecast the Owner (an actor) into a Pawn, and we can then access a property of the Pawn class. It works most of the time, but sometimes Owner can be None or at least not a Pawn. In that case an Accessed None error is logged at runtime. Accessing None can have unpredictable effects, so it should be avoided by adding some extra code:

local Pawn MyPawn;
MyPawn = Pawn(Owner);
if ( MyPawn != None )
  log(MyPawn.Health);
else
  log("Owner is not a Pawn!");

You can also do the same thing without an additional variable:

if ( Pawn(Owner) != None )
  log(Pawn(Owner).Health);
else
  log("Owner is not a Pawn!");

Example 2[edit]

In a gametype you want to send a message to a player when he kills someone.

PlayerController.ClientMessage("message");

will do fine for that, but the Killed function of GameInfo doesn't give PlayerController, but Controller. So you have to typecast:

PlayerController(Killed).ClientMessage("Message");

would work fine, but what if the Controller isn't a PlayerController, but a AIController for example, then nothing would happen, except that it shows an "Accessed None" error in the log. That's not so good. You can use the IsA function to stop that.

if (Killer.IsA('PlayerController'))
{
    PlayerController(Killer).ClientMessage("Message");
}

Example 3: Typecasting to a Custom Pawn[edit]

If you have a custom controller and a custom pawn and you wish you access properties on your custom pawn, a natural way to do it would be like this:

CustomController(Controller).CustomPawn(Controller.Pawn).Variable;

That however, will not work. Instead, simplify things. You don't need to typecast the controller because you aren't really accessing anything from your custom controller. You are only accessing a variable in your custom pawn. Therefore this should work:

CustomPawn(Controller.Pawn).Variable; //Accesses the variable of your custom pawn

If for some reason you need to typecast your custom controller also, you can use this:

CustomPawn( CustomController(Controller).Pawn ).Variable; //Accesses the variable of your custom controller's custom pawn

Typecasting the custom controller won't really be neccesary in this case as the controller has a direct reference to it's controlled pawn so no matter what type of Controller you are, the previous example will usually work.

So....?[edit]

You can also typecast classes to other classes:

var class<actor> ActorType;
ActorType = class'Foo';
class<Foo>(ActorType).Default.Bar = 0;

A lot of casting is automatic (e.g. int -> float, byte -> int) and other types can be converted using regular casting syntax (e.g. vector -> rotator, numbers/names/objects -> string), but there are also some types which need special treatment.
(Some of these conversions might not be very useful.)

Conversions[edit]

Byte/Int/Float...[edit]

to String[edit]

Numbers are converted to their actual string representation. Float values always have six digits after the decimal point in UT and two in UT2003. (They are rounded.)

to Boolean[edit]

Zero (or first item of the enumeration) results in False, anything else is True.

to Enum[edit]

The following appears to work. No idea what happens if your int or float is out of the bounds of the enum.

EnumVar = EMyEnum( myInt )

Enum...[edit]

Note that for typecasting enums you always have to give an enum property or a fully qualified enum value, e.g. MyActor.Physics or Level.ENetMode.NM_ListenServer.

to String[edit]

When typecasting enum properties, the enum items are represented by their corresponing number. (first item = 0, second item = 1,...; e.g. PHYS_Falling = 2, ROLE_Authority = 4) You need to convert the enum item to Name and cast that to String if you want a string representation. (see below)

to Name[edit]

To get the name of an enum item you have to use the GetEnum function like this:

local name EnumName;
EnumName = GetEnum(enum'ENetRole', 2);
log(EnumName); // logs 'ROLE_SimulatedProxy'

to Boolean[edit]

The first enum item results in False, anything else in True.

to Byte/Int[edit]

This conversion happens automatically in assignments.

Boolean...[edit]

to Byte/Int/Float[edit]

True results in 1, False results in 0.

to String[edit]

You get either "True" or "False".

Keep in mind this is a special case variable. The difference is that it is localized, stored in Core.<localized>, where <localized> can be int, det, itt etc. (see Localization for some info).

This is how the Engine knows what and how to use it:

[General]
True=True
False=False

Therefore, a code like this:

function StatusCheck ()
{
    local bool Boolean;
 
    Boolean = True;
    CheckMe(string(Boolean));
}
 
function CheckMe (string Check)
{
    if (Check == "True")
        Log("You're right, it's true...");
    else
        Log("Sorry... the argument is false");
}

... is very unreliable. The reason is, that on a frt localized install (French language), "True" would correspond to "Vrai", on a det install (German) it would correspond to "Wahr" etc. The best (and most secure) way is to use byte typecasting.

Name...[edit]

to String[edit]

The name is converted to the string used in the source code. Since names are not case-sensitive the first appearance of the name within the complete source is used. (This might be an answer to the question why the name of the LocationID actor is completely lowercase while its script says "LocationID".)

Object...[edit]

"Object" can be any Actor, Object, class, Texture, mesh, Sound, etc.

to String[edit]

For classes, textures, meshes, sounds and some other objects the string usually looks like "Package.ObjectName". Actors are usually represented by "MapName.ActorClass<number>", but in UT2003 objects created at runtime (i.e. not in the editor) don't get a number added to their name by default. In general objects are represented as "RootObject.FirstChild.(...).ThisObject". Examples for this are UWindow objects in UT and objects embedded in the default properties of UT2003 classes. For some objects (like Commandlets or the UWindow objects) RootObject is "Transient" in UT and "Package" in UT2003.

to Boolean[edit]

The boolean will be False if the object, class, etc. is None and True otherwise.

Struct...[edit]

Note: Vectors and rotators are structs, too.

to String[edit]

Every part of the struct is converted separately and all values are separated by commas. This might only work for certain Built-In Struct.

to Boolean[edit]

Results in False if all parts of the struct are null values, i.e. 0, empty strings or None. Anything else results in True.

String...[edit]

to Byte/Int[edit]

The string has to start with a number which must not be negative for byte. The string is cut off at the first non-numerical character. Any other string results in 0.

to Float[edit]

Similar to integer conversion, but the value can use a much more complex format. "-12.3e-2" will be converted to -1.23, "1e4" is 10000 and "12.3" is (guess what) 12.3.

to Boolean[edit]

The conversion returns True if the string starts either with the characters "true" (not case-sensitive) or results in a non-zero number (see string to integer/float above). It returns False in all other cases.

to Struct[edit]

The components of the struct have to be in the same order the struct -> string cast uses and also have to be separated by commas. All components that are not present in the string are set to 0, False, empty strings, etc. This might only work for certain Built-In Struct.

to Name[edit]

Names refuse to be set with expressions or from string variables. They only like something like this:

MyName = 'thisStringIsHardcoded';          // This works.
SetPropertyText("MyName", MyStringValue);  // Workaround, see below

The workaround by using SetPropertyText to assign a calculated value to a name variable works reasonably well, but there's a reason to the fact that it's not possible to directly assign strings to names. Doing so creates a new name table entry which isn't communicated to remote machines in network games, which can in turn lead to many sorts of ugly inconsistency problems if it's involved in replication.

to Object[edit]

Strings can't be casted to objects directly. Instead use the DynamicLoadObject method to load the specified object and then cast it to the desired class.

local class<Actor> ActorType;
local Actor A;
 
ActorType = class<Actor>(DynamicLoadObject("MyPackage.MyActorClass", class'Class'));
A = Spawn(ActorType);
local class<UWindowList> ListType;
local UWindowList L;
 
ListType = class<UWindowList>(DynamicLoadObject("MyPackage.MyList", class'Class'));
L = New ListType;
local Texture T;
 
T = Texture(DynamicLoadObject("MyPackage.MyTexture", class'Texture'));

Dimension4: You can load objects from maps too! Example:

IterLI=LevelInfo(DynamicLoadObject(IteratedMap$".LevelInfo0", class'Actor'));

Vector...[edit]

to Rotator[edit]

Vectors and Rotators can automatically be casted to each other. Vector -> Rotator returns a rotator with the roll component set to 0, while Rotator -> Vector returns a vector with length 1.

See rotator for more.

Rotator...[edit]

to Vector[edit]

Rotators represent three angles of rotation. A rotator in itself does not necessarily represent a direction until it is used in conjunction with a vector. For instance, a Yaw += 16384 will rotate an arbitrary vector 90 degrees but if you do not know the direction of the arbitrary vector, you cannot say what the direction of the new rotated vector is. The best you can say is that the new vector is 90 degrees rotated.

However, the vector(rotator) typecast returns a vector given only a rotator (without any other explicit vector information). It is assumed that the source vector is vect(1,0,0) so that the returned value is a vector rotated from vect(1,0,0). This makes sense since vect(1,0,0) is the vector used to represent the forward direction.

Examples:

Rotator
Vector
Pitch Yaw Roll
X Y Z
0 0 any
1 0 0
0 -32768 any
-1 0 0
0 16384 any
0 1 0
0 -16384 any
0 -1 0
16384 any any
0 0 1
49151 any any
0 0 -1

See rotator for more.

Related Topics[edit]