There is no spoon

Functions

From Unreal Wiki, The Unreal Engine Documentation Site
Revision as of 13:24, 28 May 2010 by Wormbo (Talk | contribs) (Parameter modifiers: more details about the optional parameter default value)

Jump to: navigation, search

A function in UnrealScript is a subroutine associated with a specific class. Functions can define zero or up to sixteen parameters and optionally a return type. Various modifiers can be used in the declaration to change the way the function can be accessed or how it behaves when called.

Operators and delegates are special kinds of functions. Operators have either exactly one (prefix/postfix operators) or two (infix operators) parameters and can use an operator symbol instead of a function name. Delegates are variable-like placeholders you can assign functions to. Calls to a delegate actually call the function assigned to it.

Function declarations

Syntax

A regular function declaration looks like this:

[modifiers] function [returntype] functionname ( [parameter declarations] ) body_or_semicolon

If the function does not return any value, the return type can be omitted. In this case return statements in the function body may not specify any value. Otherwise, if a return value was specified, any return statement must specify an expression that evaluates to that type. Starting with Unreal Engine 2 the compiler will emit a warning if a return type was specified, but the function body does not contain any return statement. Alternatively, the keyword event can be used instead of function without any difference at the UnrealScript level. The event keyword will only become relevant when writing native code.

Note: The compiler is not very strict about the order of modifiers and the keywords function and event. Especially in the UT3 source code you will sometimes see modifiers between the keyword function and the return type or function name. This can be very confusing to anyone reading your code, so please always make function or event the last keyword before the return type or function name.

Modifiers

Before the actual function declaration you are allowed to specify one or more function modifiers. These change the way the function works in certain situations.

Access modifiers

private2,3
The function is only accessible within the same class. Not even subclasses can "see" it, which means the usual rules for overriding functions don't apply - subclasses may change the return type and the number and type of parameters. Since they don't see the parent class function, they cannot call it via the Super keyword.
protected2,3
Restricts access to this function to the current class and its subclasses. Subclasses may override the function unless it is also declared as final.
public2,3
The default access rule. If neither private nor protected is specified, this is one is implied. Public functions can be called from non-related classes and may be overridden in subclasses, unless the function is also declared as final.
static
Static functions are not tied to specific objects, but to a class. The object literal Self is not allowed within static functions and instance functions (i.e. functions not declared with the static modifier) can only be called through object references, this includes iterator functions in ForEach loops. Because static functions are not executed in the context of any object instance, they can also not be subject to replication.
final
As the name suggests, function declarations with this modifier are "final" and can't be overridden in subclasses.

Call modifiers

exec
Marks the function as potential console command. Note that exec functions are only really called via console commands for certain objects, such as the local PlayerController, that player's HUD, Pawn or selected Weapon and a few other places. The possible places for exec functions may vary from game to game, so have a look at the UnrealScript source code of your game to find out where you can put exec functions that actually work.
When overriding functions, you must also declare the new function with the exec modifier if and only if the overridden function was declared with it. This means you cannot turn a regular function into a console command!
simulated
Marks the function as valid for execution on clients if the actor containing it was replicated to that client and the local role of that client is either ROLE_SimulatedProxy or ROLE_DumbProxy.
Note: The modifier simulated does not imply any kind of replication or even broadcast! Also, this modifier is not inherited when overriding functions - every super function call evaluates that super function's simulated modifier separately, potentially breaking the chain of super calls on clients!
singular
Restricts recursive function calls by only ever allowing a single singular function to execute per object instance. If a singular function is called, the engine first checks if the object instance the function was called on already executes a singular function. If it does, the new function call is ignored.

Network modifiers

These modifiers affect function replication in Unreal Engine 3. Earlier engine generations use the replication block to define these.

client3
Specifies that this function should be replicated to the client owning the actor if it is called on the server. This modifier automatically makes the function simulated as well.
demorecording3+
Specifies that this function should be replicated to the demorecording driver. It will only be executed during demo playback. This modifier automatically makes the function simulated as well, which makes sense since demo playback essentially is a client environment.
reliable3
Replicated functions will be replicated reliably when marked with this modifier.
server3
Specifies that this function should be replicated to the server if it was called on a replicated actor that is owned by the local client.
unreliable3
Replicated functions will be replicated unreliably when marked with this modifier, i.e. they may be dropped due to packet loss or bandwidth saturation.

Native implementation modifiers

const3
This modifier is actually placed after the closing parenthesis of the parameter list. It will be passed to the generated native header file, but has no effect in UnrealScript.
event
As mentioned in the Syntax section above, the keyword event may replace the keyword function without any effect on the UnrealScript level. However, when exporting native header files from script code, event functions will have calling stubs generated so the UnrealScript function can be called more easily from C++ code. These generated C++ methods have the name eventNameOfUnrealScriptFunction and their parameters and possible return type reflects the UnrealScript function declaration.
intrinsic1,2
See native.
intrinsic(number)1,2
See native(number).
iterator
Native functions with the iterator modifier can only be used as the iterator function of a ForEach loop. This modifier may not be used for non-native functions.
latent
Native functions with the latent modifier may only be called directly from state code (but not within any ForEach loop) and usually pause state code execution at least until the next tick, possibly even longer. This modifier may not be used for non-native functions.
native
The actual implementation of this function resides in native code in a C++ function with the name execNameOfUnrealScriptFunction. Very early Unreal Engine 1 versions used the keyword intrinsic instead.
Similar to simulated functions, native functions can be called clientsidely on replicated actors even if the local role is ROLE_SimulatedProxy or ROLE_DumbProxy.
native(number)
Same as the plain native modifier, but additionally the function gets its own UnrealScript bytecode token.
Even if you can write native code for your mod, you should never specify numbers for your native functions!
noexport3, noexportheader3*
Prevents this function from being exported to native headers.
virtual3
This modifier is just passed through to exported native headers and has no effect in UnrealScript.

Parameters

A function may have zero to sixteen parameters. If you need to pass more values to your function, consider using combining some or all of them to a struct-type parameter or passing them as a static or dynamic array.

Syntax

Parameter declarations have the following syntax:

[parameter modifiers] type parametername

As with other variable declarations, the parameter name may optionally followed by a number in square brackets to declare the parameter as static array:

[parameter modifiers] type parametername[arraysize]

If more than one parameter is specified, the individual parameters must be separated by commas. The type and any modifiers only apply to the parameter they were specified for.

Parameter modifiers

coerce
Automatically performs typecasting of values passed to this parameter, if the parameter is of a primitive type other than enum. Note that the built-in numeric types int, float and byte are automatically typecasted to each other even without this modifier. Enum conversion to these numeric types is automatic as well. This modifier can also be applied to the return type of the function.
const3
For native functions this modifier is passed to the generated native headers. It has no effect in UnrealScript.
init3*
Effect unknown.
optional
Specifying the parameter in function calls is optional. Omitted parameters assume a default value, which is the parameter type's null value. In Unreal Engine 3 it is also possible to specify a different default value:
optional type parametername = defaultvalue
The defaultvalue can be any expression you could use on the right side of a variable assignment. It will be evaluated as if it was placed between the last local variable declaration and the first executable code statement of the function. In other words, you can call functions here and use any of the function's parameters or local variables. Evaluation of the expression is skipped if that parameter was specified when calling the function.
out1,2
If the parameter is a variable or an array element or struct member accessed through a variable (as opposed to a literal, or function return value or an array element or struct member accessed through a function return value), any modification to the parameter inside the function is copied back into the variable originally passed to the function after the function returns.
Note: This is not "call by reference", but actually call by value-result. Instead of only copying the value when passing the parameter in, it needs to be copied again when returning from the function, so declaring a parameter as out is actually more expensive in UnrealScript prior to Unreal Engine 3. (see below)
out3
The parameter is passed by reference instead of being copied. Any changes to the parameter inside the function become immediately visible in the variable, struct member or (static) array element originally passed to the function. For large dynamic arrays or structs passing by reference (via out parameter) may be more efficient than passing by value.
Note: Due to the way dynamic arrays are implemented, you can't pass dynamic array elements or parts of them to an out parameter. Passing an entire dynamic array via out parameter is possible, and for large arrays even recommended.
skip
Only allowed for the second parameter of a native operator declaration. The native implementation of the operator may choose to not evaluate the second operand if the operator result can be determined by evaluating only the first operand. This is used for the operators && and || to skip evaluating the second boolean expression if it is not required.

Parameters are evaluated at the time the function is called, before deciding if or where to execute the function body. Parameter expressions are evaluated from left to right in the context of the calling code and with the exception of parameters declared with the out modifier, UnrealScript always uses the call-by-value evaluation strategy. Note that reference type values are no objects, but object references! Passing objects to functions duplicates the object reference, not the object itself.

Locals

Functions can have also locals which are just like class variables with the exception of no modifiers. Locals can only be declared in functions and they are only available within functions.

See Local variables.

Function calls

Functions can be called from state code and other functions. In theory they can also be called from a replication block, but this is impractical as replication conditions are tested relatively often and at unexpected times, while function calls (especially to non-native functions) are a much more expensive operation than checking variable values.

Function calls must not be confused with executing functions. The call to and execution of a function can happen in different places if replication is involved, and some modifiers (e.g. singular or no simulated) may prevent the execution of a called function at runtime altogether.

Syntax

A typical function call uses the following syntax:

[objectreference .] functionname ( [expression [, expression [, ...]]] )

The object reference in front of the function name provides a "calling context" for the function. The function will be called on the specified object. If the reference is left out, the function is called in the same context as the code calling the function.

For static functions the context is actually the class of the specified object reference. To specify a context class directly, you can use the following syntax instead:

classreference . static . functionname ( [expression [, expression [, ...]]] )

Instead of an object reference, you can also use one of the function call specifiers Super or Global to tell the compiler you want a different implementation of a function to be called.

Determining the target function

When the UnrealScript interpreter or compiler (only for final functions and when using the Super call specifier) encounters a function call, it needs to figure out, which implementation actually gets called. It does so by starting at the class of the context object reference. The compiler and UnrealScript bytecode interpreter have similar rules for this, but there are some important differences. The most important difference is that the compiler doesn't actually execute the code, so it doesn't know e.g. the content of variables at runtime. Due to this restriction, the compiler relies on variable types instead of the referenced objects to find the calling context. Similar restrictions apply to determining the state of the context object. The compiler can only assume that functions without a context object reference are called in the context of the state the calling code resides in. At runtime, the context state can change before a state function returns if any code that function executes calls the GotoState() function.

If the context class and context object state have been determined, the engine first looks in that state for a function definition with the specified name. If none is found, it continues to look in the parent state for extended states or in the parent class version of the same state for inherited states. It continues upwards until no more parent states are found. In that case, non-state functions with the specified name are looked up in a similar manner, starting at the context class. If the compiler can't find any function definition this way, it issues an error. Due to the compiler check the engine should always find a function to call at runtime, but there are some special cases the compiler can't catch and in these cases the engine crash will crash with a "Failed to find function" error.

How function calls are processed

After the engine resolved, which function it should actually call, it evaluates the function call.

Before looking at the function itself, all specified argument expressions are evaluated from left to right. If an expression contains a function call, its return value is determined by calling that function first. Some native functions have non-standard argument evaluation methods, especially the operators && and ||, which may choose to not evaluate their second argument at all.

If the function has replication logic attached, the engine checks if it needs to replicate the call. If that's the case, the function's argument values are wrapped up along with the function name in a network packet, which is sent to the remote side. The local function call then returns immediately and if the function has a return type, it returns that type's null value. When the remote side receives the function call, it first checks whether it actually wants it (in case something strange happened) and then continues to process the function call similar to a local function call as described below.

Now that the function's argument values are known and the is to be executed locally, the engine checks if and how it should actually execute the function. If the function is neither native nor simulated, but the context object is an actor with Role <= ROLE_SimulatedProxy, then the function is not executed. Similarly, if the function is singular and the context object already executes the same or another singular function, the function is not executed. And lastly, if the function is a probe function and the probe event with the same name was disabled, then the function is not executed. In all these cases, if the function has a return type, the corresponding null value is returned.

If the engine found that the function should be executed, it initializes all parameters (except out parameters in Unreal Engine 3) as local variables with an initial value equal to the corresponding argument value. Omitted optional parameters with a default value expression are initialized accordingly at this point. Any other optional parameters and local variables are initialized with their type's corresponding null value. After that is done, the code in the function body is executed until either a return statement or the end of the function body is reached. If the function has a return type, but code execution did not reach any return statement, the function's return value is the return type's corresponding null value.

In Unreal Engine 1 and 2, if the function has any out parameters, the final values of those local variables are copied back to the corresponding variables originally passed in as arguments.

Finally, if the function was called as part of an expression, its return value is used to further evaluate that expression.

Limits for function calls

Generally there is a limit of at most 250 nested function calls at runtime. If this limit is exceeded, the engine will crash with an "Infinite script recursion" error. The value 250 may look like a severe restriction, but in practice you will rarely ever run into it unless your code contains logic errors (bad CheckReplacement() logic is a frequent cause) or you tried to explicitly implement a recursive algorithm with recursive function calls. In the latter case you may have to try and find a different solution using either a stack-like structure or a completely different algorithm. Generally, recursion must be viewed as an expensive operation in UnrealScript.

A function call is nested in another function call if it happens as part of the containing function's code execution. Function calls as part of a parameter expression for another function call are nested in the calling environment, not in the called function. For example:

function int X(int A, int B)
{
  return Y(Z(A, B)); // calls Z(A, B), then calls Y(result of Z call), then returns result of Y call
}
 
function int Y(int C)
{
  return C - 1;
}
 
function int Z(int D, int E)
{
  return D + E;
}

Here, when calling X(), the maximum nesting level is 2 while executing Z() and Y(), respectively.

Native functions do not increase the nesting level, but UnrealScript functions called from native functions (e.g. *BeginPlay() called from Spawn()) will resume with the nesting level at which the native function was called.