UDN
Search public documentation:

UnrealScriptReference
日本語訳
中国翻译
한국어

Interested in the Unreal Engine?
Visit the Unreal Technology site.

Looking for jobs and company info?
Check out the Epic games site.

Questions about support via UDN?
Contact the UDN Staff

UE3 Home > UnrealScript > UnrealScript Language Reference

UnrealScript Language Reference


Overview


This is a technical document describing the UnrealScript language. It's not a tutorial, nor does it provide detailed examples of useful UnrealScript code. For examples of UnrealScript the reader is referred to the source code of the engine, which provides tens of thousands of lines of working UnrealScript code which solves many problems such as AI, movement, inventory, and triggers. A good way to get started is by looking at the "Actor", "Object", "Controller", "Pawn", and "Weapon" scripts.

This document assumes that the reader has a working knowledge of C/C++ or Java, is familiar with object-oriented programming, has played Unreal and has used the UnrealEd editing environment.

For further reading, UnrealWiki also provides a great deal of information over the UnrealScript language in their UnrealScript Overview.

For programmers who are new to OOP, it is highly recommended that you go to Amazon or a bookstore and buy an introductory book on Java programming. Java is very similar to UnrealScript, and is an excellent language to learn about due to its clean and simple approach.

Design goals of UnrealScript


UnrealScript was created to provide the development team and the third-party Unreal developers with a powerful, built-in programming language that maps naturally onto the needs and nuances of game programming.

The major design goals of UnrealScript are:

  • To support the major concepts of time, state, properties, and networking which traditional programming languages don't address. This greatly simplifies UnrealScript code. The major complication in C/C++ based AI and game logic programming lies in dealing with events that take a certain amount of game time to complete, and with events which are dependent on aspects of the object's state. In C/C++, this results in spaghetti-code that is hard to write, comprehend, maintain, and debug. UnrealScript includes native support for time, state, and network replication which greatly simplify game programming.
  • To provide Java-style programming simplicity, object-orientation, and compile-time error checking. Much as Java brings a clean programming platform to Web programmers, UnrealScript provides an equally clean, simple, and robust programming language to 3D gaming. The major programming concepts which UnrealScript derives from Java are:
    • a pointerless environment with automatic garbage collection;
    • a simple single-inheritance class graph;
    • strong compile-time type checking;
    • a safe client-side execution "sandbox";
    • and the familiar look and feel of C/C++/Java code.
  • To enable rich, high level programming in terms of game objects and interactions rather than bits and pixels. Where design tradeoffs had to be made in UnrealScript, I sacrificed execution speed for development simplicity and power. After all, the low-level, performance-critical code in Unreal is written in C/C++ where the performance gain outweighs the added complexity. UnrealScript operates at a level above that, at the object and interaction level, rather than the bits and pixels level.

During the early development of UnrealScript, several major different programming paradigms were explored and discarded before arriving at the current incarnation. First, I researched using the Sun and Microsoft Java VM's for Windows as the basis of Unreal's scripting language. It turned out that Java offered no programming benefits over C/C++ in the Unreal context, added frustrating restrictions due to the lack of needed language features (such as operator overloading), and turned out to be unfathomably slow due to both the overhead of the VM task switch and the inefficiencies of the Java garbage collector in the case of a large object graph. Second, I based an early implementation of UnrealScript on a Visual Basic variant, which worked fine, but was less friendly to programmers accustomed to C/C++. The final decision to base UnrealScript on a C++/Java variant was based on the desire to map game-specific concepts onto the language definition itself, and the need for speed and familiarity. This turned out to be a good decision, as it has greatly simplified many aspects of the Unreal codebase.

Language Features


Language Features

For those who are already familiar with UnrealScript, here's a short overview of the major changes in UnrealScript since Unreal Engine 2.

  • Dynamic Arrays - Dynamic arrays now have a new function find() to find the index of an element
  • Dynamic Array Iterators - The foreach operator now functions on dynamic arrays.
  • Accessing constants from other classes: class'SomeClass'.const.SOMECONST
  • Tooltip Support - Editor property windows now display tooltips when you mouse over a property, if that property has a comment of the form /** tooltip text */ above its declaration in UnrealScript.
  • Default properties - processing of the defaultproperties block has been changed\improved a bit
    • Struct defaults - structs can now also have default properties
    • It is no longer allowed to set default values for config or localized variables
    • Defaultproperties are readonly at runtime, it is no longer allowed to do class'MyClass'.default.variable = 1
  • Metadata Support - Extend in-game and in-editor functionality by associating properties with various types of metadata.
  • Debugging Functions - new debugging related functions have been added
  • Multiple timer support
  • Default function argument values - the default value for optional function arguments can now be specified.
  • Stacking states - you are now able to push and pop states onto a stack
  • UnrealScript Preprocessor - support for macros and conditional compilation
  • Interfaces - Support for interfaces has been added
  • Delegate function arguments - UE3 now allows delegates to be passed as function arguments
  • Replication - replication statements have changed in UE3:
    • The replication block is only used for variables now
    • Function replication is now defined by means of function specifiers ( Server, Client, Reliable )

Resources

UnrealScript Tools and utilities


Script Profiling

The Gameplay Profiler can help with understanding what areas of script execution are taking the most time.

Script Debugger

See the Unreal Debugging Tools page for more information.

Advanced Technical Topics


The Unreal Virtual Machine

The Unreal Virtual Machine consists of several components: The server, the client, the rendering engine, and the engine support code.

The Unreal server controls all gameplay and interaction between players and actors. In a single-player game, both the Unreal client and the Unreal server are run on the same machine; in an Internet game, there is a dedicated server running on one machine; all players connect to this machine and are clients.

All gameplay takes place inside a "level", a self-contained environment containing geometry and actors. Though UnrealServer may be capable of running more than one level simultaneously, each level operates independently, and are shielded from each other: actors cannot travel between levels, and actors on one level cannot communicate with actors on another level.

Each actor in a map can either be under player control (there can be many players in a network game) or under script control. When an actor is under script control, its script completely defines how the actor moves and interacts with other actors.

With all of those actors running around, scripts executing, and events occuring in the world, you're probably asking how one can understand the flow of execution in an UnrealScript. The answer is as follows:

To manage time, Unreal divides each second of gameplay into "Ticks". A tick is the smallest unit of time in which all actors in a level are updated. A tick typically takes between 1/100th to 1/10th of a second. The tick time is limited only by CPU power; the faster machine, the lower the tick duration is.

Some commands in UnrealScript take zero ticks to execute (i.e. they execute without any game-time passing), and others take many ticks. Functions that require game-time to pass are called "latent functions". Some examples of latent functions include Sleep, FinishAnim, and MoveTo. Latent functions in UnrealScript may only be called from code within a state (the so called "state code"), not from code within a function (that includes functions define within a state).

While an actor is executing a latent function, that actor's state execution doesn't continue until the latent function completes. However, other actors, or the VM, may call functions within the actor. The net result is that all UnrealScript functions can be called at any time, even while latent functions are pending.

In traditional programming terms, UnrealScript acts as if each actor in a level has its own "thread" of execution. Internally, Unreal does not use Windows threads, because that would be very inefficient (Windows 95 and Windows NT do not handle thousands of simultaneous threads efficiently). Instead, UnrealScript simulates threads. This fact is transparent to UnrealScript code, but becomes very apparent when you write C++ code that interacts with UnrealScript.

All UnrealScripts are executed independently of each other. If there are 100 monsters walking around in a level, all 100 of those monsters' scripts are executing simultaneously and independently each "Tick".

UnrealScript Implementation

For more detailed information on how UnrealScript works under the covers - from the Compile Process to Execution to Byte Code representaiton - see the UnrealScript Implementation page.

UnrealScript binary compatibility issues

UnrealScript is designed so that classes in package files may evolve over time without breaking binary compatibility. Here, binary compatibility means "dependent binary files may be loaded and linked without error"; whether your modified code functions as designed is a separate issue. Specifically, the kinds of modifications when may be made safely are as follows:

  • The .uc script files in a package may be recompiled without breaking binary compatibility.
  • Adding new classes to a package.
  • Adding new functions to a class.
  • Adding new states to a class.
  • Adding new variables to a class.
  • Removing private variables from a class.

Other transformations are generally unsafe, including (but not limited to):

  • Adding new members to a struct.
  • Removing a class from a package.
  • Changing the type of any variable, function parameter, or return value.
  • Changing the number of parameters in a function.

Technical notes

Garbage collection. All objects and actors in Unreal are garbage-collected using a tree-following garbage collector similar to that of the Java VM. The Unreal garbage collector uses the UObject class's serialization functionality to recursively determine which other objects are referenced by each active object. As a result, object need not be explicitly deleted, because the garbage collector will eventually hunt them down when they become unreferenced. This approach has the side-effect of latent deletion of unreferenced objects; however it is far more efficient than reference counting in the case of infrequent deletion. See the Garbage Collection page for more details.

UnrealScript is bytecode based. UnrealScript code is compiled into a series of bytecodes similar to p-code or the Java bytecodes. This makes UnrealScript platform-neutral; this porting the client and server components of Unreal to other platforms, i.e. the Macintosh or Unix, is straightforward, and all versions can interoperate easily by executing the same scripts.

Unreal as a Virtual Machine. The Unreal engine can be regarded as a virtual machine for 3D gaming in the same way that the Java language and the built-in Java class hierarchy define a virtual machine for Web page scripting. The Unreal virtual machine is inherently portable (due to splitting out all platform-dependent code in separate modules) and expandable (due to the expandable class hierarchy). However, at this time, there are no plans to document the Unreal VM to the extent necessary for others to create independent but compatible implementations.

The UnrealScript compiler is three-pass. Unlike C++, UnrealScript is compiled in three distinct passes. In the first pass, variable, struct, enum, const, state and function declarations are parsed and remembered; the skeleton of each class is built. In the second pass, the script code is compiled to byte codes. This enables complex script hierarchies with circular dependencies to be completely compiled and linked in two passes, without a separate link phase. The third phase parses and imports default properties for the class using the values specified in the defaultproperties block in the .uc file.

Persistent actor state. It is important to note that in Unreal, because the user can save the game at any time, the state of all actors, including their script execution state, can be saved only at times when all actors are at their lowest possible UnrealScript stack level. This persistence requirement is the reason behind the limitation that latent functions may only be called from state code: state code executes at the lowest possible stack level, and thus can be serialized easily. Function code may exist at any stack level, and could have (for example) C++ native functions below it on the stack, which is clearly not a situation which one could save on disk and later restore.

Unrealfiles are Unreal's native binary file format. Unrealfiles contain an index, serialized dump of the objects in a particular Unreal package. Unrealfiles are similar to DLL's, in that they can contain references to other objects stored in other Unrealfiles. This approach makes it possible to distribute Unreal content in predefined "packages" on the Internet, in order to reduce download time (by never downloading a particular package more than once).

Why UnrealScript does not support static variables. While C++ supports static (per class-process) variables for good reasons true to the language's low-level roots, and Java support static variables for reasons that appear to be not well thought out, such variables do not have a place in UnrealScript because of ambiguities over their scope with respect to serialization, derivation, and multiple levels: should static variables have "global" semantics, meaning that all static variables in all active Unreal levels have the same value? Should they be per package? Should they be per level? If so, how are they serialized -- with the class in its .u file, or with the level in its .unr file? Are they unique per base class, or do derived versions of classes have their own values of static variables? In UnrealScript, we sidestep the problem by not defining static variables as a language feature, and leaving it up to programmers to manage static-like and global-like variables by creating classes to contain them and exposing them in actual objects. If you want to have variables that are accessible per-level, you can create a new class to contain those variables and assure they are serialized with the level. This way, there is no ambiguity. For examples of classes that serve this kind of purpose, see LevelInfo and GameInfo.