Gah - a solution with more questions. – EntropicLqd
There are three types of regular operators in UnrealScript.
- infix operators (
a + b):
[modifiers] operator(precedence) [returntype] operatorname ( left operand, right operand ) body_or_semicolon
- prefix operators (
[modifiers] preoperator [returntype] operatorname ( operand ) body_or_semicolon
- postfix operators (
[modifiers] postoperator [returntype] operatorname ( operand ) body_or_semicolon
Infix operators always have two operands, while prefix and postfix operators have exactly one operand. All operator declarations must include the modifier final. The operator name can either be a single symbol character (
|), a symbol combination (
||) or a standard function name. (examples in the engine are dot, cross and clockwisefrom)
Operators can be overloaded, that means multiple operators with the same name or symbol can be defined, as long as they differ in their operand types. Overloading with identical operand types is not allowed as the engine would not be able to distinguish between the definitions.
Operator precedence controls, how tightly an operator binds its operands. A precedence value can only be specified for infix operators (pre/postfix operators bind as tightly as possible), with lower numbers binding more tightly. For example,
* has a precedence of 16, while
+ has 20. That means
* binds more tightly than
a * b + c * d is evaluated as
(a * b) + (c * d). It is possible to define different precedence values for overloaded operators. For example the
-= operator as combined subtract and assign for numeric values has a precedence of 34, while the
-= operator for removing occurrences of a string in another string has a precedence of 45.
The following table lists the operator precedence for the default operators defined across the various engine versions. Operators further up in the table bind more tightly.
||multiplication, division, dot product, cross product|
||bit shifting, vector transformation|
||bitwise AND, OR and XOR|
||logical AND and XOR|
||combined arithmetic assignment|
||combined concatenation and assignment|
Note that string operations, bitwise integer operations and logical AND, OR and XOR bind less tightly than comparison operators. See Object operators(RTNP, U1, UT, U2, U2XMP, UE2Runtime, UT2003, UT2004, UDK, UT3) for detailed descriptions of the operators.
To make it short, all regular infix operators of the same precedence are left-associative and there's no way to change that.
a + b + c + d is always evaluated as
((a + b) + c) + d. While this doesn't change the general evaluation order of the operands, it may have an impact on the type of operation and the result. For example, consider the following code:
local vector u, v; local float f, g; u = v * f * g;
This code evaluates as
(v * f) * g, that means both operators are
vector * float. If this was written as
v * (f * g), the second operator would actually be
float * float. It doesn't matter much for the result in this case, but you will have to keep this effect in mind for int operations like division or bit shifting. For these operations it matters which order you perform the operations. For example the expression
i / n * n returns the largest multiple of n that is less than or equal to i if both variables are declared of type int, while
i * n / n results in i again.
You might find the left-associativity counter-intuitive if you want to do something like:
local float a, b, c; a *= b *= c;
This won't compile because it's equal to
(a *= b) *= c and you cannot pass complex expressions into out parameters. What you really want is
a *= (b *= c);, which requires the parentheses due to left-associativity of all operators.
The following operators are not declared anywhere in UnrealScript, they "simply exist" and have special behavior that cannot be put in any kind of declaration available in the language.
The assignment operator
= implicitly exists for all possible data types, except static arrays. It could informally be described with the following declaration:
native static final operator(255) = (out type variable, type value);
Note that assignments in UnrealScript do not return any value. Unlike in many other languages where you could "chain" assignments together, an expression like
a = b = c; results in a compiler error. Also note that assignment (unlike the combined assignment operators declared in the Object class) has the highest possible precedence value, so you never need to "help" the compiler by putting the value to assign (or the variable to assign to) in parentheses.
The assignment operator is the only valid way to alter the length property of a dynamic array. Using out parameters, including those of the combined assignment operators, can cause memory leaks and GPF crashes.
Struct comparison operators
If the operators
!= are not declared for the operands' types and both operands are of the same struct type, the hard-coded struct (in)equality operators are used. These simply compare the in-memory data of the two struct values.
Note that this type of comparison does not work for dynamic arrays as struct members, which will always look different unless both compared arrays are empty. Also, there is a compiler bug that allows you to compare dynamic arrays of structs, while the compiler would throw an error if dynamic arrays of any non-struct type are compared. The compiler will apply generic struct comparison to the two dynamic array variables, which has the same result (equal only if empty) as if the arrays were struct members.
In Unreal Engine 3 and some Unreal Engine 2 games, a special ternary operator exists that works similar to the If...Else statement, except that it returns a value. The general syntax for this conditional operator is:
condition ? expression_if_true : expression_if_false
?, you might need to use
?? in Unreal Engine 2 implementations.)
Like If...Else, this operators first evaluates its condition expression and then only evaluates one of the two other expressions, depending on the condition. The condition must return a bool value, the return types of the other two expressions must match exactly as they are used as the return type of the conditional operator. Automatic type conversion for numeric values does not apply here!
The special operator
new can be used to create non-Actor objects. In its simplest form it works like a prefix operator, but it can also take additional parameters similar to a function call. The general syntax for using the
new operator is as follows:
new [( [outer [, name [, flags]]] )] class [( template )]
- 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.
If the class to create an object from is declared within another class, then the outer argument must be an object of that class or a subclass.
- The name for the new object. Unreal Engine 1 expects a name value here, but starting with Unreal Engine 2 this must be a string value. Omitting the name or passing "None" or the empty string assigns a default name consisting of the class name and a number, in Unreal Engine 3 separated by an underscore character. Unreal Engine 2 might not append the number unless the engine was started with the commandline parameter
-makenames. Specifying the name of an existing object of the same class within the same outer object may have unexpected results if the existing object is still in use as it will be replaced by the new object.
- Specifies object flags for the new object. Only use this if you know what you're doing!
- The class to create an object of. The specified class must not be abstract or extend Actor. (Actors must be created with the Spawn() function.)
- (only Unreal Engine 3) An object whose properties (except those declared in the Object class) are copied to the new object. The template must be of the same class as the new object or of a parent class.
Some examples for Unreal Engine 3:
local Object O; O = new class'TestClass'; O = new(Self) class'TestClass'; // O.Outer == Self O = new(None, 'ExampleObject') class'TestClass'; // O.Name == 'ExampleObject' O = new(None, '', RF_Transient) class'TestClass'; // makes O transient even if TestClass isn't O = new(None, '', 0) class'TestClass' (TestClass'TemplateObject'); // duplicates TemplateObject
The (compile-time) return type of the
new operator corresponds to the (compile-time) metatype of the specified class argument:
local class<WebApplication> WAClass< SEMI > local class<Object> OClass< SEMI > local Object O; local WebApplication WA; WAClass = class'UTServerAdmin'; OClass = class'UTServerAdmin'; WA = new class'UTServerAdmin'; // works because UTServerAdmin extends WebApplication WA = new WAClass< SEMI > // works because the metaclass of WAClass is WebApplication O = new WAClass< SEMI > // works because WebApplication extends Object O = new OClass< SEMI > // works because the metaclass of OClass is Object WA = new OClass< SEMI > // type mismatch error (Object doesn't extend WebApplication!)
Only in Unreal Engine 2 the newly created object receives a call to its Created() function before the
new operator returns. Other engine generations do not provide any generic initialization events.
|Declarations||Preprocessor • Classes • Interfaces • Cpptext • Constants • Enums • Structs • Variables (Metadata) • Replication block • Operators • Delegates • Functions • States • Defaultproperties (Subobjects)|
|Types||bool • byte • float • int • name • string • Object • Class • Enums • Structs (Vector ⋅ Rotator ⋅ Quat ⋅ Color) • Static arrays • Dynamic arrays • Delegates • Typecasting|
|Literals||Boolean • Float • Integer • Names • Objects (None ⋅ Self) • Vectors • Rotators • Strings|
|Flow||GoTo • If • Assert • Return • Stop • Switch • While • Do...Until • For • ForEach • Break • Continue|
|Specifiers||Super • Global • Static • Default • Const|
|UnrealScript||Syntax • .UC • .UCI • .UPKG • Comments • #directives • Native|