Typecasting
What is Typecasting?
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?
Example 1: Owners
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
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
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....?
You can also typecast classes to other classes:
var class<actor> AClass; AClass = class'Foo'; class<Foo>(AClass).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
Byte/Int/Float...
to String
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
Zero (or first item of the enumeration) results in False, anything else is True.
to Enum
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...
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
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
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
The first enum item results in False, anything else in True.
to Byte/Int
This conversion happens automatically in assignments.
Boolean...
to Byte/Int/Float
True results in 1, False results in 0.
to String
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...
to String
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...
"Object" can be any Actor, Object, class, Texture, mesh, Sound, etc.
to String
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
The boolean will be False if the object, class, etc. is None and True otherwise.
Struct...
Note: Vectors and rotators are structs, too.
to String
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
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...
to Byte/Int
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
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
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
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
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
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> aClass; local Actor A; aClass = class<Actor>(DynamicLoadObject("MyPackage.MyActorClass", class'Class')); A = Spawn(aClass);
local class<UWindowList> ListClass; local UWindowList L; ListClass = class<UWindowList>(DynamicLoadObject("MyPackage.MyList", class'Class')); L = New ListClass;
local Texture T; T = Texture(DynamicLoadObject("MyPackage.MyTexture", class'Texture'));
Vector...
to Rotator
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...
to Vector
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
- UnrealScript topic page
- Variable Type
- Function Syntax